From 65b65a4692c84462ecf500806795ea0350073e18 Mon Sep 17 00:00:00 2001 From: Dave Barach Date: Tue, 7 Jul 2020 17:33:38 -0400 Subject: [PATCH] misc: add tracedump API plugin Type: feature Signed-off-by: Dave Barach Change-Id: I586547508003b95eaa74e18e4a5ac6f72986822c --- src/plugins/tracedump/CMakeLists.txt | 28 ++++ src/plugins/tracedump/tracedump.api | 82 +++++++++++ src/plugins/tracedump/tracedump.c | 256 +++++++++++++++++++++++++++++++++ src/plugins/tracedump/tracedump.h | 55 +++++++ src/plugins/tracedump/tracedump_test.c | 150 +++++++++++++++++++ src/vlib/trace.h | 2 + 6 files changed, 573 insertions(+) create mode 100644 src/plugins/tracedump/CMakeLists.txt create mode 100644 src/plugins/tracedump/tracedump.api create mode 100644 src/plugins/tracedump/tracedump.c create mode 100644 src/plugins/tracedump/tracedump.h create mode 100644 src/plugins/tracedump/tracedump_test.c diff --git a/src/plugins/tracedump/CMakeLists.txt b/src/plugins/tracedump/CMakeLists.txt new file mode 100644 index 00000000000..d0491caf677 --- /dev/null +++ b/src/plugins/tracedump/CMakeLists.txt @@ -0,0 +1,28 @@ + +# Copyright (c) +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_vpp_plugin(tracedump + SOURCES + tracedump.c + tracedump.h + + API_FILES + tracedump.api + + API_TEST_SOURCES + tracedump_test.c +) + +# API_TEST_SOURCES +# tracedump_test.c diff --git a/src/plugins/tracedump/tracedump.api b/src/plugins/tracedump/tracedump.api new file mode 100644 index 00000000000..81e6725abc8 --- /dev/null +++ b/src/plugins/tracedump/tracedump.api @@ -0,0 +1,82 @@ +/* + * tracedump.api - streaming packet trace dump API + * + * Copyright (c) 2020 Cisco and/or its affiliates + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file tracedump.api + * @brief VPP control-plane API messages. + * + * This file defines VPP control-plane binary API messages which are generally + * called through a shared memory interface. + */ + +/* Version and type recitations */ + +option version = "0.1.0"; + +service { + rpc trace_dump returns trace_dump_reply + stream trace_details; +}; + +define trace_dump { + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Dispose of any cached data before we begin */ + u8 clear_cache; + + /* iterator positions, both ~0 to just clear the cache */ + u32 thread_id; + u32 position; + + /* Max number of replies per burst */ + u32 max_records; +}; + +define trace_dump_reply { + u32 context; + i32 retval; + u32 last_thread_id; + u32 last_position; + u8 more_this_thread; + u8 more_threads; + u8 flush_only; + u8 done; +}; + +define trace_details { + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Position in the cache of this record */ + u32 thread_id; + u32 position; + + /* More or not */ + u8 more_this_thread; + u8 more_threads; + /* Needed when set ends in the middle of a batch */ + u8 done; + + string trace_data[]; +}; diff --git a/src/plugins/tracedump/tracedump.c b/src/plugins/tracedump/tracedump.c new file mode 100644 index 00000000000..87e6cf6ac5f --- /dev/null +++ b/src/plugins/tracedump/tracedump.c @@ -0,0 +1,256 @@ +/* + * tracedump.c - skeleton vpp engine plug-in + * + * Copyright (c) + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define REPLY_MSG_ID_BASE tdmp->msg_id_base +#include + +tracedump_main_t tracedump_main; + +static int +trace_cmp (void *a1, void *a2) +{ + vlib_trace_header_t **t1 = a1; + vlib_trace_header_t **t2 = a2; + i64 dt = t1[0]->time - t2[0]->time; + return dt < 0 ? -1 : (dt > 0 ? +1 : 0); +} + +static void +toss_client_cache (tracedump_main_t * tdmp, u32 client_index, + vlib_trace_header_t *** client_trace_cache) +{ + vlib_trace_header_t **th; + int i; + + /* Across each vlib main... */ + for (i = 0; i < vec_len (client_trace_cache); i++) + { + th = client_trace_cache[i]; + /* Toss the thread's cached data */ + vec_free (th); + } + /* And toss the vector of threads */ + vec_free (client_trace_cache); + tdmp->traces[client_index] = client_trace_cache; +} + +/* API message handler */ +static void +vl_api_trace_dump_t_handler (vl_api_trace_dump_t * mp) +{ + vl_api_registration_t *rp; + vl_api_trace_dump_reply_t *rmp; + vl_api_trace_details_t *dmp; + tracedump_main_t *tdmp = &tracedump_main; + vlib_trace_header_t ***client_trace_cache, **th; + int i, j; + u32 client_index; + u32 iterator_thread_id, iterator_position, max_records; + i32 retval = VNET_API_ERROR_NO_SUCH_ENTRY; + u32 last_thread_id = ~0, last_position = ~0; + u8 last_done = 0; + u8 last_more_this_thread = 0; + u8 last_more_threads = 0; + u8 *s = 0; + + rp = vl_api_client_index_to_registration (mp->client_index); + if (rp == 0) + return; + + /* Use the registration pool index... */ + client_index = rp->vl_api_registration_pool_index; + + vec_validate_init_empty (tdmp->traces, client_index, 0); + + client_trace_cache = tdmp->traces[client_index]; + + /* Clear the per-client cache if requested */ + if (mp->clear_cache) + { + toss_client_cache (tdmp, client_index, client_trace_cache); + client_trace_cache = 0; + } + + /* Now, where were we? */ + iterator_thread_id = clib_net_to_host_u32 (mp->thread_id); + iterator_position = clib_net_to_host_u32 (mp->position); + max_records = clib_net_to_host_u32 (mp->max_records); + + /* Need a fresh cache for this client? */ + if (vec_len (client_trace_cache) == 0 + && (iterator_thread_id != ~0 || iterator_position != ~0)) + { + vlib_worker_thread_barrier_sync (&vlib_global_main); + + /* Make a slot for each worker thread */ + vec_validate (client_trace_cache, vec_len (vlib_mains) - 1); + i = 0; + + /* *INDENT-OFF* */ + foreach_vlib_main ( + ({ + vlib_trace_main_t *tm = &this_vlib_main->trace_main; + + /* Filter as directed */ + trace_apply_filter(this_vlib_main); + + pool_foreach (th, tm->trace_buffer_pool, + ({ + vec_add1 (client_trace_cache[i], th[0]); + })); + + /* Sort them by increasing time. */ + if (vec_len (client_trace_cache[i])) + vec_sort_with_function (client_trace_cache[i], trace_cmp); + + i++; + })); + /* *INDENT-ON* */ + vlib_worker_thread_barrier_release (&vlib_global_main); + } + + /* Save the cache, one way or the other */ + tdmp->traces[client_index] = client_trace_cache; + + for (i = iterator_thread_id; i < vec_len (client_trace_cache); i++) + { + for (j = iterator_position; j < vec_len (client_trace_cache[i]); j++) + { + if (max_records == 0) + break; + + retval = 0; + th = &client_trace_cache[i][j]; + + vec_reset_length (s); + + s = format (s, "Packet %d\n%U\n\n", j + 1, format_vlib_trace, + &vlib_global_main, th[0]); + + dmp = vl_msg_api_alloc (sizeof (*dmp) + vec_len (s)); + dmp->_vl_msg_id = + htons (VL_API_TRACE_DETAILS + (tdmp->msg_id_base)); + dmp->context = mp->context; + last_thread_id = dmp->thread_id = ntohl (i); + last_position = dmp->position = ntohl (j); + vl_api_vec_to_api_string (s, &dmp->trace_data); + dmp->more_threads = 0; + dmp->more_this_thread = 0; + + /* Last record in the batch? */ + if (max_records == 1) + { + /* More threads, but not more in this thread? */ + if (j == (vec_len (client_trace_cache[i]) - 1)) + dmp->more_threads = 1; + else + dmp->more_this_thread = 1; + } + /* Done, may or may not be at the end of a batch. */ + dmp->done = 0; + if (i == (vec_len (client_trace_cache) - 1) && + j == (vec_len (client_trace_cache[i]) - 1)) + { + last_done = dmp->done = 1; + last_more_threads = dmp->more_threads = 0; + last_more_this_thread = dmp->more_this_thread = 0; + vl_api_send_msg (rp, (u8 *) dmp); + goto doublebreak; + } + last_done = dmp->done; + vl_api_send_msg (rp, (u8 *) dmp); + + max_records--; + } + iterator_position = 0; + } + +doublebreak:; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + rmp->_vl_msg_id = htons (VL_API_TRACE_DUMP_REPLY + (tdmp->msg_id_base)); + rmp->context = mp->context; + rmp->retval = clib_host_to_net_u32 (retval); + rmp->last_thread_id = last_thread_id; + rmp->last_position = last_position; + rmp->done = last_done; + rmp->more_this_thread = last_more_this_thread; + rmp->more_threads = last_more_threads; + + /* Tag cleanup flushes to make life easy for the client */ + if (iterator_thread_id == ~0 && iterator_position == ~0) + { + rmp->retval = 0; + rmp->done = 1; + rmp->flush_only = 1; + } + vl_api_send_msg (rp, (u8 *) rmp); + + vec_free (s); +} + +/* API definitions */ +#include + +static clib_error_t * +tracedump_init (vlib_main_t * vm) +{ + tracedump_main_t *tdmp = &tracedump_main; + api_main_t *am = vlibapi_get_main (); + + clib_error_t *error = 0; + + tdmp->vlib_main = vm; + tdmp->vnet_main = vnet_get_main (); + + /* Add our API messages to the global name_crc hash table */ + tdmp->msg_id_base = setup_message_id_table (); + + am->is_mp_safe[tdmp->msg_id_base + VL_API_TRACE_DUMP] = 1; + + return error; +} + +VLIB_INIT_FUNCTION (tracedump_init); +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER () = +{ + .version = VPP_BUILD_VER, + .description = "Streaming packet trace dump plugin", +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/tracedump/tracedump.h b/src/plugins/tracedump/tracedump.h new file mode 100644 index 00000000000..2bd93a2f4bd --- /dev/null +++ b/src/plugins/tracedump/tracedump.h @@ -0,0 +1,55 @@ + +/* + * tracedump.h - skeleton vpp engine plug-in header file + * + * Copyright (c) + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __included_tracedump_h__ +#define __included_tracedump_h__ + +#include +#include +#include + +#include +#include + +typedef struct +{ + /* API message ID base */ + u16 msg_id_base; + + /* + * cached reply data + * traces [client_id][thread_id][trace] + */ + vlib_trace_header_t ****traces; + + /* convenience */ + vlib_main_t *vlib_main; + vnet_main_t *vnet_main; + ethernet_main_t *ethernet_main; +} tracedump_main_t; + +extern tracedump_main_t tracedump_main; + +#endif /* __included_tracedump_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/tracedump/tracedump_test.c b/src/plugins/tracedump/tracedump_test.c new file mode 100644 index 00000000000..ba811b5d38c --- /dev/null +++ b/src/plugins/tracedump/tracedump_test.c @@ -0,0 +1,150 @@ +/* + * tracedump.c - tracedump vpp-api-test plug-in + * + * Copyright (c) + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include +#include + +#define __plugin_msg_base tracedump_test_main.msg_id_base +#include + +/* Declare message IDs */ +#include +#include + +typedef struct +{ + /* API message ID base */ + u16 msg_id_base; + vat_main_t *vat_main; +} tracedump_test_main_t; + +tracedump_test_main_t tracedump_test_main; + +static void +vl_api_trace_details_t_handler (vl_api_trace_details_t * dmp) +{ + u32 thread_id, position; + + thread_id = clib_net_to_host_u32 (dmp->thread_id); + position = clib_net_to_host_u32 (dmp->position); + fformat + (stdout, + "thread %d position %d more_this_thread %d more_threads %d done %d\n", + thread_id, position, (u32) dmp->more_this_thread, + (u32) dmp->more_threads, (u32) dmp->done); + fformat (stdout, " %U\n", vl_api_format_string, (&dmp->trace_data)); +} + + +static void +vl_api_trace_dump_reply_t_handler (vl_api_trace_dump_reply_t * rmp) +{ + tracedump_test_main_t *ttm = &tracedump_test_main; + vat_main_t *vam = ttm->vat_main; + vl_api_trace_dump_t *mp; + i32 retval = (i32) clib_net_to_host_u32 (rmp->retval); + u32 thread_id, position; + + if (retval != 0 || rmp->done) + { + vam->result_ready = 1; + vam->retval = retval; + + /* Clear the cache */ + if (retval == 0 && rmp->flush_only == 0) + { + M (TRACE_DUMP, mp); + mp->clear_cache = 1; + mp->thread_id = 0xFFFFFFFF; + mp->position = 0xFFFFFFFF; + S (mp); + } + return; + } + + /* Figure out where the next batch starts */ + thread_id = clib_host_to_net_u32 (rmp->last_thread_id); + position = clib_host_to_net_u32 (rmp->last_position); + + if (rmp->more_threads) + { + position = 0; + thread_id++; + } + else + position++; + + M (TRACE_DUMP, mp); + mp->clear_cache = 0; + mp->thread_id = clib_host_to_net_u32 (thread_id); + mp->position = clib_host_to_net_u32 (position); + mp->max_records = clib_host_to_net_u32 (10); + S (mp); +} + +static int +api_trace_dump (vat_main_t * vam) +{ + vl_api_trace_dump_t *mp; + int ret; + + M (TRACE_DUMP, mp); + mp->clear_cache = 1; + mp->thread_id = 0; + mp->position = 0; + mp->max_records = clib_host_to_net_u32 (10); + + S (mp); + + W (ret); + return ret; +} + +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_endianfun +#include +#undef vl_endianfun +#define vl_printfun +#include +#undef vl_printfun + +void +manual_setup_message_id_table (vat_main_t * vam) +{ + vl_msg_api_set_handlers (VL_API_TRACE_DETAILS + + tracedump_test_main.msg_id_base, "trace_details", + vl_api_trace_details_t_handler, vl_noop_handler, + vl_api_trace_details_t_endian, + vl_api_trace_details_t_print, + sizeof (vl_api_trace_details_t), 1); +} + +#define VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE manual_setup_message_id_table +#define VL_API_TRACE_DUMP_REPLY_T_HANDLER + +#include + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vlib/trace.h b/src/vlib/trace.h index f83a6d11e51..233c0ea0b56 100644 --- a/src/vlib/trace.h +++ b/src/vlib/trace.h @@ -112,6 +112,8 @@ typedef struct format_function_t format_vlib_trace; +void trace_apply_filter (struct vlib_main_t *vm); + #endif /* included_vlib_trace_h */ /* -- 2.16.6