Files
sysprof/src/tools/sysprof-dump.c
Christian Hergert ab12f6a18a tools: don't resolve unless requested
It can be way to slow to be useful for quick things.
2023-06-27 13:39:06 -07:00

446 lines
15 KiB
C

/* sysprof-dump.c
*
* Copyright 2016-2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "config.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <glib-object.h>
#include <sysprof-capture.h>
#include <sysprof.h>
static gboolean list_files = FALSE;
static gboolean resolve = FALSE;
static const GOptionEntry main_entries[] = {
{ "list-files", 'l', 0, G_OPTION_ARG_NONE, &list_files, "List files within the capture" },
{ "resolve", 'r', 0, G_OPTION_ARG_NONE, &resolve, "Resolve symbols" },
{ 0 }
};
static char *
symbolize (GPtrArray *resolvers,
const SysprofCaptureFrame *frame,
SysprofAddressContext *context,
SysprofAddress address)
{
SysprofAddressContext last_context = *context;
if (sysprof_address_is_context_switch (address, context))
return g_strdup (sysprof_address_context_to_string (last_context));
if (last_context == SYSPROF_ADDRESS_CONTEXT_NONE)
last_context = SYSPROF_ADDRESS_CONTEXT_USER;
for (guint j = 0; j < resolvers->len; j++)
{
SysprofSymbolResolver *resolver = g_ptr_array_index (resolvers, j);
GQuark tag = 0;
char *str;
str = sysprof_symbol_resolver_resolve_with_context (resolver, frame->time, frame->pid, last_context, address, &tag);
if (str != NULL)
return str;
}
return g_strdup ("??");
}
gint
main (gint argc,
gchar *argv[])
{
g_autoptr(GOptionContext) option_context = g_option_context_new ("- dump capture data");
g_autoptr(GPtrArray) resolvers = NULL;
g_autoptr(GError) error = NULL;
SysprofCaptureReader *reader;
SysprofCaptureFrameType type;
GHashTable *ctrtypes;
gint64 begin_time;
gint64 end_time;
g_option_context_add_main_entries (option_context, main_entries, NULL);
if (!g_option_context_parse (option_context, &argc, &argv, &error))
{
g_printerr ("%s\n", error->message);
return EXIT_FAILURE;
}
if (argc != 2)
{
g_printerr ("usage: %s FILENAME\n", argv[0]);
return EXIT_FAILURE;
}
if ((reader = sysprof_capture_reader_new (argv[1])) == NULL)
{
int errsv = errno;
g_printerr ("%s\n", g_strerror (errsv));
return EXIT_FAILURE;
}
if (list_files)
{
g_autofree const gchar **files = sysprof_capture_reader_list_files (reader);
for (gsize i = 0; files[i]; i++)
g_print ("%s\n", files[i]);
return EXIT_SUCCESS;
}
resolvers = g_ptr_array_new_with_free_func (g_object_unref);
if (resolve)
{
g_ptr_array_add (resolvers, sysprof_capture_symbol_resolver_new ());
g_ptr_array_add (resolvers, sysprof_kernel_symbol_resolver_new ());
g_ptr_array_add (resolvers, sysprof_elf_symbol_resolver_new ());
for (guint i = 0; i < resolvers->len; i++)
{
sysprof_symbol_resolver_load (g_ptr_array_index (resolvers, i), reader);
sysprof_capture_reader_reset (reader);
}
}
ctrtypes = g_hash_table_new (NULL, NULL);
begin_time = sysprof_capture_reader_get_start_time (reader);
#define SET_CTR_TYPE(i,t) g_hash_table_insert(ctrtypes, GINT_TO_POINTER(i), GINT_TO_POINTER(t))
#define GET_CTR_TYPE(i) GPOINTER_TO_INT(g_hash_table_lookup(ctrtypes, GINT_TO_POINTER(i)))
begin_time = sysprof_capture_reader_get_start_time (reader);
end_time = sysprof_capture_reader_get_end_time (reader);
g_print ("Capture Time Range: %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT " (%lf)\n",
begin_time, end_time, (end_time - begin_time) / (gdouble)SYSPROF_NSEC_PER_SEC);
while (sysprof_capture_reader_peek_type (reader, &type))
{
switch (type)
{
case SYSPROF_CAPTURE_FRAME_EXIT:
{
const SysprofCaptureExit *ex = sysprof_capture_reader_read_exit (reader);
if (ex == NULL)
return EXIT_FAILURE;
g_print ("EXIT: pid=%d\n", ex->frame.pid);
break;
}
case SYSPROF_CAPTURE_FRAME_FORK:
{
const SysprofCaptureFork *fk = sysprof_capture_reader_read_fork (reader);
if (fk == NULL)
return EXIT_FAILURE;
g_print ("FORK: pid=%d child_pid=%d\n", fk->frame.pid, fk->child_pid);
break;
}
case SYSPROF_CAPTURE_FRAME_OVERLAY:
{
const SysprofCaptureOverlay *pr = sysprof_capture_reader_read_overlay (reader);
const char *src = pr->data;
const char *dst = &pr->data[pr->src_len+1];
if (pr == NULL)
return EXIT_FAILURE;
g_print ("OVERLAY: pid=%d layer=%u src=%s dst=%s\n", pr->frame.pid, pr->layer, src, dst);
break;
}
case SYSPROF_CAPTURE_FRAME_JITMAP:
{
const SysprofCaptureJitmap *jitmap = sysprof_capture_reader_read_jitmap (reader);
SysprofCaptureJitmapIter iter;
SysprofCaptureAddress addr;
const gchar *str;
if (jitmap == NULL)
return EXIT_FAILURE;
g_print ("JITMAP:\n");
sysprof_capture_jitmap_iter_init (&iter, jitmap);
while (sysprof_capture_jitmap_iter_next (&iter, &addr, &str))
g_print (" " SYSPROF_CAPTURE_ADDRESS_FORMAT " : %s\n", addr, str);
break;
}
case SYSPROF_CAPTURE_FRAME_LOG:
{
const SysprofCaptureLog *log = sysprof_capture_reader_read_log (reader);
gdouble ptime = (log->frame.time - begin_time) / (gdouble)SYSPROF_NSEC_PER_SEC;
g_print ("LOG: pid=%d time=%" G_GINT64_FORMAT " (%lf)\n"
" severity = %d\n"
" domain = %s\n"
" message = %s\n",
log->frame.pid, log->frame.time, ptime,
log->severity, log->domain, log->message);
break;
}
case SYSPROF_CAPTURE_FRAME_MAP:
{
const SysprofCaptureMap *map = sysprof_capture_reader_read_map (reader);
g_print ("MAP: pid=%d time=%" G_GINT64_FORMAT "\n"
" start = %" G_GUINT64_FORMAT "\n"
" end = %" G_GUINT64_FORMAT "\n"
" offset = %" G_GUINT64_FORMAT "\n"
" inode = %" G_GUINT64_FORMAT "\n"
" filename = %s\n",
map->frame.pid, map->frame.time,
map->start, map->end, map->offset, map->inode, map->filename);
break;
}
case SYSPROF_CAPTURE_FRAME_FILE_CHUNK:
{
const SysprofCaptureFileChunk *file_chunk = sysprof_capture_reader_read_file (reader);
gdouble ptime = (file_chunk->frame.time - begin_time) / (gdouble)SYSPROF_NSEC_PER_SEC;
g_print ("FILE_CHUNK: pid=%d time=%" G_GINT64_FORMAT " (%lf)\n"
" path = %s\n"
" is_last = %d\n"
" bytes = %d\n",
file_chunk->frame.pid, file_chunk->frame.time, ptime,
file_chunk->path, file_chunk->is_last, file_chunk->len);
break;
}
case SYSPROF_CAPTURE_FRAME_MARK:
{
const SysprofCaptureMark *mark = sysprof_capture_reader_read_mark (reader);
gdouble ptime = (mark->frame.time - begin_time) / (gdouble)SYSPROF_NSEC_PER_SEC;
g_print ("MARK: pid=%d time=%" G_GINT64_FORMAT " (%lf)\n"
" group = %s\n"
" name = %s\n"
" duration = %" G_GUINT64_FORMAT "\n"
" message = %s\n",
mark->frame.pid, mark->frame.time, ptime,
mark->group, mark->name, mark->duration, mark->message);
break;
}
case SYSPROF_CAPTURE_FRAME_METADATA:
{
const SysprofCaptureMetadata *metadata = sysprof_capture_reader_read_metadata (reader);
gdouble ptime = (metadata->frame.time - begin_time) / (gdouble)SYSPROF_NSEC_PER_SEC;
g_print ("METADATA: pid=%d time=%" G_GINT64_FORMAT " (%lf)\n"
" id = %s\n"
"\"\"\"\n%s\n\"\"\"\n",
metadata->frame.pid, metadata->frame.time, ptime,
metadata->id, metadata->metadata);
break;
}
case SYSPROF_CAPTURE_FRAME_PROCESS:
{
const SysprofCaptureProcess *pr = sysprof_capture_reader_read_process (reader);
if (pr == NULL)
perror ("Failed to read process");
g_print ("PROCESS: pid=%d cmdline=%s time=%" G_GINT64_FORMAT "\n", pr->frame.pid, pr->cmdline, pr->frame.time);
break;
}
case SYSPROF_CAPTURE_FRAME_SAMPLE:
{
const SysprofCaptureSample *s = sysprof_capture_reader_read_sample (reader);
gdouble ptime = (s->frame.time - begin_time) / (gdouble)SYSPROF_NSEC_PER_SEC;
SysprofAddressContext context = SYSPROF_ADDRESS_CONTEXT_NONE;
g_print ("SAMPLE: pid=%d tid=%d time=%" G_GINT64_FORMAT " (%lf)\n",
s->frame.pid, s->tid, s->frame.time, ptime);
for (guint i = 0; i < s->n_addrs; i++)
{
if (resolve)
{
g_autofree char *name = symbolize (resolvers, &s->frame, &context, s->addrs[i]);
g_print (" " SYSPROF_CAPTURE_ADDRESS_FORMAT " (%s)\n", s->addrs[i], name);
}
else
{
g_print (" " SYSPROF_CAPTURE_ADDRESS_FORMAT "\n", s->addrs[i]);
}
}
break;
}
case SYSPROF_CAPTURE_FRAME_TRACE:
{
const SysprofCaptureTrace *s = sysprof_capture_reader_read_trace (reader);
gdouble ptime = (s->frame.time - begin_time) / (gdouble)SYSPROF_NSEC_PER_SEC;
SysprofAddressContext context = SYSPROF_ADDRESS_CONTEXT_NONE;
g_print ("TRACE: pid=%d tid=%d time=%" G_GINT64_FORMAT " (%lf) %s\n",
s->frame.pid, s->tid, s->frame.time, ptime,
s->entering ? "ENTER" : "EXIT");
for (guint i = 0; i < s->n_addrs; i++)
{
if (resolve)
{
g_autofree char *name = symbolize (resolvers, &s->frame, &context, s->addrs[i]);
g_print (" " SYSPROF_CAPTURE_ADDRESS_FORMAT " (%s)\n", s->addrs[i], name);
}
else
{
g_print (" " SYSPROF_CAPTURE_ADDRESS_FORMAT "\n", s->addrs[i]);
}
}
break;
}
case SYSPROF_CAPTURE_FRAME_TIMESTAMP:
{
const SysprofCaptureTimestamp *ts = sysprof_capture_reader_read_timestamp (reader);
g_print ("TIMESTAMP: pid=%d time=%" G_GINT64_FORMAT "\n", ts->frame.pid, ts->frame.time);
break;
}
case SYSPROF_CAPTURE_FRAME_CTRDEF:
{
const SysprofCaptureCounterDefine *def = sysprof_capture_reader_read_counter_define (reader);
gdouble ptime = (def->frame.time - begin_time) / (gdouble)SYSPROF_NSEC_PER_SEC;
guint i;
g_print ("NEW COUNTERS: pid=%d time=%" G_GINT64_FORMAT " (%lf)\n", def->frame.pid, def->frame.time, ptime);
for (i = 0; i < def->n_counters; i++)
{
const SysprofCaptureCounter *ctr = &def->counters[i];
SET_CTR_TYPE (ctr->id, ctr->type);
g_print (" COUNTER(%03d<%s>): %s\n"
" %s\n"
" %s\n"
"\n",
ctr->id,
ctr->type == SYSPROF_CAPTURE_COUNTER_INT64 ? "i64" : "f64",
ctr->category,
ctr->name,
ctr->description);
}
}
break;
case SYSPROF_CAPTURE_FRAME_CTRSET:
{
const SysprofCaptureCounterSet *set = sysprof_capture_reader_read_counter_set (reader);
gdouble ptime = (set->frame.time - begin_time) / (gdouble)SYSPROF_NSEC_PER_SEC;
guint i;
g_print ("SET COUNTERS: pid=%d time=%" G_GINT64_FORMAT " (%lf)\n", set->frame.pid, set->frame.time, ptime);
for (i = 0; i < set->n_values; i++)
{
const SysprofCaptureCounterValues *values = &set->values[i];
guint j;
for (j = 0; j < G_N_ELEMENTS (values->ids); j++)
{
if (values->ids[j])
{
if (GET_CTR_TYPE (values->ids[j]) == SYSPROF_CAPTURE_COUNTER_INT64)
g_print (" COUNTER(%d): %" G_GINT64_FORMAT "\n",
values->ids[j],
values->values[j].v64);
else if (GET_CTR_TYPE (values->ids[j]) == SYSPROF_CAPTURE_COUNTER_DOUBLE)
g_print (" COUNTER(%d): %lf\n",
values->ids[j],
values->values[j].vdbl);
}
}
}
}
break;
case SYSPROF_CAPTURE_FRAME_ALLOCATION:
{
const SysprofCaptureAllocation *ev = sysprof_capture_reader_read_allocation (reader);
gdouble ptime = (ev->frame.time - begin_time) / (gdouble)SYSPROF_NSEC_PER_SEC;
SysprofAddressContext context = SYSPROF_ADDRESS_CONTEXT_NONE;
g_print ("%s: pid=%d tid=%d addr=0x%" G_GINT64_MODIFIER "x size=%" G_GINT64_FORMAT " time=%" G_GINT64_FORMAT " (%lf)\n",
ev->alloc_size > 0 ? "ALLOC" : "FREE",
ev->frame.pid, ev->tid,
ev->alloc_addr, ev->alloc_size,
ev->frame.time, ptime);
for (guint i = 0; i < ev->n_addrs; i++)
{
if (resolve)
{
g_autofree char *name = symbolize (resolvers, &ev->frame, &context, ev->addrs[i]);
g_print (" " SYSPROF_CAPTURE_ADDRESS_FORMAT " (%s)\n", ev->addrs[i], name);
}
else
{
g_print (" " SYSPROF_CAPTURE_ADDRESS_FORMAT "\n", ev->addrs[i]);
}
}
}
break;
default:
g_print ("Skipping unknown frame type: (%d): ", type);
if (!sysprof_capture_reader_skip (reader))
{
g_print ("Failed\n");
return EXIT_FAILURE;
}
g_print ("Success\n");
break;
}
}
return EXIT_SUCCESS;
}