diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build index 7c823021b29a56da504a49b4e55af2f2ce6709e1..6ad107166f458ef96e90ef0ddd0f7176ba0a6a6a 100644 --- a/src/libsysprof/meson.build +++ b/src/libsysprof/meson.build @@ -39,6 +39,7 @@ libsysprof_public_sources = [ 'sysprof-elf-symbolizer.c', 'sysprof-energy-usage.c', 'sysprof-instrument.c', + 'sysprof-jitdump-symbolizer.c', 'sysprof-jitmap-symbolizer.c', 'sysprof-kallsyms-symbolizer.c', 'sysprof-malloc-tracing.c', @@ -106,6 +107,7 @@ libsysprof_public_headers = [ 'sysprof-elf-symbolizer.h', 'sysprof-energy-usage.h', 'sysprof-instrument.h', + 'sysprof-jitdump-symbolizer.h', 'sysprof-jitmap-symbolizer.h', 'sysprof-kallsyms-symbolizer.h', 'sysprof-malloc-tracing.h', diff --git a/src/libsysprof/sysprof-document-loader.c b/src/libsysprof/sysprof-document-loader.c index 6026693907422242e32754d5874278329913f3b6..dcc0a97ce2cb694ed96cb67077df4088cc5c2dab 100644 --- a/src/libsysprof/sysprof-document-loader.c +++ b/src/libsysprof/sysprof-document-loader.c @@ -32,6 +32,7 @@ #include "sysprof-document-loader-private.h" #include "sysprof-document-private.h" #include "sysprof-elf-symbolizer.h" +#include "sysprof-jitdump-symbolizer.h" #include "sysprof-jitmap-symbolizer.h" #include "sysprof-kallsyms-symbolizer.h" #include "sysprof-multi-symbolizer.h" @@ -207,6 +208,7 @@ set_default_symbolizer (SysprofDocumentLoader *self) sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new ()); sysprof_multi_symbolizer_take (multi, sysprof_elf_symbolizer_new ()); sysprof_multi_symbolizer_take (multi, sysprof_jitmap_symbolizer_new ()); + sysprof_multi_symbolizer_take (multi, sysprof_jitdump_symbolizer_new ()); #if HAVE_DEBUGINFOD { diff --git a/src/libsysprof/sysprof-jitdump-symbolizer.c b/src/libsysprof/sysprof-jitdump-symbolizer.c new file mode 100644 index 0000000000000000000000000000000000000000..d748048496faf5697503128b908d3485370ed7d5 --- /dev/null +++ b/src/libsysprof/sysprof-jitdump-symbolizer.c @@ -0,0 +1,520 @@ +/* sysprof-jitdump-symbolizer.c + * + * Copyright 2025 Justin Michaud + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-jitdump-symbolizer.h" + +#include "timsort/gtktimsortprivate.h" +#include "sysprof-document-private.h" +#include "sysprof-document-process-private.h" +#include "sysprof-strings-private.h" +#include "sysprof-symbolizer-private.h" +#include "sysprof-symbol-private.h" + +typedef struct _Jitdump +{ + SysprofAddress address; + GRefString *name; + gsize code_size; +} Jitdump; + +struct _SysprofJitdumpSymbolizer +{ + SysprofSymbolizer parent_instance; + GArray *jitdumps; +}; + +struct _SysprofJitdumpSymbolizerClass +{ + SysprofSymbolizerClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofJitdumpSymbolizer, sysprof_jitdump_symbolizer, SYSPROF_TYPE_SYMBOLIZER) + +typedef struct +{ + SysprofDocument *document; +} Prepare; + +static void +prepare_free (gpointer data) +{ + Prepare *prepare = data; + + g_clear_object (&prepare->document); + g_free (prepare); +} + +static void +jitdump_clear (gpointer data) +{ + Jitdump *j = data; + g_clear_pointer (&j->name, g_ref_string_release); +} + +static int +compare_by_address (gconstpointer a, + gconstpointer b) +{ + const Jitdump *jitdump_a = a; + const Jitdump *jitdump_b = b; + + if (jitdump_a->address < jitdump_b->address) + return -1; + else if (jitdump_a->address > jitdump_b->address) + return 1; + else + return 0; +} + +static void +sysprof_jitdump_symbolizer_load_jitdump (SysprofJitdumpSymbolizer *self, + Prepare *prepare, + GDataInputStream *input, + GCancellable *cancellable, + GError **error) +{ + const guint32 JIT_CODE_LOAD = 0; + Jitdump map; + + g_assert (error != NULL); + g_assert (*error == NULL); + g_assert (G_IS_DATA_INPUT_STREAM (input)); + g_data_input_stream_set_byte_order (input, G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN); + + // https://raw.githubusercontent.com/torvalds/linux/master/tools/perf/Documentation/jitdump-specification.txt + // Each jitdump file starts with a fixed size header containing the following fields in order: + // * uint32_t magic : a magic number tagging the file type. The value is 4-byte long and represents the string "JiTD" in ASCII form. It written is as 0x4A695444. The reader will detect an endian mismatch when it reads 0x4454694a. + // * uint32_t version : a 4-byte value representing the format version. It is currently set to 1 + // * uint32_t total_size: size in bytes of file header + // * uint32_t elf_mach : ELF architecture encoding (ELF e_machine value as specified in /usr/include/elf.h) + // * uint32_t pad1 : padding. Reserved for future use + // * uint32_t pid : JIT runtime process identification (OS specific) + // * uint64_t timestamp : timestamp of when the file was created + // * uint64_t flags : a bitmask of flags + { + guint32 magic; + guint32 version; + guint32 total_size; + + magic = g_data_input_stream_read_uint32 (input, cancellable, error); + if (*error != NULL) + return; + + version = g_data_input_stream_read_uint32 (input, cancellable, error); + if (*error != NULL) + return; + + total_size = g_data_input_stream_read_uint32 (input, cancellable, error); + if (*error != NULL) + return; + + g_input_stream_skip (G_INPUT_STREAM (input), total_size - sizeof (magic) - sizeof (version) - sizeof (total_size), cancellable, error); + if (*error != NULL) + return; + + if (magic != 0x4A695444) + { + *error = g_error_new (G_IO_ERROR,G_IO_ERROR_INVALID_DATA, "Invalid magic number: %d (expect 0x4454694a for endianness mismatch)", magic); + return; + } + + if (version != 1) + { + *error = g_error_new (G_IO_ERROR,G_IO_ERROR_INVALID_DATA, "Invalid version"); + return; + } + } + + // The file header is immediately followed by records. Each record starts with a fixed size header describing the record that follows. + // The record header is specified in order as follows: + // * uint32_t id : a value identifying the record type (see below) + // * uint32_t total_size: the size in bytes of the record including the header. + // * uint64_t timestamp : a timestamp of when the record was created. + + while (g_buffered_input_stream_get_available (G_BUFFERED_INPUT_STREAM (input)) != 0) + { + guint32 id; + guint32 total_size; + G_GNUC_UNUSED guint64 timestamp; + const gsize fields_size_header = (2 * sizeof (uint32_t) + sizeof (uint64_t)); + const gsize string_terminator = sizeof (char); + + if (*error != NULL) + return; + + id = g_data_input_stream_read_uint32 (input, cancellable, error); + if (*error != NULL) + return; + + total_size = g_data_input_stream_read_uint32 (input, cancellable, error); + if (*error != NULL) + return; + + if (total_size < fields_size_header) + { + *error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid record size"); + return; + } + + timestamp = g_data_input_stream_read_uint64 (input, cancellable, error); + if (*error != NULL) + return; + + if (id == JIT_CODE_LOAD) + { + // The record has the following fields following the fixed-size record header in order: + // * uint32_t pid: OS process id of the runtime generating the jitted code + // * uint32_t tid: OS thread identification of the runtime thread generating the jitted code + // * uint64_t vma: virtual address of jitted code start + // * uint64_t code_addr: code start address for the jitted code. By default vma = code_addr + // * uint64_t code_size: size in bytes of the generated jitted code + // * uint64_t code_index: unique identifier for the jitted code (see below) + // * char[n]: function name in ASCII including the null termination + // * native code: raw byte encoding of the jitted code + G_GNUC_UNUSED guint32 pid; + G_GNUC_UNUSED guint32 tid; + guint64 vma; + guint64 code_addr; + guint64 code_size; + G_GNUC_UNUSED guint64 code_index; + + gsize name_len; + g_autofree gchar *name = NULL; + g_autofree guint8 *code = NULL; + gsize code_read_bytes = 0; + gsize name_read_bytes = 0; + const gsize fields_size_jit_code_load = 2 * sizeof (uint32_t) + 4 * sizeof (uint64_t); + + if (total_size < fields_size_jit_code_load + fields_size_header + string_terminator) + { + *error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid jit load record size"); + return; + } + + pid = g_data_input_stream_read_uint32 (input, cancellable, error); + if (*error != NULL) + return; + + tid = g_data_input_stream_read_uint32 (input, cancellable, error); + if (*error != NULL) + return; + + vma = g_data_input_stream_read_uint64 (input, cancellable, error); + if (*error != NULL) + return; + + code_addr = g_data_input_stream_read_uint64 (input, cancellable, error); + if (*error != NULL) + return; + + code_size = g_data_input_stream_read_uint64 (input, cancellable, error); + if (*error != NULL) + return; + + code_index = g_data_input_stream_read_uint64 (input, cancellable, error); + if (*error != NULL) + return; + + if (code_size >= UINT32_MAX || total_size < fields_size_jit_code_load + fields_size_header + string_terminator + code_size) + { + *error = g_error_new (G_IO_ERROR,G_IO_ERROR_INVALID_DATA, "Invalid jit record code size: %"G_GUINT64_FORMAT"", code_size); + return; + } + + name_len = total_size - code_size - fields_size_jit_code_load - fields_size_header - string_terminator; + name = g_malloc0(name_len + string_terminator); + code = g_malloc0(code_size); + g_input_stream_read_all (G_INPUT_STREAM (input), name, name_len + string_terminator, &name_read_bytes, cancellable, error); + if (*error != NULL) + return; + + if (name_read_bytes != name_len + string_terminator) + { + *error = g_error_new (G_IO_ERROR,G_IO_ERROR_INVALID_DATA, "Invalid jit record name. Read %" G_GSIZE_FORMAT " bytes, expected %" G_GSIZE_FORMAT, name_read_bytes, name_len + string_terminator); + return; + } + + g_input_stream_read_all (G_INPUT_STREAM (input), code, code_size, &code_read_bytes, cancellable, error); + if (*error != NULL) + return; + + if (code_read_bytes != code_size) + { + *error = g_error_new (G_IO_ERROR,G_IO_ERROR_INVALID_DATA, "Invalid jit record code. Read %" G_GSIZE_FORMAT " bytes, expected %"G_GUINT64_FORMAT"", code_read_bytes, code_size); + return; + } + + if (vma != code_addr) + { + *error = g_error_new (G_IO_ERROR,G_IO_ERROR_INVALID_DATA, "Unexpected vma: %"G_GUINT64_FORMAT" code_addr: %"G_GUINT64_FORMAT"", vma, code_addr); + return; + } + + map.name = _sysprof_document_ref_string (prepare->document, name); + map.address = code_addr; + map.code_size = code_size; + g_array_append_val (self->jitdumps, map); + } + else + { + // Skip this record. + gsize skip = total_size - (2 * sizeof (uint32_t) + sizeof (uint64_t)); + gsize skipped = g_input_stream_skip (G_INPUT_STREAM (input), skip, cancellable, error); + g_assert_not_reached (); + if (skipped != skip) + { + *error = g_error_new (G_IO_ERROR,G_IO_ERROR_INVALID_DATA, "Failed to skip record: %zd %zd", skip, skipped); + return; + } + + if (*error != NULL) + return; + } + g_buffered_input_stream_fill (G_BUFFERED_INPUT_STREAM (input), -1, cancellable, error); + if (*error != NULL) + return; + } + + if (*error != NULL) + return; +} + +static void +sysprof_jitdump_symbolizer_prepare_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + SysprofJitdumpSymbolizer *self = source_object; + Prepare *prepare = task_data; + g_autoptr(GListModel) processes = NULL; + guint n_processes; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_JITDUMP_SYMBOLIZER (self)); + g_assert (prepare != NULL); + g_assert (SYSPROF_IS_DOCUMENT (prepare->document)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + processes = sysprof_document_list_processes (prepare->document); + n_processes = g_list_model_get_n_items (processes); + + g_warning ("JITDUMP PREPARE WORKER"); + + for (guint i = 0; i < n_processes; i++) + { + g_autoptr(SysprofDocumentProcess) process = g_list_model_get_item (processes, i); + SysprofProcessInfo* process_info = _sysprof_document_process_get_info (process); + SysprofMountNamespace* mount_namespace = process_info->mount_namespace; + g_autoptr(GListModel) memory_maps = sysprof_document_process_list_memory_maps (process); + guint n_memory_maps = g_list_model_get_n_items (memory_maps); + + for (guint j = 0; j < n_memory_maps; j++) + { + g_autoptr(SysprofDocumentMmap) memory_map = g_list_model_get_item (memory_maps, j); + const char *file_path_original = sysprof_document_mmap_get_file (memory_map); + g_autoptr(GFile) file_handle = NULL; + g_autofree char *file_basename = NULL; + g_autoptr(GFileInputStream) stream = NULL; + g_autoptr(GError) error = NULL; + g_autofree char** translated_file_paths = NULL; + + if (file_path_original == NULL) + continue; + + file_handle = g_file_new_for_path (file_path_original); + file_basename = g_file_get_basename (file_handle); + + if (file_basename == NULL) + continue; + + if (!g_str_has_prefix (file_basename, "jit-")) + continue; + + if (!g_str_has_suffix (file_basename, ".dump")) + continue; + + stream = g_file_read (file_handle, cancellable, &error); + g_clear_error (&error); + if (mount_namespace != NULL && stream == NULL) + { + translated_file_paths = sysprof_mount_namespace_translate (mount_namespace, file_path_original); + for (gint k = 0; translated_file_paths && translated_file_paths[k]; k++) + { + file_handle = g_file_new_for_path (translated_file_paths[k]); + stream = g_file_read (file_handle, cancellable, &error); + g_clear_error (&error); + if (stream != NULL) + break; + } + } + + if (stream == NULL) + { + g_warning ("Failed to open Perf JITDump file %s", file_path_original); + continue; + } + + { + g_autoptr(GDataInputStream) data_stream = g_data_input_stream_new (G_INPUT_STREAM (stream)); + sysprof_jitdump_symbolizer_load_jitdump (self, prepare, data_stream, cancellable, &error); + } + + if (error != NULL) + { + g_warning ("Failed to load Perf JITDump file %s: %s", file_path_original, error->message); + g_clear_error (&error); + continue; + } + } + } + + gtk_tim_sort (self->jitdumps->data, + self->jitdumps->len, + sizeof (Jitdump), + (GCompareDataFunc)compare_by_address, + NULL); + + for (int i = 0; i < self->jitdumps->len && i < 50; ++i) + { + Jitdump *jitdump = &g_array_index (self->jitdumps, Jitdump, i); + g_warning ("Got dump: %s with address %"G_GUINT64_FORMAT, jitdump->name, jitdump->address); + } + + g_task_return_boolean (task, TRUE); +} + +static void +sysprof_jitdump_symbolizer_prepare_async (SysprofSymbolizer *symbolizer, + SysprofDocument *document, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SysprofJitdumpSymbolizer *self = (SysprofJitdumpSymbolizer *)symbolizer; + g_autoptr(GTask) task = NULL; + Prepare *prepare; + + g_assert (SYSPROF_IS_JITDUMP_SYMBOLIZER (self)); + g_assert (SYSPROF_IS_DOCUMENT (document)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + prepare = g_new0 (Prepare, 1); + prepare->document = g_object_ref (document); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_jitdump_symbolizer_prepare_async); + g_task_set_task_data (task, prepare, prepare_free); + g_task_run_in_thread (task, sysprof_jitdump_symbolizer_prepare_worker); +} + +static gboolean +sysprof_jitdump_symbolizer_prepare_finish (SysprofSymbolizer *symbolizer, + GAsyncResult *result, + GError **error) +{ + g_assert (SYSPROF_IS_JITDUMP_SYMBOLIZER (symbolizer)); + g_assert (G_IS_TASK (result)); + g_assert (g_task_is_valid (result, symbolizer)); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static SysprofSymbol * +sysprof_jitdump_symbolizer_symbolize (SysprofSymbolizer *symbolizer, + SysprofStrings *strings, + const SysprofProcessInfo *process_info, + SysprofAddressContext context, + SysprofAddress address) +{ + SysprofJitdumpSymbolizer *self = (SysprofJitdumpSymbolizer *)symbolizer; + // const Jitdump key = { address, NULL }; + const Jitdump *match = NULL; + + if (context != SYSPROF_ADDRESS_CONTEXT_NONE && + context != SYSPROF_ADDRESS_CONTEXT_USER) + return NULL; + + // TODO bsearch + for (int i = 0; i < self->jitdumps->len; ++i) + { + const Jitdump *jitdump = &g_array_index(self->jitdumps, Jitdump, i); + if (jitdump->address <= address && jitdump->address + jitdump->code_size > address) + { + match = jitdump; + break; + } + } + // match = bsearch (&key, + // self->jitdumps->data, + // self->jitdumps->len, + // sizeof (Jitdump), + // compare_by_address); + + if (match == NULL) + return NULL; + + return _sysprof_symbol_new (g_ref_string_acquire (match->name), + NULL, + NULL, + match->address, + match->address + match->code_size, + SYSPROF_SYMBOL_KIND_USER); +} + +static void +sysprof_jitdump_symbolizer_finalize (GObject *object) +{ + SysprofJitdumpSymbolizer *self = (SysprofJitdumpSymbolizer *)object; + + g_clear_pointer (&self->jitdumps, g_array_unref); + + G_OBJECT_CLASS (sysprof_jitdump_symbolizer_parent_class)->finalize (object); +} + +static void +sysprof_jitdump_symbolizer_class_init (SysprofJitdumpSymbolizerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass); + + object_class->finalize = sysprof_jitdump_symbolizer_finalize; + + symbolizer_class->prepare_async = sysprof_jitdump_symbolizer_prepare_async; + symbolizer_class->prepare_finish = sysprof_jitdump_symbolizer_prepare_finish; + symbolizer_class->symbolize = sysprof_jitdump_symbolizer_symbolize; +} + +static void +sysprof_jitdump_symbolizer_init (SysprofJitdumpSymbolizer *self) +{ + self->jitdumps = g_array_new (FALSE, FALSE, sizeof (Jitdump)); + g_array_set_clear_func (self->jitdumps, jitdump_clear); +} + +SysprofSymbolizer * +sysprof_jitdump_symbolizer_new (void) +{ + return g_object_new (SYSPROF_TYPE_JITDUMP_SYMBOLIZER, NULL); +} diff --git a/src/libsysprof/sysprof-jitdump-symbolizer.h b/src/libsysprof/sysprof-jitdump-symbolizer.h new file mode 100644 index 0000000000000000000000000000000000000000..584cd7c2bc2a022411a410c028a785836c652625 --- /dev/null +++ b/src/libsysprof/sysprof-jitdump-symbolizer.h @@ -0,0 +1,51 @@ +/* sysprof-jitdump-symbolizer.h + * + * Copyright 2025 Justin Michaud + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-symbolizer.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_JITDUMP_SYMBOLIZER (sysprof_jitdump_symbolizer_get_type()) +#define SYSPROF_IS_JITDUMP_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_JITDUMP_SYMBOLIZER) +#define SYSPROF_JITDUMP_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_JITDUMP_SYMBOLIZER, SysprofJitdumpSymbolizer) +#define SYSPROF_JITDUMP_SYMBOLIZER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_JITDUMP_SYMBOLIZER, SysprofJitdumpSymbolizerClass) + +typedef struct _SysprofJitdumpSymbolizer SysprofJitdumpSymbolizer; +typedef struct _SysprofJitdumpSymbolizerClass SysprofJitdumpSymbolizerClass; + +/** + * SysprofJitdumpSymbolizer: + * + * A symbolizer that reads from a Perf JITDump (https://raw.githubusercontent.com/torvalds/linux/master/tools/perf/Documentation/jitdump-specification.txt). + * + * There is considerable overlap between this format and the JITMap symbolizer used by gjs, however this format + * does not require writing a custom profiler. The target application (for example, JavaScriptCore) mmaps a file + * named JIT_*.dump, and that stores all of the information we need to symbolize the code. + */ +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_jitdump_symbolizer_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_49 +SysprofSymbolizer *sysprof_jitdump_symbolizer_new (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofJitdumpSymbolizer, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof/sysprof-symbols-bundle.c b/src/libsysprof/sysprof-symbols-bundle.c index 4e04d2ed1dcccabbabbc9b9b0fa41495894a8fb4..b91607aedff4bcf2b8ffb90be121ee888b300e6a 100644 --- a/src/libsysprof/sysprof-symbols-bundle.c +++ b/src/libsysprof/sysprof-symbols-bundle.c @@ -107,6 +107,8 @@ sysprof_symbols_bundle_augment_fiber (gpointer user_data) sysprof_multi_symbolizer_take (multi, g_steal_pointer (&debuginfod)); } + sysprof_multi_symbolizer_take (multi, sysprof_jitdump_symbolizer_new ()); + sysprof_document_loader_set_symbolizer (loader, SYSPROF_SYMBOLIZER (multi)); if (!(document = dex_await_object (_sysprof_document_loader_load (loader), &error))) diff --git a/src/libsysprof/sysprof.h b/src/libsysprof/sysprof.h index 514e332b30ad89be4382729e1c5278b0d689ae2a..52aa758cab9209286189ba256f881793bb137e67 100644 --- a/src/libsysprof/sysprof.h +++ b/src/libsysprof/sysprof.h @@ -64,6 +64,7 @@ G_BEGIN_DECLS # include "sysprof-energy-usage.h" # include "sysprof-enums.h" # include "sysprof-instrument.h" +# include "sysprof-jitdump-symbolizer.h" # include "sysprof-jitmap-symbolizer.h" # include "sysprof-kallsyms-symbolizer.h" # include "sysprof-malloc-tracing.h" diff --git a/src/sysprof/sysprof-recording-template.c b/src/sysprof/sysprof-recording-template.c index be5173463a91dfd64263c24fcc1c2c3cb553ccfa..309bb2bd2719155dbec9f1eabf4eaa5d4c2ef2c1 100644 --- a/src/sysprof/sysprof-recording-template.c +++ b/src/sysprof/sysprof-recording-template.c @@ -810,6 +810,7 @@ sysprof_recording_template_create_loader (SysprofRecordingTemplate *self, sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new ()); sysprof_multi_symbolizer_take (multi, SYSPROF_SYMBOLIZER (g_steal_pointer (&elf))); sysprof_multi_symbolizer_take (multi, sysprof_jitmap_symbolizer_new ()); + sysprof_multi_symbolizer_take (multi, sysprof_jitdump_symbolizer_new ()); sysprof_document_loader_set_symbolizer (loader, SYSPROF_SYMBOLIZER (multi));