summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhei Makarov <serhei@serhei.io>2023-12-11 13:56:23 -0500
committerSerhei Makarov <serhei@serhei.io>2023-12-11 13:56:23 -0500
commite6fcd99b82b2f1d818f29dfd8a18bd81fe15a89f (patch)
tree790bddc25a77e06438d62136f00b3f95619dcb3e
parenta62ee609ed5ea7a0a2f09a5cc9ac48cf4db5b983 (diff)
eu-stacktrace WIP: initial version of Dwfl caching, print comm
-rw-r--r--src/stacktrace.c161
1 files changed, 144 insertions, 17 deletions
diff --git a/src/stacktrace.c b/src/stacktrace.c
index 8d726528..5f224999 100644
--- a/src/stacktrace.c
+++ b/src/stacktrace.c
@@ -715,15 +715,141 @@ sysprof_find_procfile (Dwfl *dwfl, pid_t *pid, Elf **elf, int *elf_fd)
return 0;
}
+/* TODO: This echoes lib/dynamicsizehash.* with some modifications. */
+typedef struct
+{
+ bool used;
+ pid_t pid;
+ Dwfl *dwfl;
+ char *comm;
+} dwfltab_ent;
+
+typedef struct
+{
+ ssize_t size;
+ ssize_t filled;
+ dwfltab_ent *table;
+} dwfltab;
+
+/* TODO: Store in sui, update below functions. */
+/* XXX initial size must be a prime */
+#define DWFLTAB_DEFAULT_SIZE 1021
+dwfltab default_table;
+
+/* XXX based on lib/dynamicsizehash.* *_init */
+void dwfltab_init(void)
+{
+ dwfltab *htab = &default_table;
+ htab->size = DWFLTAB_DEFAULT_SIZE;
+ htab->filled = 0;
+ htab->table = calloc ((htab->size + 1), sizeof(htab->table[0]));
+}
+
+/* XXX based on lib/dynamicsizehash.* *_find */
+dwfltab_ent *dwfltab_find(pid_t pid)
+{
+ dwfltab *htab = &default_table;
+ ssize_t idx = 1 + (htab->size > (ssize_t)pid ? (ssize_t)pid : (ssize_t)pid % htab->size);
+
+ if (!htab->table[idx].used)
+ goto found;
+ if (htab->table[idx].pid == pid)
+ goto found;
+
+ int64_t hash = 1 + pid % (htab->size - 2);
+ do
+ {
+ if (idx <= hash)
+ idx = htab->size + idx - hash;
+ else
+ idx -= hash;
+
+ if (!htab->table[idx].used)
+ goto found;
+ if (htab->table[idx].pid == pid)
+ goto found;
+ }
+ while (true);
+
+ found:
+ if (htab->table[idx].pid == 0)
+ {
+ /* TODO: Implement resizing or LRU eviction? */
+ if (100 * htab->filled > 90 * htab->size)
+ return NULL;
+ htab->table[idx].used = true;
+ htab->table[idx].pid = pid;
+ htab->filled += 1;
+ }
+ return &htab->table[idx];
+}
+
+Dwfl *
+pid_find_dwfl (pid_t pid)
+{
+ dwfltab_ent *entry = dwfltab_find(pid);
+ if (entry == NULL)
+ return NULL;
+ return entry->dwfl;
+}
+
+char *
+pid_find_comm (pid_t pid)
+{
+ dwfltab_ent *entry = dwfltab_find(pid);
+ if (entry == NULL)
+ return "<unknown>";
+ if (entry->comm != NULL)
+ return entry->comm;
+ char name[64];
+ int i = snprintf (name, sizeof(name), "/proc/%ld/comm", (long) pid);
+ FILE *procfile = fopen(name, "r");
+ if (procfile == NULL)
+ goto fail;
+ size_t linelen = 0;
+ i = getline(&entry->comm, &linelen, procfile);
+ if (i < 0)
+ {
+ free(entry->comm);
+ goto fail;
+ }
+ for (i = linelen - 1; i > 0; i--)
+ if (entry->comm[i] == '\n')
+ entry->comm[i] = '\0';
+ fclose(procfile);
+ goto done;
+ fail:
+ entry->comm = (char *)malloc(16);
+ snprintf (entry->comm, 16, "<unknown>");
+ done:
+ return entry->comm;
+}
+
+/* Cache dwfl structs in a basic hash table. */
+void
+pid_store_dwfl (pid_t pid, Dwfl *dwfl)
+{
+ dwfltab_ent *entry = dwfltab_find(pid);
+ if (entry == NULL)
+ return;
+ entry->pid = pid;
+ entry->dwfl = dwfl;
+ pid_find_comm(pid);
+ return;
+}
+
Dwfl *
sysprof_init_dwfl (struct sysprof_unwind_info *sui,
SysprofCaptureStackUser *ev,
SysprofCaptureUserRegs *regs)
{
pid_t pid = ev->frame.pid;
- /* TODO: Needs a per-process(?) caching scheme to avoid initializing on every sample. */
(void)sui;
- Dwfl *dwfl = dwfl_begin (&sample_callbacks);
+
+ Dwfl *dwfl = pid_find_dwfl(pid);
+ if (dwfl != NULL)
+ return dwfl;
+ dwfl = dwfl_begin (&sample_callbacks);
int err = dwfl_linux_proc_report (dwfl, pid);
if (err < 0)
@@ -783,17 +909,10 @@ sysprof_init_dwfl (struct sysprof_unwind_info *sui,
free (sample_arg);
return NULL;
}
+ pid_store_dwfl (pid, dwfl);
return dwfl;
}
-void
-sysprof_recycle_dwfl (struct sysprof_unwind_info *sui __attribute__((unused)),
- Dwfl *dwfl)
-{
- /* TODO: Needs a per-process(?) caching scheme to avoid de-initializing on every sample. */
- dwfl_end (dwfl);
-}
-
int
sysprof_unwind_frame_cb (Dwfl_Frame *state, void *arg)
{
@@ -831,6 +950,18 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg)
{
struct sysprof_unwind_info *sui = (struct sysprof_unwind_info *)arg;
ssize_t n_write;
+
+ /* additional diagnostic to display process name */
+ char *comm = NULL;
+ if (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE || frame->type == SYSPROF_CAPTURE_FRAME_STACK_USER)
+ comm = pid_find_comm(frame->pid);
+
+ if (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE)
+ {
+ /* XXX additional diagnostics for comparing to eu-stacktrace unwind */
+ SysprofCaptureSample *ev_sample = (SysprofCaptureSample *)frame;
+ fprintf(stderr, "sysprof_unwind_cb pid %lld (%s): callchain sample with %d frames\n", (long long)frame->pid, comm, ev_sample->n_addrs);
+ }
if (frame->type != SYSPROF_CAPTURE_FRAME_STACK_USER)
{
sysprof_reader_bswap_frame (sui->reader, frame); /* reverse the earlier bswap */
@@ -851,10 +982,7 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg)
Dwfl *dwfl = sysprof_init_dwfl (sui, ev, regs);
if (dwfl == NULL)
{
-#ifdef DEBUG
- fprintf(stderr, "DEBUG sysprof_init_dwfl pid %lld (failed)\n", (long long)frame->pid);
-#endif
- sysprof_recycle_dwfl (sui, dwfl);
+ fprintf(stderr, "DEBUG sysprof_init_dwfl pid %lld (%s) (failed)\n", (long long)frame->pid, comm);
return SYSPROF_CB_OK;
}
sui->n_addrs = 0;
@@ -865,7 +993,7 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg)
fprintf(stderr, "DEBUG dwfl_getthread_frames pid %lld: %s\n", (long long)frame->pid, dwfl_errmsg(-1));
#endif
}
- fprintf(stderr, "sysprof_unwind_cb pid %lld: unwound %d frames\n", (long long)frame->pid, sui->n_addrs); /* TODO rename sui->nframes to sui->n_addrs */
+ fprintf(stderr, "sysprof_unwind_cb pid %lld (%s): unwound %d frames\n", (long long)frame->pid, comm, sui->n_addrs);
/* Assemble and output callchain frame. */
/* TODO: assert(sizeof(Dwarf_Addr) == sizeof(SysprofCaptureAddress)); */
@@ -877,7 +1005,6 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg)
#ifdef DEBUG
fprintf(stderr, "DEBUG sysprof_unwind_cb frame size %d is too large (max %d)\n", len, USHRT_MAX);
#endif
- sysprof_recycle_dwfl (sui, dwfl);
return SYSPROF_CB_OK;
}
SysprofCaptureFrame *out_frame = (SysprofCaptureFrame *)ev_callchain;
@@ -894,7 +1021,6 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg)
n_write = write (sui->output_fd, ev_callchain, len);
if (n_write < 0)
error (EXIT_BAD, errno, N_("Write error to file or FIFO '%s'"), output_path);
- sysprof_recycle_dwfl (sui, dwfl);
return SYSPROF_CB_OK;
}
#endif
@@ -978,6 +1104,7 @@ Utility is a work-in-progress, see README.eu-stacktrace in the source branch.")
}
else /* processing_mode == MODE_NAIVE */
{
+ dwfltab_init();
struct sysprof_unwind_info sui;
sui.output_fd = output_fd;
sui.reader = reader;