summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatus Fabian <matfabia@cisco.com>2024-07-19 15:40:59 +0200
committerFlorin Coras <florin.coras@gmail.com>2024-07-22 15:50:22 +0000
commit7561c45bcce4913620388a60be36dc1284d484a0 (patch)
treef028f1572a2460d0e35675f02f7551a0f5202a28 /src
parentef827b3efc5270d4772cf8482b0b08ef7fbbf8c5 (diff)
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 <matfabia@cisco.com>
Diffstat (limited to 'src')
-rw-r--r--src/vlib/cli.c106
1 files changed, 106 insertions, 0 deletions
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 <vppinfra/callback.h>
#include <vppinfra/cpu.h>
#include <vppinfra/elog.h>
+#include <vppinfra/cJSON.h>
#include <unistd.h>
#include <ctype.h>
@@ -1115,6 +1116,111 @@ VLIB_CLI_COMMAND (enable_disable_memory_trace_command, static) = {
};
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/<filename>).
+ *
+ * @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 <filename>",
+ .function = save_memory_trace,
+};
+
+static clib_error_t *
restart_cmd_fn (vlib_main_t * vm, unformat_input_t * input,
vlib_cli_command_t * cmd)
{