From b2b3b48b522aa9d1279265b7e15483d861d75cb7 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 28 May 2019 12:15:31 -0700 Subject: [PATCH] libsysprof: add __symbols__ file with supplemental symbol data This can be used by a future symbol resolver to resolve symbols from the target host rather than on the capture view host. --- src/libsysprof/sysprof-symbols-source.c | 212 ++++++++++++++++++++++++ 1 file changed, 212 insertions(+) diff --git a/src/libsysprof/sysprof-symbols-source.c b/src/libsysprof/sysprof-symbols-source.c index 8e464ef9..f287b9c9 100644 --- a/src/libsysprof/sysprof-symbols-source.c +++ b/src/libsysprof/sysprof-symbols-source.c @@ -22,8 +22,11 @@ #include "config.h" +#include "sysprof-elf-symbol-resolver.h" #include "sysprof-symbols-source.h" +#include "sysprof-platform.h" + struct _SysprofSymbolsSource { GObject parent_instance; @@ -71,17 +74,218 @@ sysprof_symbols_source_set_writer (SysprofSource *source, self->writer = sysprof_capture_writer_ref (writer); } +SYSPROF_ALIGNED_BEGIN(1) +typedef struct +{ + SysprofCaptureAddress begin; + SysprofCaptureAddress end; + guint32 pid; + guint32 offset; + guint32 tag_offset; + guint32 padding; +} Symbol +SYSPROF_ALIGNED_END(1); + +G_STATIC_ASSERT (sizeof (Symbol) == 32); + +static void +symbol_free (Symbol *sym) +{ + g_slice_free (Symbol, sym); +} + +static gint +symbol_compare (gconstpointer a, + gconstpointer b, + G_GNUC_UNUSED gpointer unused) +{ + const Symbol *aa = a; + const Symbol *bb = b; + + if (aa->pid < bb->pid) + return -1; + + if (aa->pid > bb->pid) + return 1; + + if (aa->begin < bb->begin) + return -1; + + if (aa->begin > bb->begin) + return -1; + + return 0; +} + static void sysprof_symbols_source_supplement (SysprofSource *source, SysprofCaptureReader *reader) { SysprofSymbolsSource *self = (SysprofSymbolsSource *)source; + g_autoptr(SysprofSymbolResolver) native = NULL; + g_autoptr(GByteArray) strings = NULL; + g_autoptr(GHashTable) seen = NULL; + g_autoptr(GSequence) symbols = NULL; + SysprofCaptureFrameType type; + guint base; + gint fd = -1; g_assert (SYSPROF_IS_SYMBOLS_SOURCE (self)); g_assert (reader != NULL); g_assert (self->writer != NULL); + if (-1 == (fd = sysprof_memfd_create ("[sysprof-decode]"))) + return; + strings = g_byte_array_new (); + symbols = g_sequence_new ((GDestroyNotify) symbol_free); + seen = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + native = sysprof_elf_symbol_resolver_new (); + sysprof_symbol_resolver_load (native, reader); + sysprof_capture_reader_reset (reader); + + while (sysprof_capture_reader_peek_type (reader, &type)) + { + SysprofAddressContext last_context = SYSPROF_ADDRESS_CONTEXT_NONE; + const SysprofCaptureSample *sample; + + if (type != SYSPROF_CAPTURE_FRAME_SAMPLE) + { + sysprof_capture_reader_skip (reader); + continue; + } + + sample = sysprof_capture_reader_read_sample (reader); + + for (guint i = 0; i < sample->n_addrs; i++) + { + g_autofree gchar *str = NULL; + SysprofCaptureAddress addr = sample->addrs[i]; + SysprofCaptureAddress begin, end; + SysprofAddressContext context; + GSequenceIter *iter; + const gchar *qstr; + Symbol sym; + GQuark tag = 0; + gpointer poff; + + if (sysprof_address_is_context_switch (addr, &context)) + { + last_context = context; + continue; + } + + if (!sysprof_elf_symbol_resolver_resolve_full ((SysprofElfSymbolResolver *)native, + sample->frame.time, + sample->frame.pid, + last_context, + addr, + &begin, + &end, + &str, + &tag)) + continue; + + g_assert (str != NULL); + + sym.pid = sample->frame.pid; + sym.begin = begin; + sym.end = end; + sym.offset = 0; + sym.tag_offset = 0; + + /* Lookup (but get us the next position on miss so we can insert + * without a second lookup for the same position. + */ + iter = g_sequence_search (symbols, &sym, symbol_compare, NULL); + + /* Skip if we found it */ + if (!g_sequence_iter_is_end (iter) && + symbol_compare (&sym, g_sequence_get (iter), NULL) == 0) + continue; + + /* Add the string if we haven't seen it */ + if (!g_hash_table_lookup_extended (seen, str, NULL, &poff)) + { + sym.offset = strings->len; + g_byte_array_append (strings, (guint8 *)str, strlen (str) + 1); + g_hash_table_insert (seen, g_steal_pointer (&str), GUINT_TO_POINTER (sym.offset)); + } + else + { + sym.offset = GPOINTER_TO_UINT (poff); + } + + /* And the tag */ + if (tag != 0 && + (qstr = g_quark_to_string (tag)) && + !g_hash_table_lookup_extended (seen, qstr, NULL, &poff)) + { + sym.tag_offset = strings->len; + g_byte_array_append (strings, (guint8 *)qstr, strlen (qstr) + 1); + g_hash_table_insert (seen, g_strdup (qstr), GUINT_TO_POINTER (sym.tag_offset)); + } + else + { + sym.tag_offset = GPOINTER_TO_UINT (poff); + } + + g_sequence_insert_before (iter, g_slice_dup (Symbol, &sym)); + } + } + + g_debug ("Writing supplemental %d symbols with %u bytes of strings", + g_sequence_get_length (symbols), + strings->len); + + base = (g_sequence_get_length (symbols) + 1) * sizeof (Symbol); + + for (GSequenceIter *iter = g_sequence_get_begin_iter (symbols); + !g_sequence_iter_is_end (iter); + iter = g_sequence_iter_next (iter)) + { + Symbol *sym = g_sequence_get (iter); + + sym->offset += base; + sym->tag_offset += base; + + write (fd, sym, sizeof *sym); + } + + /* Write tailing empty symbol */ + { + static const Symbol zero = {0}; + write (fd, &zero, sizeof zero); + } + + write (fd, strings->data, strings->len); + lseek (fd, 0, SEEK_SET); + + sysprof_capture_writer_add_file_fd (self->writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + "__symbols__", + fd); + + close (fd); +} + +static void +sysprof_symbols_source_start (SysprofSource *source) +{ + g_assert (SYSPROF_IS_SYMBOLS_SOURCE (source)); + + sysprof_source_emit_ready (source); +} + +static void +sysprof_symbols_source_stop (SysprofSource *source) +{ + g_assert (SYSPROF_IS_SYMBOLS_SOURCE (source)); + + sysprof_source_emit_finished (source); } static void @@ -89,4 +293,12 @@ source_iface_init (SysprofSourceInterface *iface) { iface->set_writer = sysprof_symbols_source_set_writer; iface->supplement = sysprof_symbols_source_supplement; + iface->start = sysprof_symbols_source_start; + iface->stop = sysprof_symbols_source_stop; +} + +SysprofSource * +sysprof_symbols_source_new (void) +{ + return g_object_new (SYSPROF_TYPE_SYMBOLS_SOURCE, NULL); }