From 7561c45bcce4913620388a60be36dc1284d484a0 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Fri, 19 Jul 2024 15:40:59 +0200 Subject: vlib: add "save memory-trace" debug CLI Save memory traces of the currently traced heap in JSON format to file which can be used as machine-readable data for memory leak diagnose. Type: improvement Change-Id: I277f5be5838510e907c4dd7a8a4e9a883cb67bc3 Signed-off-by: Matus Fabian --- src/vlib/cli.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) (limited to 'src/vlib') diff --git a/src/vlib/cli.c b/src/vlib/cli.c index 98d57c6ccb0..4198b4b0976 100644 --- a/src/vlib/cli.c +++ b/src/vlib/cli.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -1114,6 +1115,111 @@ VLIB_CLI_COMMAND (enable_disable_memory_trace_command, static) = { .function = enable_disable_memory_trace, }; +static clib_error_t * +save_memory_trace (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + char *file, *chroot_file; + uword was_enabled; + mheap_trace_t *t, *mem_traces = 0; + u8 *tmp; + cJSON *traces, *trace, *traceback, *symbol; + int i; + FILE *fp; + char *json_str = 0; + + cJSON_Hooks cjson_hooks = { + .malloc_fn = clib_mem_alloc, + .free_fn = clib_mem_free, + .realloc_fn = clib_mem_realloc, + }; + cJSON_InitHooks (&cjson_hooks); + + if (!unformat (input, "%s", &file)) + { + vlib_cli_output (vm, "expected file name, got `%U'", + format_unformat_error, input); + return 0; + } + + /* It's fairly hard to get "../oopsie" through unformat; just in case */ + if (strstr (file, "..") || strchr (file, '/')) + { + vlib_cli_output (vm, "illegal characters in filename '%s'", file); + return 0; + } + chroot_file = (char *) format (0, "/tmp/%s%c", file, 0); + vec_free (file); + fp = fopen ((char *) chroot_file, "w"); + if (fp == NULL) + { + vlib_cli_output (vm, "couldn't open output file %s '%s'", chroot_file); + vec_free (chroot_file); + return 0; + } + + was_enabled = clib_mem_trace_enable_disable (0); + vlib_cli_output (vm, "Saving trace to '%s'", chroot_file); + mem_traces = clib_mem_trace_dup (current_traced_heap); + traces = cJSON_CreateArray (); + vec_foreach (t, mem_traces) + { + /* Skip over free elements. */ + if (t->n_allocations == 0) + continue; + + trace = cJSON_CreateObject (); + cJSON_AddNumberToObject (trace, "count", t->n_allocations); + cJSON_AddNumberToObject (trace, "bytes", t->n_bytes); + tmp = format (0, "%p%c", t->offset, 0); + cJSON_AddStringToObject (trace, "sample", (char *) tmp); + vec_free (tmp); + traceback = cJSON_AddArrayToObject (trace, "traceback"); + for (i = 0; i < ARRAY_LEN (t->callers) && t->callers[i]; i++) + { +#if defined(CLIB_UNIX) && !defined(__APPLE__) + tmp = format (0, "%U%c\n", format_clib_elf_symbol_with_address, + t->callers[i], 0); + symbol = cJSON_CreateString ((char *) tmp); + cJSON_AddItemToArray (traceback, symbol); + vec_free (tmp); +#else + tmp = format (0, "%p%c\n", t->callers[i], 0); + symbol = cJSON_CreateString ((char *) tmp); + cJSON_AddItemToArray (traceback, symbol); + vec_free (tmp); +#endif + } + + cJSON_AddItemToArray (traces, trace); + } + json_str = cJSON_PrintUnformatted (traces); + cJSON_Delete (traces); + fputs (json_str, fp); + fclose (fp); + clib_mem_free (json_str); + + vec_free (mem_traces); + clib_mem_trace_enable_disable (was_enabled); + + vec_free (chroot_file); + + return 0; +} + +/*? + * Save memory traces of the currently traced heap in JSON format to file. + * Only filename can be specified, path is fixed (/tmp/). + * + * @cliexpar + * @cliexcmd{save memory-trace mem_trace.json} +?*/ +VLIB_CLI_COMMAND (save_memory_trace_command, static) = { + .path = "save memory-trace", + .short_help = "save memory-trace ", + .function = save_memory_trace, +}; + static clib_error_t * restart_cmd_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) -- cgit 1.2.3-korg