libsysprof: join libsysprof-analyze and libsysprof-profile

This brings together the two libraries back into one now that the whole
design is pretty well sorted out. They depend on roughly the same libraries
anyway and it's way easier of the single library can both read and write
the capture files (along with bringing in libsysprof-capture symbols in
a single place).
This commit is contained in:
Christian Hergert
2023-07-19 17:40:41 -07:00
parent a990fc6b3c
commit dbb7833cbf
217 changed files with 187 additions and 316 deletions

View File

@ -0,0 +1,41 @@
/* mapped-ring-buffer-source-private.h
*
* Copyright 2020 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
*/
#pragma once
#include <glib.h>
#include "mapped-ring-buffer.h"
G_BEGIN_DECLS
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MappedRingBuffer, mapped_ring_buffer_unref)
G_GNUC_INTERNAL
guint mapped_ring_buffer_create_source (MappedRingBuffer *self,
MappedRingBufferCallback callback,
gpointer user_data);
G_GNUC_INTERNAL
guint mapped_ring_buffer_create_source_full (MappedRingBuffer *self,
MappedRingBufferCallback callback,
gpointer user_data,
GDestroyNotify destroy);
G_END_DECLS

View File

@ -0,0 +1,119 @@
/* mapped-ring-buffer-source.c
*
* Copyright 2020 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 <glib.h>
#include "mapped-ring-buffer-source-private.h"
typedef struct _MappedRingSource
{
GSource source;
MappedRingBuffer *buffer;
} MappedRingSource;
static gboolean
mapped_ring_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
MappedRingSource *real_source = (MappedRingSource *)source;
g_assert (source != NULL);
return mapped_ring_buffer_drain (real_source->buffer,
(MappedRingBufferCallback)callback,
user_data);
}
static void
mapped_ring_source_finalize (GSource *source)
{
MappedRingSource *real_source = (MappedRingSource *)source;
if (real_source != NULL)
g_clear_pointer (&real_source->buffer, mapped_ring_buffer_unref);
}
static gboolean
mapped_ring_source_check (GSource *source)
{
MappedRingSource *real_source = (MappedRingSource *)source;
g_assert (real_source != NULL);
g_assert (real_source->buffer != NULL);
return !mapped_ring_buffer_is_empty (real_source->buffer);
}
static gboolean
mapped_ring_source_prepare (GSource *source,
gint *timeout_)
{
MappedRingSource *real_source = (MappedRingSource *)source;
g_assert (real_source != NULL);
g_assert (real_source->buffer != NULL);
if (!mapped_ring_buffer_is_empty (real_source->buffer))
return TRUE;
*timeout_ = 5;
return FALSE;
}
static GSourceFuncs mapped_ring_source_funcs = {
.prepare = mapped_ring_source_prepare,
.check = mapped_ring_source_check,
.dispatch = mapped_ring_source_dispatch,
.finalize = mapped_ring_source_finalize,
};
guint
mapped_ring_buffer_create_source_full (MappedRingBuffer *self,
MappedRingBufferCallback source_func,
gpointer user_data,
GDestroyNotify destroy)
{
MappedRingSource *source;
guint ret;
g_return_val_if_fail (self != NULL, 0);
g_return_val_if_fail (source_func != NULL, 0);
source = (MappedRingSource *)g_source_new (&mapped_ring_source_funcs, sizeof (MappedRingSource));
source->buffer = mapped_ring_buffer_ref (self);
g_source_set_callback ((GSource *)source, (GSourceFunc)source_func, user_data, destroy);
g_source_set_name ((GSource *)source, "MappedRingSource");
ret = g_source_attach ((GSource *)source, g_main_context_default ());
g_source_unref ((GSource *)source);
return ret;
}
guint
mapped_ring_buffer_create_source (MappedRingBuffer *self,
MappedRingBufferCallback source_func,
gpointer user_data)
{
return mapped_ring_buffer_create_source_full (self, source_func, user_data, NULL);
}

208
src/libsysprof/meson.build Normal file
View File

@ -0,0 +1,208 @@
libsysprof_public_sources = [
'sysprof-battery-charge.c',
'sysprof-bundled-symbolizer.c',
'sysprof-callgraph-frame.c',
'sysprof-callgraph-symbol.c',
'sysprof-callgraph.c',
'sysprof-cpu-info.c',
'sysprof-cpu-usage.c',
'sysprof-diagnostic.c',
'sysprof-disk-usage.c',
'sysprof-document-allocation.c',
'sysprof-document-counter-value.c',
'sysprof-document-counter.c',
'sysprof-document-ctrdef.c',
'sysprof-document-ctrset.c',
'sysprof-document-exit.c',
'sysprof-document-file-chunk.c',
'sysprof-document-file.c',
'sysprof-document-fork.c',
'sysprof-document-frame.c',
'sysprof-document-jitmap.c',
'sysprof-document-loader.c',
'sysprof-document-log.c',
'sysprof-document-mark.c',
'sysprof-document-metadata.c',
'sysprof-document-mmap.c',
'sysprof-document-overlay.c',
'sysprof-document-process.c',
'sysprof-document-sample.c',
'sysprof-document-traceable.c',
'sysprof-document.c',
'sysprof-elf-symbolizer.c',
'sysprof-energy-usage.c',
'sysprof-instrument.c',
'sysprof-jitmap-symbolizer.c',
'sysprof-kallsyms-symbolizer.c',
'sysprof-malloc-tracing.c',
'sysprof-mark-catalog.c',
'sysprof-memory-usage.c',
'sysprof-multi-symbolizer.c',
'sysprof-network-usage.c',
'sysprof-no-symbolizer.c',
'sysprof-power-profile.c',
'sysprof-profiler.c',
'sysprof-proxied-instrument.c',
'sysprof-recording.c',
'sysprof-sampler.c',
'sysprof-spawnable.c',
'sysprof-symbol.c',
'sysprof-symbolizer.c',
'sysprof-symbols-bundle.c',
'sysprof-system-logs.c',
'sysprof-thread-info.c',
'sysprof-time-span.c',
'sysprof-tracer.c',
]
libsysprof_public_headers = [
'sysprof.h',
'sysprof-battery-charge.h',
'sysprof-bundled-symbolizer.h',
'sysprof-callgraph-frame.h',
'sysprof-callgraph-symbol.h',
'sysprof-callgraph.h',
'sysprof-cpu-info.h',
'sysprof-cpu-usage.h',
'sysprof-diagnostic.h',
'sysprof-disk-usage.h',
'sysprof-document-allocation.h',
'sysprof-document-counter-value.h',
'sysprof-document-counter.h',
'sysprof-document-ctrdef.h',
'sysprof-document-ctrset.h',
'sysprof-document-exit.h',
'sysprof-document-file-chunk.h',
'sysprof-document-file.h',
'sysprof-document-fork.h',
'sysprof-document-frame.h',
'sysprof-document-jitmap.h',
'sysprof-document-loader.h',
'sysprof-document-log.h',
'sysprof-document-mark.h',
'sysprof-document-metadata.h',
'sysprof-document-mmap.h',
'sysprof-document-overlay.h',
'sysprof-document-process.h',
'sysprof-document-sample.h',
'sysprof-document-traceable.h',
'sysprof-document.h',
'sysprof-elf-symbolizer.h',
'sysprof-energy-usage.h',
'sysprof-instrument.h',
'sysprof-jitmap-symbolizer.h',
'sysprof-kallsyms-symbolizer.h',
'sysprof-malloc-tracing.h',
'sysprof-mark-catalog.h',
'sysprof-memory-usage.h',
'sysprof-mount.h',
'sysprof-multi-symbolizer.h',
'sysprof-network-usage.h',
'sysprof-no-symbolizer.h',
'sysprof-power-profile.h',
'sysprof-profiler.h',
'sysprof-proxied-instrument.h',
'sysprof-recording.h',
'sysprof-sampler.h',
'sysprof-spawnable.h',
'sysprof-symbol.h',
'sysprof-symbolizer.h',
'sysprof-symbols-bundle.h',
'sysprof-system-logs.h',
'sysprof-thread-info.h',
'sysprof-time-span.h',
'sysprof-tracer.h',
]
libsysprof_private_sources = [
'mapped-ring-buffer-source.c',
'sysprof-address-layout.c',
'sysprof-controlfd-instrument.c',
'sysprof-descendants-model.c',
'sysprof-document-bitset-index.c',
'sysprof-document-symbols.c',
'sysprof-elf-loader.c',
'sysprof-elf.c',
'sysprof-journald-source.c',
'sysprof-maps-parser.c',
'sysprof-mount-device.c',
'sysprof-mount-namespace.c',
'sysprof-mount.c',
'sysprof-perf-event-stream.c',
'sysprof-podman.c',
'sysprof-process-info.c',
'sysprof-strings.c',
'sysprof-symbol-cache.c',
]
if polkit_dep.found()
libsysprof_private_sources += ['sysprof-polkit.c']
endif
if host_machine.system() == 'linux'
libsysprof_private_sources += ['sysprof-linux-instrument.c']
endif
libsysprof_deps = [
dependency('gio-2.0', version: glib_req_version),
dependency('gio-unix-2.0',
version: glib_req_version,
required: host_machine.system() != 'windows'),
dependency('libdex-1', version: dex_req_version),
dependency('json-glib-1.0'),
libsystemd_dep,
polkit_dep,
libeggbitset_static_dep,
libelfparser_static_dep,
liblinereader_static_dep,
libsysprof_capture_dep,
]
libsysprof_static = static_library(
'sysprof-analyze-@0@'.format(soname_major_version),
libsysprof_public_sources +
libsysprof_private_sources,
include_directories: [include_directories('.'),
libsysprof_capture_include_dirs],
dependencies: libsysprof_deps,
gnu_symbol_visibility: 'hidden',
)
libsysprof_static_dep = declare_dependency(
link_with: libsysprof_static,
dependencies: libsysprof_deps,
include_directories: [include_directories('.'),
libsysprof_capture_include_dirs],
)
libsysprof = library('sysprof-analyze-@0@'.format(soname_major_version),
dependencies: [libsysprof_static_dep],
gnu_symbol_visibility: 'hidden',
version: '@0@.0.0'.format(soname_major_version),
darwin_versions: '@0@.0'.format(soname_major_version),
install: get_option('libsysprof'),
)
libsysprof_dep = declare_dependency(
link_with: libsysprof,
dependencies: libsysprof_deps,
include_directories: [include_directories('.'), libsysprof_capture_include_dirs],
)
meson.override_dependency('sysprof-analyze-@0@'.format(soname_major_version), libsysprof_dep)
pkgconfig.generate(libsysprof,
subdirs: [sysprof_header_subdir],
description: 'A library for recording and analyzing system performance',
install_dir: join_paths(get_option('libdir'), 'pkgconfig'),
requires: ['gio-2.0'],
variables: ['datadir=' + datadir_for_pc_file],
)
install_headers(libsysprof_public_headers, subdir: sysprof_header_subdir)
if get_option('tests')
subdir('tests')
endif

View File

@ -0,0 +1,39 @@
/* sysprof-address-layout.h
*
* Copyright 2023 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
*/
#pragma once
#include <glib-object.h>
#include "sysprof-document-mmap.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_ADDRESS_LAYOUT (sysprof_address_layout_get_type())
G_DECLARE_FINAL_TYPE (SysprofAddressLayout, sysprof_address_layout, SYSPROF, ADDRESS_LAYOUT, GObject)
SysprofAddressLayout *sysprof_address_layout_new (void);
void sysprof_address_layout_take (SysprofAddressLayout *self,
SysprofDocumentMmap *map);
SysprofDocumentMmap *sysprof_address_layout_lookup (SysprofAddressLayout *self,
SysprofAddress address);
G_END_DECLS

View File

@ -0,0 +1,227 @@
/* sysprof-address-layout.c
*
* Copyright 2023 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 <gio/gio.h>
#include <eggbitset.h>
#include "sysprof-address-layout-private.h"
struct _SysprofAddressLayout
{
GObject parent_instance;
GPtrArray *mmaps;
guint mmaps_dirty : 1;
};
static guint
sysprof_address_layout_get_n_items (GListModel *model)
{
return SYSPROF_ADDRESS_LAYOUT (model)->mmaps->len;
}
static GType
sysprof_address_layout_get_item_type (GListModel *model)
{
return SYSPROF_TYPE_DOCUMENT_MMAP;
}
static gpointer
sysprof_address_layout_get_item (GListModel *model,
guint position)
{
SysprofAddressLayout *self = SYSPROF_ADDRESS_LAYOUT (model);
if (position >= self->mmaps->len)
return NULL;
return g_object_ref (g_ptr_array_index (self->mmaps, position));
}
static void
list_model_iface_init (GListModelInterface *iface)
{
iface->get_n_items = sysprof_address_layout_get_n_items;
iface->get_item = sysprof_address_layout_get_item;
iface->get_item_type = sysprof_address_layout_get_item_type;
}
G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofAddressLayout, sysprof_address_layout, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
static void
sysprof_address_layout_finalize (GObject *object)
{
SysprofAddressLayout *self = (SysprofAddressLayout *)object;
g_clear_pointer (&self->mmaps, g_ptr_array_unref);
G_OBJECT_CLASS (sysprof_address_layout_parent_class)->finalize (object);
}
static void
sysprof_address_layout_class_init (SysprofAddressLayoutClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_address_layout_finalize;
}
static void
sysprof_address_layout_init (SysprofAddressLayout *self)
{
self->mmaps = g_ptr_array_new_with_free_func (g_object_unref);
}
SysprofAddressLayout *
sysprof_address_layout_new (void)
{
return g_object_new (SYSPROF_TYPE_ADDRESS_LAYOUT, NULL);
}
void
sysprof_address_layout_take (SysprofAddressLayout *self,
SysprofDocumentMmap *map)
{
g_return_if_fail (SYSPROF_IS_ADDRESS_LAYOUT (self));
g_return_if_fail (SYSPROF_IS_DOCUMENT_MMAP (map));
g_ptr_array_add (self->mmaps, map);
self->mmaps_dirty = TRUE;
}
static int
compare_mmaps (gconstpointer a,
gconstpointer b)
{
SysprofDocumentMmap *mmap_a = *(SysprofDocumentMmap * const *)a;
SysprofDocumentMmap *mmap_b = *(SysprofDocumentMmap * const *)b;
guint64 begin_a = sysprof_document_mmap_get_start_address (mmap_a);
guint64 begin_b = sysprof_document_mmap_get_start_address (mmap_b);
guint64 end_a = sysprof_document_mmap_get_end_address (mmap_a);
guint64 end_b = sysprof_document_mmap_get_end_address (mmap_b);
if (begin_a < begin_b)
return -1;
if (begin_a > begin_b)
return 1;
if (end_a < end_b)
return -1;
if (end_a > end_b)
return 1;
return 0;
}
static gboolean
mmaps_overlap (SysprofDocumentMmap *a,
SysprofDocumentMmap *b)
{
if ((sysprof_document_mmap_get_start_address (a) <= sysprof_document_mmap_get_start_address (b)) &&
(sysprof_document_mmap_get_end_address (a) > sysprof_document_mmap_get_start_address (b)))
return TRUE;
return FALSE;
}
static EggBitset *
find_duplicates (GPtrArray *sorted)
{
EggBitset *bitset = egg_bitset_new_empty ();
if (sorted->len == 0)
return bitset;
for (guint i = 0; i < sorted->len-1; i++)
{
SysprofDocumentMmap *map = g_ptr_array_index (sorted, i);
SysprofDocumentMmap *next = g_ptr_array_index (sorted, i+1);
/* Take the second one if they overlap, which is generally the large of
* the mmaps. That can happen when something like [stack] is resized into
* a larger mmap. It's useful to remove duplicates so we get more
* predictable bsearch results.
*/
if (mmaps_overlap (map, next))
egg_bitset_add (bitset, i);
}
return bitset;
}
static int
find_by_address (gconstpointer a,
gconstpointer b)
{
const SysprofAddress *key = a;
SysprofDocumentMmap *map = *(SysprofDocumentMmap * const *)b;
if (*key < sysprof_document_mmap_get_start_address (map))
return -1;
if (*key >= sysprof_document_mmap_get_end_address (map))
return 1;
return 0;
}
SysprofDocumentMmap *
sysprof_address_layout_lookup (SysprofAddressLayout *self,
SysprofAddress address)
{
SysprofDocumentMmap **ret;
g_return_val_if_fail (SYSPROF_IS_ADDRESS_LAYOUT (self), NULL);
if (self->mmaps_dirty)
{
g_autoptr(EggBitset) dups = NULL;
EggBitsetIter iter;
guint old_len = self->mmaps->len;
guint i;
self->mmaps_dirty = FALSE;
g_ptr_array_sort (self->mmaps, compare_mmaps);
dups = find_duplicates (self->mmaps);
if (egg_bitset_iter_init_last (&iter, dups, &i))
{
do
g_ptr_array_remove_index (self->mmaps, i);
while (egg_bitset_iter_previous (&iter, &i));
}
g_list_model_items_changed (G_LIST_MODEL (self), 0, old_len, self->mmaps->len);
}
ret = bsearch (&address,
self->mmaps->pdata,
self->mmaps->len,
sizeof (gpointer),
find_by_address);
return ret ? *ret : NULL;
}

View File

@ -0,0 +1,306 @@
/* sysprof-battery-charge.c
*
* Copyright 2023 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 <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <glib-unix.h>
#include <glib/gstdio.h>
#include "line-reader-private.h"
#include "sysprof-battery-charge.h"
#include "sysprof-instrument-private.h"
#include "sysprof-recording-private.h"
#define SYS_CLASS_POWER_SUPPLY "/sys/class/power_supply/"
struct _SysprofBatteryCharge
{
SysprofInstrument parent_instance;
};
struct _SysprofBatteryChargeClass
{
SysprofInstrumentClass parent_class;
};
G_DEFINE_FINAL_TYPE (SysprofBatteryCharge, sysprof_battery_charge, SYSPROF_TYPE_INSTRUMENT)
typedef struct _Record
{
SysprofRecording *recording;
DexFuture *cancellable;
} Record;
static void
record_free (gpointer data)
{
Record *record = data;
g_clear_object (&record->recording);
dex_clear (&record->cancellable);
g_free (record);
}
static void
clear_fd (gpointer ptr)
{
int *fdptr = ptr;
g_clear_fd (fdptr, NULL);
}
static char **
collect_battery_names (void)
{
GPtrArray *ar = g_ptr_array_new ();
g_autoptr(GDir) dir = NULL;
if ((dir = g_dir_open (SYS_CLASS_POWER_SUPPLY, 0, NULL)))
{
const char *name;
while ((name = g_dir_read_name (dir)))
{
if (strcmp (name, "AC") == 0)
continue;
g_ptr_array_add (ar, g_strdup (name));
}
}
g_ptr_array_add (ar, NULL);
return (char **)g_ptr_array_free (ar, FALSE);
}
typedef struct _ReadBuffer
{
char buf[32];
} ReadBuffer;
static DexFuture *
sysprof_battery_charge_record_fiber (gpointer user_data)
{
const int invalid_fd = -1;
g_autofree guint *ids = NULL;
g_autofree SysprofCaptureCounterValue *values = NULL;
g_autofree SysprofCaptureCounter *counters = NULL;
g_autofree ReadBuffer *bufs = NULL;
SysprofCaptureWriter *writer;
Record *record = user_data;
g_autoptr(GArray) charge_fds = NULL;
g_auto(GStrv) names = NULL;
guint n_names;
guint n_counters = 1;
g_assert (record != NULL);
g_assert (SYSPROF_IS_RECORDING (record->recording));
g_assert (DEX_IS_FUTURE (record->cancellable));
writer = _sysprof_recording_writer (record->recording);
names = collect_battery_names ();
n_names = g_strv_length (names);
/* Use some stack space for our counters and values. */
ids = g_new0 (guint, n_names + 1);
counters = g_new0 (SysprofCaptureCounter, n_names + 1);
values = g_new0 (SysprofCaptureCounterValue, n_names + 1);
bufs = g_new0 (ReadBuffer, n_names + 1);
/* Setup the combined counter which is the total charge of all of
* the batteries we discover on the system.
*/
counters[0].id = ids[0] = sysprof_capture_writer_request_counter (writer, 1);
g_strlcpy (counters[0].category, "Battery Charge", sizeof counters[0].category);
g_strlcpy (counters[0].name, "Combined", sizeof counters[0].name);
g_snprintf (counters[0].description, sizeof counters[0].description, "Combined Battery Charge (µAh)");
counters[0].type = SYSPROF_CAPTURE_COUNTER_INT64;
counters[0].value.v64 = 0;
/* Create array to store open FDs to the charge file. We'll do a
* positioned read at 0 on these so we get new data on each subsequent
* read via AIO. Set the first FD to invalid since that will map to the
* position of the combined-counter.
*/
charge_fds = g_array_new (FALSE, FALSE, sizeof (int));
g_array_set_clear_func (charge_fds, clear_fd);
g_array_append_val (charge_fds, invalid_fd);
for (guint i = 0; names[i]; i++)
{
SysprofCaptureCounter *counter = &counters[n_counters];
const char *name = names[i];
g_autofree char *charge_path = g_build_filename (SYS_CLASS_POWER_SUPPLY, name, "charge_now", NULL);
g_autofree char *model_name_path = g_build_filename (SYS_CLASS_POWER_SUPPLY, name, "model_name", NULL);
g_autofree char *type_path = g_build_filename (SYS_CLASS_POWER_SUPPLY, name, "type", NULL);
g_autofree char *model_name_data = NULL;
g_autofree char *type_data = NULL;
g_autofd int charge_fd = -1;
if (!g_file_get_contents (type_path, &type_data, NULL, NULL) ||
!g_str_has_prefix (type_data, "Battery") ||
-1 == (charge_fd = open (charge_path, O_RDONLY|O_CLOEXEC)))
continue;
counter->id = ids[n_counters] = sysprof_capture_writer_request_counter (writer, 1);
counter->type = SYSPROF_CAPTURE_COUNTER_INT64;
g_strlcpy (counter->category, "Battery Charge", sizeof counter->description);
if (g_file_get_contents (model_name_path, &model_name_data, NULL, NULL))
g_strlcpy (counter->name, g_strstrip (model_name_data), sizeof counter->name);
else
g_strlcpy (counter->name, name, sizeof counter->name);
g_snprintf (counter->description, sizeof counter->description, "%s (µAh)", counter->name);
counter->value.v64 = 0;
g_array_append_val (charge_fds, charge_fd);
charge_fd = -1;
n_counters++;
}
/* If we only have the combined counter, then just short-circuit and
* don't record any counters. Otherwise the battery charge row might
* show up in UI which we would want to omit.
*/
if (n_counters == 1)
return dex_future_new_for_boolean (TRUE);
sysprof_capture_writer_define_counters (writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
counters,
n_counters);
/* Main recording loop */
for (;;)
{
g_autoptr(GPtrArray) futures = g_ptr_array_new_with_free_func (dex_unref);
/* Read the contents of all the charge buffers in a single
* submission to the AIO layer.
*/
g_ptr_array_add (futures, dex_future_new_for_boolean (TRUE));
for (guint i = 1; i < charge_fds->len; i++)
{
int charge_fd = g_array_index (charge_fds, int, i);
g_ptr_array_add (futures,
dex_aio_read (NULL,
charge_fd,
&bufs[i].buf,
sizeof bufs[i].buf-1,
0));
}
/* Now wait until all the reads complete */
if (futures->len > 0)
dex_await (dex_future_anyv ((DexFuture **)futures->pdata, futures->len), NULL);
/* Parse the current charge values */
values[0].v64 = 0;
for (guint i = 1; i < charge_fds->len; i++)
{
gssize len = dex_await_int64 (dex_ref (g_ptr_array_index (futures, i)), NULL);
guint64 v64;
if (len <= 0)
continue;
errno = 0;
bufs[i].buf[len] = 0;
v64 = g_ascii_strtoull (bufs[i].buf, NULL, 10);
if (v64 == G_MAXUINT64 || errno != 0)
continue;
values[i].v64 = v64;
values[0].v64 += v64;
}
/* Deliver new values to capture */
sysprof_capture_writer_set_counters (writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
ids,
values,
n_counters);
/* Wait for cancellation or ½ second */
dex_await (dex_future_first (dex_ref (record->cancellable),
dex_timeout_new_usec (G_USEC_PER_SEC / 2),
NULL),
NULL);
/* If cancellable is rejected, then we're done recording */
if (dex_future_get_status (record->cancellable) != DEX_FUTURE_STATUS_PENDING)
break;
}
return dex_future_new_for_boolean (TRUE);
}
static DexFuture *
sysprof_battery_charge_record (SysprofInstrument *instrument,
SysprofRecording *recording,
GCancellable *cancellable)
{
Record *record;
g_assert (SYSPROF_IS_BATTERY_CHARGE (instrument));
g_assert (SYSPROF_IS_RECORDING (recording));
g_assert (G_IS_CANCELLABLE (cancellable));
record = g_new0 (Record, 1);
record->recording = g_object_ref (recording);
record->cancellable = dex_cancellable_new_from_cancellable (cancellable);
return dex_scheduler_spawn (NULL, 0,
sysprof_battery_charge_record_fiber,
record,
record_free);
}
static void
sysprof_battery_charge_class_init (SysprofBatteryChargeClass *klass)
{
SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass);
instrument_class->record = sysprof_battery_charge_record;
}
static void
sysprof_battery_charge_init (SysprofBatteryCharge *self)
{
}
SysprofInstrument *
sysprof_battery_charge_new (void)
{
return g_object_new (SYSPROF_TYPE_BATTERY_CHARGE, NULL);
}

View File

@ -0,0 +1,42 @@
/* sysprof-battery-charge.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-instrument.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_BATTERY_CHARGE (sysprof_battery_charge_get_type())
#define SYSPROF_IS_BATTERY_CHARGE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_BATTERY_CHARGE)
#define SYSPROF_BATTERY_CHARGE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_BATTERY_CHARGE, SysprofBatteryCharge)
#define SYSPROF_BATTERY_CHARGE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_BATTERY_CHARGE, SysprofBatteryChargeClass)
typedef struct _SysprofBatteryCharge SysprofBatteryCharge;
typedef struct _SysprofBatteryChargeClass SysprofBatteryChargeClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_battery_charge_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
SysprofInstrument *sysprof_battery_charge_new (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofBatteryCharge, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,39 @@
/* sysprof-bundled-symbolizer-private.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-bundled-symbolizer.h"
G_BEGIN_DECLS
SYSPROF_ALIGNED_BEGIN(1)
typedef struct _SysprofPackedSymbol
{
SysprofCaptureAddress addr_begin;
SysprofCaptureAddress addr_end;
guint32 pid;
guint32 offset;
guint32 tag_offset;
guint32 padding;
} SysprofPackedSymbol
SYSPROF_ALIGNED_END(1);
G_END_DECLS

View File

@ -0,0 +1,260 @@
/* sysprof-bundled-symbolizer.c
*
* Copyright 2023 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 "sysprof-bundled-symbolizer-private.h"
#include "sysprof-document-private.h"
#include "sysprof-symbolizer-private.h"
#include "sysprof-symbol-private.h"
struct _SysprofBundledSymbolizer
{
SysprofSymbolizer parent_instance;
const SysprofPackedSymbol *symbols;
guint n_symbols;
GBytes *bytes;
const gchar *beginptr;
const gchar *endptr;
};
struct _SysprofBundledSymbolizerClass
{
SysprofSymbolizerClass parent_class;
};
G_DEFINE_FINAL_TYPE (SysprofBundledSymbolizer, sysprof_bundled_symbolizer, SYSPROF_TYPE_SYMBOLIZER)
static void
sysprof_bundled_symbolizer_decode (SysprofBundledSymbolizer *self,
GBytes *bytes,
gboolean is_native)
{
char *beginptr;
char *endptr;
g_assert (SYSPROF_IS_BUNDLED_SYMBOLIZER (self));
g_assert (bytes != NULL);
/* Our GBytes always contain a trialing \0 after what we think
* is the end of the buffer.
*/
beginptr = (char *)g_bytes_get_data (bytes, NULL);
endptr = beginptr + g_bytes_get_size (bytes);
for (char *ptr = beginptr;
ptr < endptr && (ptr + sizeof (SysprofPackedSymbol)) < endptr;
ptr += sizeof (SysprofPackedSymbol))
{
SysprofPackedSymbol *sym = (SysprofPackedSymbol *)ptr;
if (sym->addr_begin == 0 &&
sym->addr_end == 0 &&
sym->pid == 0 &&
sym->offset == 0)
{
self->symbols = (const SysprofPackedSymbol *)beginptr;
self->n_symbols = sym - self->symbols;
break;
}
else if (!is_native)
{
sym->addr_begin = GUINT64_SWAP_LE_BE (sym->addr_begin);
sym->addr_end = GUINT64_SWAP_LE_BE (sym->addr_end);
sym->pid = GUINT32_SWAP_LE_BE (sym->pid);
sym->offset = GUINT32_SWAP_LE_BE (sym->offset);
sym->tag_offset = GUINT32_SWAP_LE_BE (sym->tag_offset);
}
}
self->beginptr = beginptr;
self->endptr = endptr;
self->bytes = g_bytes_ref (bytes);
}
static void
sysprof_bundled_symbolizer_prepare_async (SysprofSymbolizer *symbolizer,
SysprofDocument *document,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
SysprofBundledSymbolizer *self = (SysprofBundledSymbolizer *)symbolizer;
g_autoptr(SysprofDocumentFile) file = NULL;
g_autoptr(GBytes) bytes = NULL;
g_autoptr(GTask) task = NULL;
g_assert (SYSPROF_IS_BUNDLED_SYMBOLIZER (self));
g_assert (SYSPROF_IS_DOCUMENT (document));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_bundled_symbolizer_prepare_async);
if ((file = sysprof_document_lookup_file (document, "__symbols__")) &&
(bytes = sysprof_document_file_dup_bytes (file)))
sysprof_bundled_symbolizer_decode (self, bytes, _sysprof_document_is_native (document));
g_task_return_boolean (task, TRUE);
}
static gboolean
sysprof_bundled_symbolizer_prepare_finish (SysprofSymbolizer *symbolizer,
GAsyncResult *result,
GError **error)
{
g_assert (SYSPROF_IS_BUNDLED_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 gint
search_for_symbol_cb (gconstpointer a,
gconstpointer b)
{
const SysprofPackedSymbol *key = a;
const SysprofPackedSymbol *ele = b;
if (key->pid < ele->pid)
return -1;
if (key->pid > ele->pid)
return 1;
g_assert (key->pid == ele->pid);
if (key->addr_begin < ele->addr_begin)
return -1;
if (key->addr_begin >= ele->addr_end)
return 1;
g_assert (key->addr_begin >= ele->addr_begin);
g_assert (key->addr_end <= ele->addr_end);
return 0;
}
static SysprofSymbol *
sysprof_bundled_symbolizer_symbolize (SysprofSymbolizer *symbolizer,
SysprofStrings *strings,
const SysprofProcessInfo *process_info,
SysprofAddressContext context,
SysprofAddress address)
{
SysprofBundledSymbolizer *self = SYSPROF_BUNDLED_SYMBOLIZER (symbolizer);
g_autoptr(GRefString) tag = NULL;
const SysprofPackedSymbol *ret;
const SysprofPackedSymbol key = {
.addr_begin = address,
.addr_end = address,
.pid = process_info ? process_info->pid : 0,
.offset = 0,
.tag_offset = 0,
};
if (self->n_symbols == 0)
return NULL;
g_assert (self->symbols != NULL);
g_assert (self->n_symbols > 0);
ret = bsearch (&key,
self->symbols,
self->n_symbols,
sizeof (SysprofPackedSymbol),
search_for_symbol_cb);
if (ret == NULL || ret->offset == 0)
return NULL;
if (ret->tag_offset > 0)
{
if (ret->tag_offset < (self->endptr - self->beginptr))
tag = sysprof_strings_get (strings, &self->beginptr[ret->tag_offset]);
}
if (ret->offset < (self->endptr - self->beginptr))
{
const char *name = &self->beginptr[ret->offset];
SysprofSymbolKind kind;
if (g_str_has_prefix (name, "- -"))
kind = SYSPROF_SYMBOL_KIND_CONTEXT_SWITCH;
else if (context == SYSPROF_ADDRESS_CONTEXT_KERNEL)
kind = SYSPROF_SYMBOL_KIND_KERNEL;
else if (name[0] == '[')
kind = SYSPROF_SYMBOL_KIND_PROCESS;
else
kind = SYSPROF_SYMBOL_KIND_USER;
return _sysprof_symbol_new (sysprof_strings_get (strings, name),
NULL,
g_steal_pointer (&tag),
ret->addr_begin,
ret->addr_end,
kind);
}
return NULL;
}
static void
sysprof_bundled_symbolizer_finalize (GObject *object)
{
SysprofBundledSymbolizer *self = (SysprofBundledSymbolizer *)object;
self->symbols = NULL;
self->n_symbols = 0;
self->beginptr = NULL;
self->endptr = NULL;
g_clear_pointer (&self->bytes, g_bytes_unref);
G_OBJECT_CLASS (sysprof_bundled_symbolizer_parent_class)->finalize (object);
}
static void
sysprof_bundled_symbolizer_class_init (SysprofBundledSymbolizerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass);
object_class->finalize = sysprof_bundled_symbolizer_finalize;
symbolizer_class->prepare_async = sysprof_bundled_symbolizer_prepare_async;
symbolizer_class->prepare_finish = sysprof_bundled_symbolizer_prepare_finish;
symbolizer_class->symbolize = sysprof_bundled_symbolizer_symbolize;
}
static void
sysprof_bundled_symbolizer_init (SysprofBundledSymbolizer *self)
{
}
SysprofSymbolizer *
sysprof_bundled_symbolizer_new (void)
{
return g_object_new (SYSPROF_TYPE_BUNDLED_SYMBOLIZER, NULL);
}

View File

@ -0,0 +1,42 @@
/* sysprof-bundled-symbolizer.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-symbolizer.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_BUNDLED_SYMBOLIZER (sysprof_bundled_symbolizer_get_type())
#define SYSPROF_IS_BUNDLED_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_BUNDLED_SYMBOLIZER)
#define SYSPROF_BUNDLED_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_BUNDLED_SYMBOLIZER, SysprofBundledSymbolizer)
#define SYSPROF_BUNDLED_SYMBOLIZER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_BUNDLED_SYMBOLIZER, SysprofBundledSymbolizerClass)
typedef struct _SysprofBundledSymbolizer SysprofBundledSymbolizer;
typedef struct _SysprofBundledSymbolizerClass SysprofBundledSymbolizerClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_bundled_symbolizer_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
SysprofSymbolizer *sysprof_bundled_symbolizer_new (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofBundledSymbolizer, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,32 @@
/* sysprof-callgraph-frame-private.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-callgraph.h"
#include "sysprof-callgraph-frame.h"
G_BEGIN_DECLS
SysprofCallgraphFrame *_sysprof_callgraph_frame_new_for_node (SysprofCallgraph *callgraph,
GObject *owner,
SysprofCallgraphNode *node);
G_END_DECLS

View File

@ -0,0 +1,477 @@
/* sysprof-callgraph-frame.c
*
* Copyright 2023 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 <gio/gio.h>
#include "sysprof-callgraph-private.h"
#include "sysprof-callgraph-frame-private.h"
#include "sysprof-symbol-private.h"
#include "sysprof-document-bitset-index-private.h"
#include "eggbitset.h"
#define MAX_STACK_DEPTH 128
struct _SysprofCallgraphFrame
{
GObject parent_instance;
SysprofCallgraph *callgraph;
GObject *owner;
SysprofCallgraphNode *node;
guint n_children;
};
enum {
PROP_0,
PROP_CALLGRAPH,
PROP_SYMBOL,
PROP_N_ITEMS,
N_PROPS
};
static GType
sysprof_callgraph_frame_get_item_type (GListModel *model)
{
return SYSPROF_TYPE_CALLGRAPH_FRAME;
}
static guint
sysprof_callgraph_frame_get_n_items (GListModel *model)
{
return SYSPROF_CALLGRAPH_FRAME (model)->n_children;
}
static gpointer
sysprof_callgraph_frame_get_item (GListModel *model,
guint position)
{
SysprofCallgraphFrame *self = SYSPROF_CALLGRAPH_FRAME (model);
SysprofCallgraphNode *iter;
if (self->callgraph == NULL)
return NULL;
iter = self->node->children;
while (iter != NULL && position > 0)
{
iter = iter->next;
position--;
}
if (iter == NULL)
return NULL;
return _sysprof_callgraph_frame_new_for_node (self->callgraph, self->owner, iter);
}
static void
list_model_iface_init (GListModelInterface *iface)
{
iface->get_item_type = sysprof_callgraph_frame_get_item_type;
iface->get_n_items = sysprof_callgraph_frame_get_n_items;
iface->get_item = sysprof_callgraph_frame_get_item;
}
G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofCallgraphFrame, sysprof_callgraph_frame, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
static GParamSpec *properties [N_PROPS];
static void
sysprof_callgraph_frame_finalize (GObject *object)
{
SysprofCallgraphFrame *self = (SysprofCallgraphFrame *)object;
g_clear_weak_pointer (&self->callgraph);
g_clear_object (&self->owner);
self->node = NULL;
G_OBJECT_CLASS (sysprof_callgraph_frame_parent_class)->finalize (object);
}
static void
sysprof_callgraph_frame_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofCallgraphFrame *self = SYSPROF_CALLGRAPH_FRAME (object);
switch (prop_id)
{
case PROP_CALLGRAPH:
g_value_set_object (value, self->callgraph);
break;
case PROP_N_ITEMS:
g_value_set_uint (value, g_list_model_get_n_items (G_LIST_MODEL (self)));
break;
case PROP_SYMBOL:
g_value_set_object (value, sysprof_callgraph_frame_get_symbol (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_callgraph_frame_class_init (SysprofCallgraphFrameClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_callgraph_frame_finalize;
object_class->get_property = sysprof_callgraph_frame_get_property;
properties [PROP_CALLGRAPH] =
g_param_spec_object ("callgraph", NULL, NULL,
SYSPROF_TYPE_CALLGRAPH,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_N_ITEMS] =
g_param_spec_uint ("n-items", NULL, NULL,
0, G_MAXUINT, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_SYMBOL] =
g_param_spec_object ("symbol", NULL, NULL,
SYSPROF_TYPE_SYMBOL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_callgraph_frame_init (SysprofCallgraphFrame *self)
{
}
SysprofCallgraphFrame *
_sysprof_callgraph_frame_new_for_node (SysprofCallgraph *callgraph,
GObject *owner,
SysprofCallgraphNode *node)
{
SysprofCallgraphFrame *self;
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (callgraph), NULL);
g_return_val_if_fail (node != NULL, NULL);
self = g_object_new (SYSPROF_TYPE_CALLGRAPH_FRAME, NULL);
g_set_weak_pointer (&self->callgraph, callgraph);
g_set_object (&self->owner, owner);
self->node = node;
for (const SysprofCallgraphNode *iter = node->children;
iter != NULL;
iter = iter->next)
self->n_children++;
return self;
}
/**
* sysprof_callgraph_frame_get_symbol:
* @self: a #SysprofCallgraphFrame
*
* Gets the symbol for the frame.
*
* Returns: (nullable) (transfer none): a #SysprofSymbol
*/
SysprofSymbol *
sysprof_callgraph_frame_get_symbol (SysprofCallgraphFrame *self)
{
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self), NULL);
if (self->callgraph == NULL)
return NULL;
return self->node->summary->symbol;
}
/**
* sysprof_callgraph_frame_get_augment: (skip)
* @self: a #SysprofCallgraphFrame
*
* Gets the augmentation that was attached to the callgrpah node.
*
* Returns: (nullable) (transfer none): the augmentation data
*/
gpointer
sysprof_callgraph_frame_get_augment (SysprofCallgraphFrame *self)
{
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self), NULL);
if (self->callgraph == NULL)
return NULL;
return sysprof_callgraph_get_augment (self->callgraph, self->node);
}
/**
* sysprof_callgraph_frame_get_summary_augment: (skip)
* @self: a #SysprofCallgraphFrame
*
* Gets the augmentation that was attached to the summary for
* the callgraph node's symbol.
*
* Returns: (nullable) (transfer none): the augmentation data
*/
gpointer
sysprof_callgraph_frame_get_summary_augment (SysprofCallgraphFrame *self)
{
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self), NULL);
if (self->callgraph == NULL)
return NULL;
return sysprof_callgraph_get_summary_augment (self->callgraph, self->node);
}
/**
* sysprof_callgraph_frame_get_callgraph:
* @self: a #SysprofCallgraphFrame
*
* Gets the callgraph the frame belongs to.
*
* Returns: (transfer none) (nullable): a #SysprofCallgraph, or %NULL
*/
SysprofCallgraph *
sysprof_callgraph_frame_get_callgraph (SysprofCallgraphFrame *self)
{
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self), NULL);
return self->callgraph;
}
static gboolean
traceable_has_prefix (SysprofDocument *document,
SysprofDocumentTraceable *traceable,
GPtrArray *prefix)
{
SysprofAddressContext final_context;
SysprofSymbol **symbols;
SysprofAddress *addresses;
guint s = 0;
guint stack_depth;
guint n_symbols;
stack_depth = sysprof_document_traceable_get_stack_depth (traceable);
if (stack_depth > MAX_STACK_DEPTH)
return FALSE;
addresses = g_alloca (sizeof (SysprofAddress) * stack_depth);
sysprof_document_traceable_get_stack_addresses (traceable, addresses, stack_depth);
symbols = g_alloca (sizeof (SysprofSymbol *) * stack_depth);
n_symbols = sysprof_document_symbolize_traceable (document, traceable, symbols, stack_depth, &final_context);
if (n_symbols < prefix->len)
return FALSE;
for (guint p = 0; p < prefix->len; p++)
{
SysprofSymbol *prefix_symbol = g_ptr_array_index (prefix, p);
gboolean found = FALSE;
for (; !found && s < n_symbols; s++)
found = sysprof_symbol_equal (prefix_symbol, symbols[s]);
if (!found)
return FALSE;
}
return TRUE;
}
typedef struct _FilterByPrefix
{
SysprofDocument *document;
GListModel *traceables;
GPtrArray *prefix;
EggBitset *bitset;
} FilterByPrefix;
static void
filter_by_prefix_free (FilterByPrefix *state)
{
g_clear_object (&state->document);
g_clear_object (&state->traceables);
g_clear_pointer (&state->prefix, g_ptr_array_unref);
g_clear_pointer (&state->bitset, egg_bitset_unref);
g_free (state);
}
static void
filter_by_prefix_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
FilterByPrefix *state = task_data;
g_autoptr(EggBitset) bitset = NULL;
SysprofDocument *document;
GListModel *model;
GPtrArray *prefix;
EggBitsetIter iter;
guint i;
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_CALLGRAPH_FRAME (source_object));
g_assert (state != NULL);
g_assert (G_IS_LIST_MODEL (state->traceables));
g_assert (state->prefix != NULL);
g_assert (state->prefix->len > 0);
g_assert (state->bitset != NULL);
g_assert (!egg_bitset_is_empty (state->bitset));
bitset = egg_bitset_new_empty ();
model = state->traceables;
document = state->document;
prefix = state->prefix;
if (egg_bitset_iter_init_first (&iter, state->bitset, &i))
{
do
{
g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (model, i);
if (traceable_has_prefix (document, traceable, prefix))
egg_bitset_add (bitset, i);
}
while (egg_bitset_iter_next (&iter, &i));
}
g_task_return_pointer (task,
_sysprof_document_bitset_index_new (model, bitset),
g_object_unref);
}
/**
* sysprof_callgraph_frame_list_traceables:
* @self: a #SysprofCallgraphFrame
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: a #GAsyncReadyCallback
* @user_data: closure data for @callback
*
* Asynchronously lists the traceables that contain @self.
*/
void
sysprof_callgraph_frame_list_traceables_async (SysprofCallgraphFrame *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_autoptr(GPtrArray) prefix = NULL;
g_autoptr(EggBitset) bitset = NULL;
FilterByPrefix *state;
g_return_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_callgraph_frame_list_traceables_async);
if (self->callgraph == NULL)
{
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Callgraph already disposed");
return;
}
prefix = g_ptr_array_new ();
for (SysprofCallgraphNode *node = self->node;
node != NULL;
node = node->parent)
{
SysprofCallgraphSummary *summary = node->summary;
SysprofSymbol *symbol = summary->symbol;
if (symbol->kind != SYSPROF_SYMBOL_KIND_USER &&
symbol->kind != SYSPROF_SYMBOL_KIND_KERNEL)
continue;
if (bitset == NULL)
bitset = egg_bitset_copy (summary->traceables);
else
egg_bitset_intersect (bitset, summary->traceables);
g_ptr_array_add (prefix, symbol);
}
if (prefix->len == 0 || egg_bitset_is_empty (bitset))
{
g_task_return_pointer (task,
g_list_store_new (SYSPROF_TYPE_DOCUMENT_TRACEABLE),
g_object_unref);
return;
}
state = g_new0 (FilterByPrefix, 1);
state->document = g_object_ref (self->callgraph->document);
state->traceables = g_object_ref (self->callgraph->traceables);
state->prefix = g_steal_pointer (&prefix);
state->bitset = egg_bitset_ref (bitset);
g_task_set_task_data (task, state, (GDestroyNotify)filter_by_prefix_free);
g_task_run_in_thread (task, filter_by_prefix_worker);
}
/**
* sysprof_callgraph_frame_list_traceables_finish:
* @self: a #SysprofCallgraphFrame
*
* Completes an asynchronous request to list traceables.
*
* Returns: (transfer full): a #GListModel of #SysprofDocumentTraceable if
* successful; otherwise %NULL and @error is set.
*/
GListModel *
sysprof_callgraph_frame_list_traceables_finish (SysprofCallgraphFrame *self,
GAsyncResult *result,
GError **error)
{
GListModel *ret;
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self), NULL);
g_return_val_if_fail (G_IS_TASK (result), NULL);
ret = g_task_propagate_pointer (G_TASK (result), error);
g_return_val_if_fail (!ret || G_IS_LIST_MODEL (ret), NULL);
return ret;
}
gboolean
sysprof_callgraph_frame_is_leaf (SysprofCallgraphFrame *self)
{
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_FRAME (self), 0);
return self->n_children == 0;
}

View File

@ -0,0 +1,54 @@
/* sysprof-callgraph-frame.h
*
* Copyright 2023 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
*/
#pragma once
#include <gio/gio.h>
#include <sysprof-capture.h>
#include "sysprof-symbol.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_CALLGRAPH_FRAME (sysprof_callgraph_frame_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofCallgraphFrame, sysprof_callgraph_frame, SYSPROF, CALLGRAPH_FRAME, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSymbol *sysprof_callgraph_frame_get_symbol (SysprofCallgraphFrame *self);
SYSPROF_AVAILABLE_IN_ALL
gpointer sysprof_callgraph_frame_get_augment (SysprofCallgraphFrame *self);
SYSPROF_AVAILABLE_IN_ALL
gpointer sysprof_callgraph_frame_get_summary_augment (SysprofCallgraphFrame *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_callgraph_frame_list_traceables_async (SysprofCallgraphFrame *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_callgraph_frame_list_traceables_finish (SysprofCallgraphFrame *self,
GAsyncResult *result,
GError **error);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_callgraph_frame_is_leaf (SysprofCallgraphFrame *self);
G_END_DECLS

View File

@ -0,0 +1,87 @@
/* sysprof-callgraph-private.h
*
* Copyright 2023 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
*/
#pragma once
#include <gio/gio.h>
#include "sysprof-callgraph.h"
#include "sysprof-document.h"
#include "eggbitset.h"
G_BEGIN_DECLS
typedef struct _SysprofCallgraphSummary
{
SysprofSymbol *symbol;
EggBitset *traceables;
GPtrArray *callers;
gpointer augment[2];
} SysprofCallgraphSummary;
struct _SysprofCallgraphNode
{
SysprofCallgraphNode *parent;
SysprofCallgraphNode *prev;
SysprofCallgraphNode *next;
SysprofCallgraphNode *children;
SysprofCallgraphSummary *summary;
gpointer augment[2];
};
struct _SysprofCallgraph
{
GObject parent_instance;
SysprofDocument *document;
GListModel *traceables;
GHashTable *symbol_to_summary;
GPtrArray *symbols;
SysprofCallgraphFlags flags;
gsize augment_size;
SysprofAugmentationFunc augment_func;
gpointer augment_func_data;
GDestroyNotify augment_func_data_destroy;
SysprofCallgraphNode root;
};
void _sysprof_callgraph_new_async (SysprofDocument *document,
SysprofCallgraphFlags flags,
GListModel *traceables,
gsize augment_size,
SysprofAugmentationFunc augment_func,
gpointer augment_func_data,
GDestroyNotify augment_func_data_destroy,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SysprofCallgraph *_sysprof_callgraph_new_finish (GAsyncResult *result,
GError **error);
gpointer _sysprof_callgraph_get_symbol_augment (SysprofCallgraph *self,
SysprofSymbol *symbol);
void _sysprof_callgraph_node_free (SysprofCallgraphNode *self,
gboolean free_self);
G_END_DECLS

View File

@ -0,0 +1,30 @@
/* sysprof-callgraph-symbol-private.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-callgraph.h"
G_BEGIN_DECLS
GListModel *_sysprof_callgraph_symbol_list_model_new (SysprofCallgraph *callgraph,
GPtrArray *symbols);
G_END_DECLS

View File

@ -0,0 +1,265 @@
/* sysprof-callgraph-symbol.c
*
* Copyright 2023 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 <gio/gio.h>
#include "sysprof-callgraph-private.h"
#include "sysprof-callgraph-symbol-private.h"
struct _SysprofCallgraphSymbol
{
GObject parent_instance;
SysprofCallgraph *callgraph;
SysprofSymbol *symbol;
};
enum {
PROP_0,
PROP_CALLGRAPH,
PROP_SYMBOL,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofCallgraphSymbol, sysprof_callgraph_symbol, G_TYPE_OBJECT)
static GParamSpec *properties [N_PROPS];
static void
sysprof_callgraph_symbol_finalize (GObject *object)
{
SysprofCallgraphSymbol *self = (SysprofCallgraphSymbol *)object;
g_clear_weak_pointer (&self->callgraph);
g_clear_object (&self->symbol);
G_OBJECT_CLASS (sysprof_callgraph_symbol_parent_class)->finalize (object);
}
static void
sysprof_callgraph_symbol_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofCallgraphSymbol *self = SYSPROF_CALLGRAPH_SYMBOL (object);
switch (prop_id)
{
case PROP_CALLGRAPH:
g_value_set_object (value, self->callgraph);
break;
case PROP_SYMBOL:
g_value_set_object (value, sysprof_callgraph_symbol_get_symbol (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_callgraph_symbol_class_init (SysprofCallgraphSymbolClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_callgraph_symbol_finalize;
object_class->get_property = sysprof_callgraph_symbol_get_property;
properties [PROP_CALLGRAPH] =
g_param_spec_object ("callgraph", NULL, NULL,
SYSPROF_TYPE_CALLGRAPH,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_SYMBOL] =
g_param_spec_object ("symbol", NULL, NULL,
SYSPROF_TYPE_SYMBOL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_callgraph_symbol_init (SysprofCallgraphSymbol *self)
{
}
SysprofCallgraphSymbol *
_sysprof_callgraph_symbol_new (SysprofCallgraph *callgraph,
SysprofSymbol *symbol)
{
SysprofCallgraphSymbol *self;
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (callgraph), NULL);
g_return_val_if_fail (SYSPROF_IS_SYMBOL (symbol), NULL);
self = g_object_new (SYSPROF_TYPE_CALLGRAPH_SYMBOL, NULL);
g_set_weak_pointer (&self->callgraph, callgraph);
g_set_object (&self->symbol, symbol);
return self;
}
/**
* sysprof_callgraph_symbol_get_symbol:
* @self: a #SysprofCallgraphSymbol
*
* Gets the symbol for the symbol.
*
* Returns: (nullable) (transfer none): a #SysprofSymbol
*/
SysprofSymbol *
sysprof_callgraph_symbol_get_symbol (SysprofCallgraphSymbol *self)
{
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_SYMBOL (self), NULL);
return self->symbol;
}
/**
* sysprof_callgraph_symbol_get_summary_augment: (skip)
* @self: a #SysprofCallgraphSymbol
*
* Gets the augmentation that was attached to the summary for
* the callgraph node's symbol.
*
* Returns: (nullable) (transfer none): the augmentation data
*/
gpointer
sysprof_callgraph_symbol_get_summary_augment (SysprofCallgraphSymbol *self)
{
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_SYMBOL (self), NULL);
if (self->callgraph == NULL)
return NULL;
return _sysprof_callgraph_get_symbol_augment (self->callgraph, self->symbol);
}
/**
* sysprof_callgraph_symbol_get_callgraph:
* @self: a #SysprofCallgraphSymbol
*
* Gets the callgraph the symbol belongs to.
*
* Returns: (transfer none) (nullable): a #SysprofCallgraph, or %NULL
*/
SysprofCallgraph *
sysprof_callgraph_symbol_get_callgraph (SysprofCallgraphSymbol *self)
{
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_SYMBOL (self), NULL);
return self->callgraph;
}
typedef struct _SysprofCallgraphSymbolListModel
{
GObject parent_instance;
SysprofCallgraph *callgraph;
GPtrArray *symbols;
} SysprofCallgraphSymbolListModel;
static guint
sysprof_callgraph_symbol_list_model_get_n_items (GListModel *model)
{
SysprofCallgraphSymbolListModel *self = (SysprofCallgraphSymbolListModel *)model;
if (self->symbols != NULL)
return self->symbols->len;
return 0;
}
static GType
sysprof_callgraph_symbol_list_model_get_item_type (GListModel *model)
{
return SYSPROF_TYPE_CALLGRAPH_SYMBOL;
}
static gpointer
sysprof_callgraph_symbol_list_model_get_item (GListModel *model,
guint position)
{
SysprofCallgraphSymbolListModel *self = (SysprofCallgraphSymbolListModel *)model;
if (self->symbols == NULL || position >= self->symbols->len || self->callgraph == NULL)
return NULL;
return _sysprof_callgraph_symbol_new (self->callgraph,
g_ptr_array_index (self->symbols, position));
}
static void
list_model_iface_init (GListModelInterface *iface)
{
iface->get_n_items = sysprof_callgraph_symbol_list_model_get_n_items;
iface->get_item_type = sysprof_callgraph_symbol_list_model_get_item_type;
iface->get_item = sysprof_callgraph_symbol_list_model_get_item;
}
#define SYSPROF_TYPE_CALLGRAPH_SYMBOL_LIST_MODEL (sysprof_callgraph_symbol_list_model_get_type())
G_DECLARE_FINAL_TYPE (SysprofCallgraphSymbolListModel, sysprof_callgraph_symbol_list_model, SYSPROF, CALLGRAPH_SYMBOL_LIST_MODEL, GObject)
G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofCallgraphSymbolListModel, sysprof_callgraph_symbol_list_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
static void
sysprof_callgraph_symbol_list_model_dispose (GObject *object)
{
SysprofCallgraphSymbolListModel *self = (SysprofCallgraphSymbolListModel *)object;
g_clear_pointer (&self->symbols, g_ptr_array_unref);
g_clear_weak_pointer (&self->callgraph);
G_OBJECT_CLASS (sysprof_callgraph_symbol_parent_class)->dispose (object);
}
static void
sysprof_callgraph_symbol_list_model_class_init (SysprofCallgraphSymbolListModelClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = sysprof_callgraph_symbol_list_model_dispose;
}
static void
sysprof_callgraph_symbol_list_model_init (SysprofCallgraphSymbolListModel *self)
{
}
GListModel *
_sysprof_callgraph_symbol_list_model_new (SysprofCallgraph *callgraph,
GPtrArray *symbols)
{
SysprofCallgraphSymbolListModel *self;
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (callgraph), NULL);
self = g_object_new (SYSPROF_TYPE_CALLGRAPH_SYMBOL_LIST_MODEL, NULL);
g_set_weak_pointer (&self->callgraph, callgraph);
if (symbols != NULL)
self->symbols = g_ptr_array_ref (symbols);
return G_LIST_MODEL (self);
}
G_END_DECLS

View File

@ -0,0 +1,41 @@
/* sysprof-callgraph-symbol.h
*
* Copyright 2023 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
*/
#pragma once
#include <glib-object.h>
#include <sysprof-capture.h>
#include "sysprof-symbol.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_CALLGRAPH_SYMBOL (sysprof_callgraph_symbol_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofCallgraphSymbol, sysprof_callgraph_symbol, SYSPROF, CALLGRAPH_SYMBOL, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofSymbol *sysprof_callgraph_symbol_get_symbol (SysprofCallgraphSymbol *self);
SYSPROF_AVAILABLE_IN_ALL
gpointer sysprof_callgraph_symbol_get_summary_augment (SysprofCallgraphSymbol *self);
G_END_DECLS

View File

@ -0,0 +1,658 @@
/* sysprof-callgraph.c
*
* Copyright 2023 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 "sysprof-callgraph-private.h"
#include "sysprof-callgraph-frame-private.h"
#include "sysprof-callgraph-symbol-private.h"
#include "sysprof-descendants-model-private.h"
#include "sysprof-document-bitset-index-private.h"
#include "sysprof-document-private.h"
#include "sysprof-document-traceable.h"
#include "sysprof-symbol-private.h"
#include "eggbitset.h"
#define MAX_STACK_DEPTH 1024
#define INLINE_AUGMENT_SIZE (GLIB_SIZEOF_VOID_P*2)
static GType
sysprof_callgraph_get_item_type (GListModel *model)
{
return SYSPROF_TYPE_CALLGRAPH_FRAME;
}
static guint
sysprof_callgraph_get_n_items (GListModel *model)
{
return 1;
}
static gpointer
sysprof_callgraph_get_item (GListModel *model,
guint position)
{
SysprofCallgraph *self = SYSPROF_CALLGRAPH (model);
if (position > 0)
return NULL;
return _sysprof_callgraph_frame_new_for_node (self, NULL, &self->root);
}
static void
list_model_iface_init (GListModelInterface *iface)
{
iface->get_item_type = sysprof_callgraph_get_item_type;
iface->get_n_items = sysprof_callgraph_get_n_items;
iface->get_item = sysprof_callgraph_get_item;
}
G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofCallgraph, sysprof_callgraph, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
static SysprofSymbol *everything;
static SysprofSymbol *untraceable;
static void
sysprof_callgraph_summary_free_all (SysprofCallgraphSummary *summary)
{
g_clear_pointer (&summary->augment[0], g_free);
summary->augment[1] = NULL;
g_clear_pointer (&summary->callers, g_ptr_array_unref);
g_clear_pointer (&summary->traceables, egg_bitset_unref);
g_free (summary);
}
static void
sysprof_callgraph_summary_free_self (SysprofCallgraphSummary *summary)
{
summary->augment[0] = NULL;
summary->augment[1] = NULL;
g_clear_pointer (&summary->callers, g_ptr_array_unref);
g_clear_pointer (&summary->traceables, egg_bitset_unref);
g_free (summary);
}
static inline SysprofCallgraphSummary *
sysprof_callgraph_get_summary (SysprofCallgraph *self,
SysprofSymbol *symbol)
{
SysprofCallgraphSummary *summary;
if G_UNLIKELY (!(summary = g_hash_table_lookup (self->symbol_to_summary, symbol)))
{
summary = g_new0 (SysprofCallgraphSummary, 1);
summary->traceables = egg_bitset_new_empty ();
summary->callers = g_ptr_array_new ();
summary->symbol = symbol;
g_hash_table_insert (self->symbol_to_summary, symbol, summary);
g_ptr_array_add (self->symbols, symbol);
}
return summary;
}
void
_sysprof_callgraph_node_free (SysprofCallgraphNode *node,
gboolean free_self)
{
SysprofCallgraphNode *iter = node->children;
while (iter)
{
SysprofCallgraphNode *to_free = iter;
iter = iter->next;
_sysprof_callgraph_node_free (to_free, TRUE);
}
if (free_self)
g_free (node);
}
static void
sysprof_callgraph_dispose (GObject *object)
{
SysprofCallgraph *self = (SysprofCallgraph *)object;
GDestroyNotify notify = self->augment_func_data_destroy;
gpointer notify_data = self->augment_func_data;
self->augment_size = 0;
self->augment_func = NULL;
self->augment_func_data = NULL;
self->augment_func_data_destroy = NULL;
if (notify != NULL)
notify (notify_data);
G_OBJECT_CLASS (sysprof_callgraph_parent_class)->dispose (object);
}
static void
sysprof_callgraph_finalize (GObject *object)
{
SysprofCallgraph *self = (SysprofCallgraph *)object;
g_clear_pointer (&self->symbol_to_summary, g_hash_table_unref);
g_clear_pointer (&self->symbols, g_ptr_array_unref);
g_clear_object (&self->document);
g_clear_object (&self->traceables);
_sysprof_callgraph_node_free (&self->root, FALSE);
G_OBJECT_CLASS (sysprof_callgraph_parent_class)->finalize (object);
}
static void
sysprof_callgraph_class_init (SysprofCallgraphClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = sysprof_callgraph_dispose;
object_class->finalize = sysprof_callgraph_finalize;
everything = _sysprof_symbol_new (g_ref_string_new_intern ("All Processes"),
NULL, NULL, 0, 0,
SYSPROF_SYMBOL_KIND_ROOT);
untraceable = _sysprof_symbol_new (g_ref_string_new_intern ("Unwindable"),
NULL, NULL, 0, 0,
SYSPROF_SYMBOL_KIND_UNWINDABLE);
}
static void
sysprof_callgraph_init (SysprofCallgraph *self)
{
}
static void
sysprof_callgraph_populate_callers (SysprofCallgraph *self,
SysprofCallgraphNode *node,
guint list_model_index)
{
g_assert (SYSPROF_IS_CALLGRAPH (self));
g_assert (node != NULL);
for (const SysprofCallgraphNode *iter = node;
iter != NULL;
iter = iter->parent)
{
egg_bitset_add (iter->summary->traceables, list_model_index);
if (iter->parent != NULL)
{
SysprofSymbol *parent_symbol = iter->parent->summary->symbol;
SysprofSymbolKind parent_kind = sysprof_symbol_get_kind (parent_symbol);
guint pos;
if (parent_kind != SYSPROF_SYMBOL_KIND_PROCESS &&
parent_kind != SYSPROF_SYMBOL_KIND_ROOT &&
!g_ptr_array_find (iter->summary->callers, parent_symbol, &pos))
g_ptr_array_add (iter->summary->callers, parent_symbol);
}
}
}
static SysprofCallgraphNode *
sysprof_callgraph_add_trace (SysprofCallgraph *self,
SysprofSymbol **symbols,
guint n_symbols,
guint list_model_index,
gboolean hide_system_libraries)
{
SysprofCallgraphNode *parent = NULL;
g_assert (SYSPROF_IS_CALLGRAPH (self));
g_assert (n_symbols >= 2);
g_assert (symbols[n_symbols-1] == everything);
parent = &self->root;
for (guint i = n_symbols - 1; i > 0; i--)
{
SysprofSymbol *symbol = symbols[i-1];
SysprofCallgraphNode *node = NULL;
if (hide_system_libraries && _sysprof_symbol_is_system_library (symbol))
continue;
/* Try to find @symbol within the children of @parent */
for (SysprofCallgraphNode *iter = parent->children;
iter != NULL;
iter = iter->next)
{
g_assert (iter != NULL);
g_assert (iter->summary != NULL);
g_assert (iter->summary->symbol != NULL);
g_assert (symbol != NULL);
if (_sysprof_symbol_equal (iter->summary->symbol, symbol))
{
node = iter;
goto next_symbol;
}
}
/* Otherwise create a new node */
node = g_new0 (SysprofCallgraphNode, 1);
node->summary = sysprof_callgraph_get_summary (self, symbol);
node->parent = parent;
node->next = parent->children;
if (parent->children)
parent->children->prev = node;
parent->children = node;
next_symbol:
parent = node;
}
sysprof_callgraph_populate_callers (self, parent, list_model_index);
return parent;
}
static void
reverse_symbols (SysprofSymbol **symbols,
guint n_symbols)
{
guint half = n_symbols / 2;
for (guint i = 0; i < half; i++)
{
SysprofSymbol *tmp = symbols[i];
symbols[i] = symbols[n_symbols-1-i];
symbols[n_symbols-1-i] = tmp;
}
}
static void
sysprof_callgraph_add_traceable (SysprofCallgraph *self,
SysprofDocumentTraceable *traceable,
guint list_model_index)
{
SysprofAddressContext final_context;
SysprofCallgraphNode *node;
SysprofSymbol **symbols;
guint stack_depth;
guint n_symbols;
int pid;
int tid;
g_assert (SYSPROF_IS_CALLGRAPH (self));
g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable));
pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable));
tid = sysprof_document_traceable_get_thread_id (traceable);
stack_depth = sysprof_document_traceable_get_stack_depth (traceable);
if (stack_depth == 0 || stack_depth > MAX_STACK_DEPTH)
return;
symbols = g_newa (SysprofSymbol *, stack_depth + 4);
n_symbols = sysprof_document_symbolize_traceable (self->document,
traceable,
symbols,
stack_depth,
&final_context);
g_assert (n_symbols <= stack_depth);
/* Sometimes we get a very unhelpful unwind from the capture
* which is basically a single frame of "user space context".
* That means we got no amount of the stack, but we should
* really account costs to something in the application other
* than the [Application] entry itself so that it's more clear
* that it was a corrupted unwind when recording.
*/
if (n_symbols == 1 &&
_sysprof_symbol_is_context_switch (symbols[0]) &&
final_context == SYSPROF_ADDRESS_CONTEXT_USER)
symbols[0] = untraceable;
/* We saved 3 extra spaces for these above so that we can
* tack on the "Process" symbol and the "All Processes" symbol.
* If the final address context places us in Kernel, we want
* to add a "- - Kernel - -" symbol to ensure that we are
* accounting cost to the kernel for the process.
*/
if (final_context == SYSPROF_ADDRESS_CONTEXT_KERNEL)
symbols[n_symbols++] = _sysprof_document_kernel_symbol (self->document);
/* If the first thing we see is a context switch, then there is
* nothing after it to account for. Just skip the symbol as it
* provides nothing to us in the callgraph.
*/
if (_sysprof_symbol_is_context_switch (symbols[0]))
{
symbols++;
n_symbols--;
}
if ((self->flags & SYSPROF_CALLGRAPH_FLAGS_BOTTOM_UP) != 0)
reverse_symbols (symbols, n_symbols);
/* If the user requested thread-ids within each process, then
* insert a symbol for that before the real stacks.
*/
if ((self->flags & SYSPROF_CALLGRAPH_FLAGS_INCLUDE_THREADS) != 0)
symbols[n_symbols++] = _sysprof_document_thread_symbol (self->document, pid, tid);
symbols[n_symbols++] = _sysprof_document_process_symbol (self->document, pid);
symbols[n_symbols++] = everything;
node = sysprof_callgraph_add_trace (self,
symbols,
n_symbols,
list_model_index,
!!(self->flags & SYSPROF_CALLGRAPH_FLAGS_HIDE_SYSTEM_LIBRARIES));
if (node && self->augment_func)
self->augment_func (self,
node,
SYSPROF_DOCUMENT_FRAME (traceable),
TRUE,
self->augment_func_data);
}
static void
sysprof_callgraph_new_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
SysprofCallgraph *self = task_data;
guint n_items;
g_assert (G_IS_TASK (task));
g_assert (source_object == NULL);
g_assert (SYSPROF_IS_CALLGRAPH (self));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
n_items = g_list_model_get_n_items (self->traceables);
for (guint i = 0; i < n_items; i++)
{
g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (self->traceables, i);
sysprof_callgraph_add_traceable (self, traceable, i);
}
g_task_return_pointer (task, g_object_ref (self), g_object_unref);
}
void
_sysprof_callgraph_new_async (SysprofDocument *document,
SysprofCallgraphFlags flags,
GListModel *traceables,
gsize augment_size,
SysprofAugmentationFunc augment_func,
gpointer augment_func_data,
GDestroyNotify augment_func_data_destroy,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(SysprofCallgraph) self = NULL;
g_autoptr(GTask) task = NULL;
GDestroyNotify summary_free;
g_return_if_fail (SYSPROF_IS_DOCUMENT (document));
g_return_if_fail (G_IS_LIST_MODEL (traceables));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
if (augment_size > INLINE_AUGMENT_SIZE)
summary_free = (GDestroyNotify)sysprof_callgraph_summary_free_all;
else
summary_free = (GDestroyNotify)sysprof_callgraph_summary_free_self;
self = g_object_new (SYSPROF_TYPE_CALLGRAPH, NULL);
self->flags = flags;
self->document = g_object_ref (document);
self->traceables = g_object_ref (traceables);
self->augment_size = augment_size;
self->augment_func = augment_func;
self->augment_func_data = augment_func_data;
self->augment_func_data_destroy = augment_func_data_destroy;
self->symbol_to_summary = g_hash_table_new_full ((GHashFunc)sysprof_symbol_hash,
(GEqualFunc)sysprof_symbol_equal,
NULL,
summary_free);
self->symbols = g_ptr_array_new ();
self->root.summary = sysprof_callgraph_get_summary (self, everything);
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, _sysprof_callgraph_new_async);
g_task_set_task_data (task, g_object_ref (self), g_object_unref);
g_task_run_in_thread (task, sysprof_callgraph_new_worker);
}
SysprofCallgraph *
_sysprof_callgraph_new_finish (GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (G_IS_TASK (result), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
static inline gpointer
get_augmentation (SysprofCallgraph *self,
gpointer *augment_location)
{
if (self->augment_size == 0)
return NULL;
if (self->augment_size <= INLINE_AUGMENT_SIZE)
return augment_location;
if (*augment_location == NULL)
*augment_location = g_malloc0 (self->augment_size);
return *augment_location;
}
gpointer
sysprof_callgraph_get_augment (SysprofCallgraph *self,
SysprofCallgraphNode *node)
{
if (node == NULL)
node = &self->root;
return get_augmentation (self, &node->augment[0]);
}
gpointer
sysprof_callgraph_get_summary_augment (SysprofCallgraph *self,
SysprofCallgraphNode *node)
{
if (node == NULL)
node = &self->root;
return get_augmentation (self, &node->summary->augment[0]);
}
gpointer
_sysprof_callgraph_get_symbol_augment (SysprofCallgraph *self,
SysprofSymbol *symbol)
{
SysprofCallgraphSummary *summary;
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL);
g_return_val_if_fail (SYSPROF_IS_SYMBOL (symbol), NULL);
if ((summary = g_hash_table_lookup (self->symbol_to_summary, symbol)))
return get_augmentation (self, &summary->augment[0]);
return NULL;
}
SysprofCallgraphNode *
sysprof_callgraph_node_parent (SysprofCallgraphNode *node)
{
return node->parent;
}
/**
* sysprof_callgraph_list_callers:
* @self: a #SysprofCallgraph
* @symbol: a #SysprofSymbol
*
* Gets a list of #SysprofSymbol that call @symbol.
*
* Returns: (trasfer full): a #GListModel of #SysprofCallgraphSymbol
*/
GListModel *
sysprof_callgraph_list_callers (SysprofCallgraph *self,
SysprofSymbol *symbol)
{
SysprofCallgraphSummary *summary;
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL);
g_return_val_if_fail (SYSPROF_IS_SYMBOL (symbol), NULL);
if ((summary = g_hash_table_lookup (self->symbol_to_summary, symbol)))
return _sysprof_callgraph_symbol_list_model_new (self, summary->callers);
return G_LIST_MODEL (g_list_store_new (SYSPROF_TYPE_CALLGRAPH_SYMBOL));
}
/**
* sysprof_callgraph_list_traceables_for_symbol:
* @self: a #SysprofCallgraph
* @symbol: a #SysprofSymbol
*
* Gets a list of all the #SysprofTraceable within the callgraph
* that contain @symbol.
*
* Returns: (transfer full): a #GListModel of #SysprofTraceable
*/
GListModel *
sysprof_callgraph_list_traceables_for_symbol (SysprofCallgraph *self,
SysprofSymbol *symbol)
{
SysprofCallgraphSummary *summary;
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL);
g_return_val_if_fail (SYSPROF_IS_SYMBOL (symbol), NULL);
if ((summary = g_hash_table_lookup (self->symbol_to_summary, symbol)))
return _sysprof_document_bitset_index_new (self->traceables, summary->traceables);
return G_LIST_MODEL (g_list_store_new (SYSPROF_TYPE_DOCUMENT_TRACEABLE));
}
GListModel *
sysprof_callgraph_list_traceables_for_symbols_matching (SysprofCallgraph *self,
const char *pattern)
{
g_autoptr(GPatternSpec) pspec = NULL;
g_autoptr(EggBitset) bitset = NULL;
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL);
if (pattern == NULL || pattern[0] == 0)
return g_object_ref (self->traceables);
pspec = g_pattern_spec_new (pattern);
bitset = egg_bitset_new_empty ();
for (guint i = 0; i < self->symbols->len; i++)
{
SysprofSymbol *symbol = g_ptr_array_index (self->symbols, i);
const char *name = sysprof_symbol_get_name (symbol);
if (g_pattern_spec_match (pspec, strlen (name), name, NULL))
{
SysprofCallgraphSummary *summary = g_hash_table_lookup (self->symbol_to_summary, symbol);
if (summary != NULL)
egg_bitset_union (bitset, summary->traceables);
}
}
return _sysprof_document_bitset_index_new (self->traceables, bitset);
}
/**
* sysprof_callgraph_list_symbols:
* @self: a #SysprofCallgraph
*
* Gets a #GListModel of #SysprofCallgraphSymbol that an be used to
* display a function list and associated augmentation data.
*
* Returns: (transfer full): a #GListModel of #SysprofCallgraphSymbol
*/
GListModel *
sysprof_callgraph_list_symbols (SysprofCallgraph *self)
{
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL);
return _sysprof_callgraph_symbol_list_model_new (self, self->symbols);
}
static void
sysprof_callgraph_descendants_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
SysprofCallgraph *self = source_object;
SysprofSymbol *symbol = task_data;
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_CALLGRAPH (self));
g_assert (SYSPROF_IS_SYMBOL (symbol));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
g_task_return_pointer (task,
_sysprof_descendants_model_new (self, symbol),
g_object_unref);
}
void
sysprof_callgraph_descendants_async (SysprofCallgraph *self,
SysprofSymbol *symbol,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_return_if_fail (SYSPROF_IS_CALLGRAPH (self));
g_return_if_fail (SYSPROF_IS_SYMBOL (symbol));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_callgraph_descendants_async);
g_task_set_task_data (task, g_object_ref (symbol), g_object_unref);
g_task_run_in_thread (task, sysprof_callgraph_descendants_worker);
}
GListModel *
sysprof_callgraph_descendants_finish (SysprofCallgraph *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (self), NULL);
g_return_val_if_fail (G_IS_TASK (result), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}

View File

@ -0,0 +1,109 @@
/* sysprof-callgraph.h
*
* Copyright 2023 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
*/
#pragma once
#include <gio/gio.h>
#include <sysprof-capture.h>
#include "sysprof-callgraph-frame.h"
#include "sysprof-callgraph-symbol.h"
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_CALLGRAPH (sysprof_callgraph_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofCallgraph, sysprof_callgraph, SYSPROF, CALLGRAPH, GObject)
typedef struct _SysprofCallgraphNode SysprofCallgraphNode;
/**
* SysprofAugmentationFunc:
* @callgraph: the callgraph being augmented
* @node: the node within the callgraph
* @frame: the frame used to generate this node
* @summarize: if summaries should be generated
* @user_data: closure data for augmentation func
*
* This function is called for the bottom most node in a trace as it is added
* to a callgraph.
*
* The augmentation func should augment the node in whatever way it sees fit
* and generally will want to walk up the node tree to the root to augment the
* parents as it goes. Your augmentation function is not called for each node,
* only the deepest node.
*
* If @summarize is %TRUE, then you should also generate summary augmentation
* using sysprof_callgraph_get_summary_augment() or similar.
*/
typedef void (*SysprofAugmentationFunc) (SysprofCallgraph *callgraph,
SysprofCallgraphNode *node,
SysprofDocumentFrame *frame,
gboolean summarize,
gpointer user_data);
typedef enum _SysprofCallgraphFlags
{
SYSPROF_CALLGRAPH_FLAGS_NONE = 0,
SYSPROF_CALLGRAPH_FLAGS_INCLUDE_THREADS = 1 << 1,
SYSPROF_CALLGRAPH_FLAGS_HIDE_SYSTEM_LIBRARIES = 1 << 2,
SYSPROF_CALLGRAPH_FLAGS_BOTTOM_UP = 1 << 3,
} SysprofCallgraphFlags;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_callgraph_flags_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_callgraph_list_symbols (SysprofCallgraph *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_callgraph_list_callers (SysprofCallgraph *self,
SysprofSymbol *symbol);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_callgraph_list_traceables_for_symbol (SysprofCallgraph *self,
SysprofSymbol *symbol);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_callgraph_list_traceables_for_symbols_matching (SysprofCallgraph *self,
const char *pattern);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_callgraph_descendants_async (SysprofCallgraph *self,
SysprofSymbol *symbol,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_callgraph_descendants_finish (SysprofCallgraph *self,
GAsyncResult *result,
GError **error);
SYSPROF_AVAILABLE_IN_ALL
gpointer sysprof_callgraph_get_augment (SysprofCallgraph *self,
SysprofCallgraphNode *node);
SYSPROF_AVAILABLE_IN_ALL
gpointer sysprof_callgraph_get_summary_augment (SysprofCallgraph *self,
SysprofCallgraphNode *node);
SYSPROF_AVAILABLE_IN_ALL
SysprofCallgraphNode *sysprof_callgraph_node_parent (SysprofCallgraphNode *node);
SYSPROF_AVAILABLE_IN_ALL
SysprofCallgraph *sysprof_callgraph_frame_get_callgraph (SysprofCallgraphFrame *self);
SYSPROF_AVAILABLE_IN_ALL
SysprofCallgraph *sysprof_callgraph_symbol_get_callgraph (SysprofCallgraphSymbol *self);
G_END_DECLS

View File

@ -0,0 +1,33 @@
/* sysprof-controlfd-instrument-private.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-instrument-private.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_CONTROLFD_INSTRUMENT (sysprof_controlfd_instrument_get_type())
G_DECLARE_FINAL_TYPE (SysprofControlfdInstrument, sysprof_controlfd_instrument, SYSPROF, CONTROLFD_INSTRUMENT, SysprofInstrument)
SysprofInstrument *_sysprof_controlfd_instrument_new (void);
G_END_DECLS

View File

@ -0,0 +1,352 @@
/* sysprof-controlfd-instrument.c
*
* Copyright 2023 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 <glib.h>
#ifdef G_OS_UNIX
# include <fcntl.h>
# include <glib-unix.h>
# include <glib/gstdio.h>
# include <gio/gunixinputstream.h>
# include <gio/gunixoutputstream.h>
# include <gio/gunixconnection.h>
# include <sys/socket.h>
# include <sys/types.h>
#endif
#include "sysprof-controlfd-instrument-private.h"
#include "sysprof-recording-private.h"
#ifdef G_OS_UNIX
# include "mapped-ring-buffer.h"
# include "mapped-ring-buffer-source-private.h"
#endif
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCaptureReader, sysprof_capture_reader_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCaptureWriter, sysprof_capture_writer_unref)
struct _SysprofControlfdInstrument
{
SysprofInstrument parent_instance;
#ifdef G_OS_UNIX
GUnixConnection *connection;
char read_buf[10];
#endif
};
G_DEFINE_FINAL_TYPE (SysprofControlfdInstrument, sysprof_controlfd_instrument, SYSPROF_TYPE_INSTRUMENT)
#ifdef G_OS_UNIX
typedef struct _RingData
{
SysprofCaptureWriter *writer;
GArray *source_ids;
guint id;
} RingData;
static void
ring_data_free (gpointer data)
{
RingData *ring_data = data;
for (guint i = 0; i < ring_data->source_ids->len; i++)
{
guint *id = &g_array_index (ring_data->source_ids, guint, i);
if (*id == ring_data->id)
{
*id = 0;
g_array_remove_index_fast (ring_data->source_ids, i);
break;
}
}
ring_data->id = 0;
g_clear_pointer (&ring_data->writer, sysprof_capture_writer_unref);
g_array_unref (ring_data->source_ids);
g_free (ring_data);
}
static DexFuture *
sysprof_controlfd_instrument_prepare (SysprofInstrument *instrument,
SysprofRecording *recording)
{
SysprofControlfdInstrument *self = (SysprofControlfdInstrument *)instrument;
SysprofSpawnable *spawnable;
g_autofree char *child_no_str = NULL;
g_autoptr(GSocketConnection) stream = NULL;
g_autoptr(GSocket) sock = NULL;
int fds[2] = {-1, -1};
int child_no;
g_assert (SYSPROF_IS_CONTROLFD_INSTRUMENT (self));
g_assert (SYSPROF_IS_RECORDING (recording));
/* If the recording is not spawning a process, then there is
* nothing for us to do.
*/
if (!(spawnable = _sysprof_recording_get_spawnable (recording)))
goto finish;
/* Create a socket pair to send control messages over */
if (socketpair (AF_LOCAL, SOCK_STREAM, 0, fds) != 0)
return dex_future_new_reject (G_IO_ERROR,
G_IO_ERROR_NOT_CONNECTED,
"Failed to create socketpair");
/* Set FDs non-blocking so that we can use them from main
* context iteration without blocking.
*/
g_unix_set_fd_nonblocking (fds[0], TRUE, NULL);
g_unix_set_fd_nonblocking (fds[1], TRUE, NULL);
/* @child_no is assigned the FD the child will receive. We can
* use that to set the environment variable of the control FD.
*/
child_no = sysprof_spawnable_take_fd (spawnable, fds[1], -1);
child_no_str = g_strdup_printf ("%d", child_no);
sysprof_spawnable_setenv (spawnable, "SYSPROF_CONTROL_FD", child_no_str);
if (!(sock = g_socket_new_from_fd (fds[0], NULL)))
{
close (fds[0]);
return dex_future_new_reject (G_IO_ERROR,
G_IO_ERROR_NOT_CONNECTED,
"Failed to create socket from FD");
}
self->connection = G_UNIX_CONNECTION (g_socket_connection_factory_create_connection (sock));
finish:
return dex_future_new_for_boolean (TRUE);
}
typedef struct _SysprofControlfdRecording
{
GIOStream *stream;
SysprofRecording *recording;
DexFuture *cancellable;
GArray *source_ids;
} SysprofControlfdRecording;
static void
sysprof_controlfd_recording_free (gpointer data)
{
SysprofControlfdRecording *state = data;
dex_clear (&state->cancellable);
g_clear_pointer (&state->source_ids, g_array_unref);
g_clear_object (&state->recording);
g_clear_object (&state->stream);
g_free (state);
}
static bool
sysprof_controlfd_instrument_frame_cb (gconstpointer data,
gsize *length,
gpointer user_data)
{
const SysprofCaptureFrame *fr = data;
RingData *ring_data = user_data;
g_assert (ring_data != NULL);
g_assert (ring_data->source_ids != NULL);
g_assert (ring_data->writer != NULL);
g_assert (ring_data->id > 0);
if G_UNLIKELY (*length < sizeof *fr ||
*length < fr->len ||
fr->type >= SYSPROF_CAPTURE_FRAME_LAST)
return G_SOURCE_REMOVE;
_sysprof_capture_writer_add_raw (ring_data->writer, fr);
*length = fr->len;
return G_SOURCE_CONTINUE;
}
static DexFuture *
sysprof_controlfd_instrument_record_fiber (gpointer user_data)
{
SysprofControlfdRecording *state = user_data;
g_autoptr(SysprofCaptureWriter) temp_writer = NULL;
SysprofCaptureWriter *writer;
g_autoptr(GError) error = NULL;
g_autofd int mem_fd = -1;
GInputStream *input;
g_assert (state != NULL);
g_assert (SYSPROF_IS_RECORDING (state->recording));
g_assert (DEX_IS_CANCELLABLE (state->cancellable));
g_assert (state->source_ids != NULL);
input = g_io_stream_get_input_stream (G_IO_STREAM (state->stream));
if (!(mem_fd = sysprof_memfd_create ("[controlfd-memfd]")))
return dex_future_new_for_errno (errno);
temp_writer = sysprof_capture_writer_new_from_fd (g_steal_fd (&mem_fd), 0);
writer = _sysprof_recording_writer (state->recording);
for (;;)
{
g_autoptr(DexFuture) future = dex_input_stream_read_bytes (input, 10, 0);
g_autoptr(MappedRingBuffer) ring_buffer = NULL;
g_autoptr(GBytes) bytes = NULL;
const guint8 *data;
gsize len;
dex_await (dex_future_any (dex_ref (future),
dex_ref (state->cancellable),
NULL),
&error);
if (error != NULL)
goto handle_error;
if (!(bytes = dex_await_boxed (dex_ref (future), &error)))
goto handle_error;
data = g_bytes_get_data (bytes, &len);
if (len != 10 || memcmp (data, "CreatRing\0", 10) != 0)
break;
if ((ring_buffer = mapped_ring_buffer_new_reader (0)))
{
int fd = mapped_ring_buffer_get_fd (ring_buffer);
RingData *ring_data;
ring_data = g_new0 (RingData, 1);
ring_data->writer = sysprof_capture_writer_ref (temp_writer);
ring_data->source_ids = g_array_ref (state->source_ids);
ring_data->id = mapped_ring_buffer_create_source_full (ring_buffer,
sysprof_controlfd_instrument_frame_cb,
ring_data,
(GDestroyNotify)ring_data_free);
g_array_append_val (state->source_ids, ring_data->id);
g_unix_connection_send_fd (G_UNIX_CONNECTION (state->stream), fd, NULL, NULL);
}
}
handle_error:
while (state->source_ids->len > 0)
{
guint id = g_array_index (state->source_ids, guint, state->source_ids->len-1);
state->source_ids->len--;
g_source_remove (id);
}
if (temp_writer != NULL)
{
g_autoptr(SysprofCaptureReader) reader = sysprof_capture_writer_create_reader (temp_writer);
if (reader != NULL)
sysprof_capture_writer_cat (writer, reader);
}
if (error != NULL)
return dex_future_new_for_error (g_steal_pointer (&error));
return dex_future_new_for_boolean (TRUE);
}
static void
_g_clear_source (gpointer data)
{
guint *id = data;
if (*id != 0)
g_source_remove (*id);
}
static DexFuture *
sysprof_controlfd_instrument_record (SysprofInstrument *instrument,
SysprofRecording *recording,
GCancellable *cancellable)
{
SysprofControlfdInstrument *self = (SysprofControlfdInstrument *)instrument;
SysprofControlfdRecording *state;
SysprofSpawnable *spawnable;
g_assert (SYSPROF_IS_CONTROLFD_INSTRUMENT (self));
g_assert (SYSPROF_IS_RECORDING (recording));
if (!(spawnable = _sysprof_recording_get_spawnable (recording)))
return dex_future_new_for_boolean (TRUE);
state = g_new0 (SysprofControlfdRecording, 1);
state->recording = g_object_ref (recording);
state->stream = g_object_ref (G_IO_STREAM (self->connection));
state->cancellable = dex_cancellable_new_from_cancellable (cancellable);
state->source_ids = g_array_new (FALSE, FALSE, sizeof (guint));
g_array_set_clear_func (state->source_ids, _g_clear_source);
return dex_scheduler_spawn (NULL, 0,
sysprof_controlfd_instrument_record_fiber,
state,
sysprof_controlfd_recording_free);
}
static void
sysprof_controlfd_instrument_finalize (GObject *object)
{
SysprofControlfdInstrument *self = (SysprofControlfdInstrument *)object;
g_clear_object (&self->connection);
G_OBJECT_CLASS (sysprof_controlfd_instrument_parent_class)->finalize (object);
}
#endif
static void
sysprof_controlfd_instrument_class_init (SysprofControlfdInstrumentClass *klass)
{
#ifdef G_OS_UNIX
GObjectClass *object_class = G_OBJECT_CLASS (klass);
SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass);
object_class->finalize = sysprof_controlfd_instrument_finalize;
instrument_class->prepare = sysprof_controlfd_instrument_prepare;
instrument_class->record = sysprof_controlfd_instrument_record;
#endif
}
static void
sysprof_controlfd_instrument_init (SysprofControlfdInstrument *self)
{
}
SysprofInstrument *
_sysprof_controlfd_instrument_new (void)
{
#ifndef G_OS_UNIX
g_warning_once ("SysprofControlfdInstrument not supported on this platform");
#endif
return g_object_new (SYSPROF_TYPE_CONTROLFD_INSTRUMENT, NULL);
}

View File

@ -0,0 +1,32 @@
/* sysprof-cpu-info-private.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-cpu-info.h"
G_BEGIN_DECLS
void _sysprof_cpu_info_set_core_id (SysprofCpuInfo *self,
guint core_id);
void _sysprof_cpu_info_set_model_name (SysprofCpuInfo *self,
const char *model_name);
G_END_DECLS

View File

@ -0,0 +1,186 @@
/* sysprof-cpu-info.c
*
* Copyright 2023 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 "sysprof-cpu-info.h"
struct _SysprofCpuInfo
{
GObject parent_instance;
char *model_name;
guint id;
guint core_id;
};
enum {
PROP_0,
PROP_ID,
PROP_CORE_ID,
PROP_MODEL_NAME,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofCpuInfo, sysprof_cpu_info, G_TYPE_OBJECT)
static GParamSpec *properties [N_PROPS];
static void
sysprof_cpu_info_finalize (GObject *object)
{
SysprofCpuInfo *self = (SysprofCpuInfo *)object;
g_clear_pointer (&self->model_name, g_free);
G_OBJECT_CLASS (sysprof_cpu_info_parent_class)->finalize (object);
}
static void
sysprof_cpu_info_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofCpuInfo *self = SYSPROF_CPU_INFO (object);
switch (prop_id)
{
case PROP_ID:
g_value_set_uint (value, self->id);
break;
case PROP_CORE_ID:
g_value_set_uint (value, self->core_id);
break;
case PROP_MODEL_NAME:
g_value_set_string (value, self->model_name);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_cpu_info_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofCpuInfo *self = SYSPROF_CPU_INFO (object);
switch (prop_id)
{
case PROP_ID:
self->id = g_value_get_uint (value);
break;
case PROP_CORE_ID:
self->core_id = g_value_get_uint (value);
break;
case PROP_MODEL_NAME:
self->model_name = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_cpu_info_class_init (SysprofCpuInfoClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_cpu_info_finalize;
object_class->get_property = sysprof_cpu_info_get_property;
object_class->set_property = sysprof_cpu_info_set_property;
properties[PROP_ID] =
g_param_spec_uint ("id", NULL, NULL,
0, G_MAXUINT, 0,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
properties[PROP_CORE_ID] =
g_param_spec_uint ("core-id", NULL, NULL,
0, G_MAXUINT, 0,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
properties[PROP_MODEL_NAME] =
g_param_spec_string ("model-name", NULL, NULL,
NULL,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_cpu_info_init (SysprofCpuInfo *self)
{
}
const char *
sysprof_cpu_info_get_model_name (SysprofCpuInfo *self)
{
g_return_val_if_fail (SYSPROF_IS_CPU_INFO (self), NULL);
return self->model_name;
}
guint
sysprof_cpu_info_get_id (SysprofCpuInfo *self)
{
g_return_val_if_fail (SYSPROF_IS_CPU_INFO (self), 0);
return self->id;
}
void
_sysprof_cpu_info_set_model_name (SysprofCpuInfo *self,
const char *model_name)
{
g_return_if_fail (SYSPROF_IS_CPU_INFO (self));
if (g_set_str (&self->model_name, model_name))
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL_NAME]);
}
guint
sysprof_cpu_info_get_core_id (SysprofCpuInfo *self)
{
g_return_val_if_fail (SYSPROF_IS_CPU_INFO (self), 0);
return self->core_id;
}
void
_sysprof_cpu_info_set_core_id (SysprofCpuInfo *self,
guint core_id)
{
g_return_if_fail (SYSPROF_IS_CPU_INFO (self));
if (self->core_id != core_id)
{
self->core_id = core_id;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CORE_ID]);
}
}

View File

@ -0,0 +1,41 @@
/* sysprof-cpu-info.h
*
* Copyright 2023 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
*/
#pragma once
#include <glib-object.h>
#include <sysprof-capture.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_CPU_INFO (sysprof_cpu_info_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofCpuInfo, sysprof_cpu_info, SYSPROF, CPU_INFO, GObject)
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_cpu_info_get_id (SysprofCpuInfo *self);
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_cpu_info_get_core_id (SysprofCpuInfo *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_cpu_info_get_model_name (SysprofCpuInfo *self);
G_END_DECLS

View File

@ -0,0 +1,401 @@
/* sysprof-cpu-usage.c
*
* Copyright 2023 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 <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <glib-unix.h>
#include <glib/gstdio.h>
#include "line-reader-private.h"
#include "sysprof-cpu-usage.h"
#include "sysprof-instrument-private.h"
#include "sysprof-recording-private.h"
#define PROC_STAT_BUF_SIZE (4096*4)
struct _SysprofCpuUsage
{
SysprofInstrument parent_instance;
};
struct _SysprofCpuUsageClass
{
SysprofInstrumentClass parent_class;
};
G_DEFINE_FINAL_TYPE (SysprofCpuUsage, sysprof_cpu_usage, SYSPROF_TYPE_INSTRUMENT)
typedef struct _CpuInfo
{
int counter_base;
double total;
glong last_user;
glong last_idle;
glong last_system;
glong last_nice;
glong last_iowait;
glong last_irq;
glong last_softirq;
glong last_steal;
glong last_guest;
glong last_guest_nice;
} CpuInfo;
typedef struct _CpuFreq
{
gint64 max;
int stat_fd;
char buf[116];
} CpuFreq;
typedef struct _Record
{
SysprofRecording *recording;
DexFuture *cancellable;
} Record;
static void
record_free (gpointer data)
{
Record *record = data;
g_clear_object (&record->recording);
dex_clear (&record->cancellable);
g_free (record);
}
static void
freq_info_clear (gpointer data)
{
CpuFreq *cpu_freq = data;
g_clear_fd (&cpu_freq->stat_fd, NULL);
}
static double
get_cpu_freq (int stat_fd,
guint cpu,
double max,
char *buf,
gssize len)
{
gint64 val;
if (stat_fd == -1)
return .0;
if (len <= 0)
return .0;
buf[len] = 0;
g_strchug (buf);
val = g_ascii_strtoll (buf, NULL, 10);
val = CLAMP (val, .0, max);
return (double)val / max * 100.;
}
static DexFuture *
sysprof_cpu_usage_record_fiber (gpointer user_data)
{
Record *record = user_data;
g_autoptr(GArray) cpu_info = NULL;
g_autoptr(GArray) freq_info = NULL;
g_autofd int stat_fd = -1;
g_autofree char *read_buffer = NULL;
g_autofree SysprofCaptureCounterValue *values = NULL;
g_autofree SysprofCaptureCounter *counters = NULL;
g_autofree guint *ids = NULL;
SysprofCaptureCounter *counter;
SysprofCaptureWriter *writer;
guint n_cpu;
g_assert (record != NULL);
g_assert (SYSPROF_IS_RECORDING (record->recording));
g_assert (DEX_IS_FUTURE (record->cancellable));
writer = _sysprof_recording_writer (record->recording);
n_cpu = g_get_num_processors ();
stat_fd = open ("/proc/stat", O_RDONLY|O_CLOEXEC);
g_unix_set_fd_nonblocking (stat_fd, TRUE, NULL);
read_buffer = g_malloc (PROC_STAT_BUF_SIZE);
counters = g_new0 (SysprofCaptureCounter, (n_cpu * 2) + 1);
ids = g_new0 (guint, (n_cpu * 2) + 1);
values = g_new0 (SysprofCaptureCounterValue, (n_cpu * 2) + 1);
cpu_info = g_array_new (FALSE, TRUE, sizeof (CpuInfo));
g_array_set_size (cpu_info, n_cpu);
freq_info = g_array_new (FALSE, TRUE, sizeof (CpuFreq));
g_array_set_clear_func (freq_info, freq_info_clear);
/* Create counter information for all of our counters that we will need
* to submit in upcoming parses.
*/
for (guint i = 0; i < n_cpu; i++)
{
g_autofree char *max_path = g_strdup_printf ("/sys/devices/system/cpu/cpu%u/cpufreq/scaling_max_freq", i);
g_autofree char *cur_path = g_strdup_printf ("/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i);
g_autofree char *max_value = NULL;
CpuFreq cf;
ids[i*2] = sysprof_capture_writer_request_counter (writer, 1);
ids[i*2+1] = sysprof_capture_writer_request_counter (writer, 1);
counter = &counters[i*2];
counter->id = ids[i*2];
counter->type = SYSPROF_CAPTURE_COUNTER_DOUBLE;
counter->value.vdbl = 0;
g_strlcpy (counter->category, "CPU Percent", sizeof counter->category);
g_snprintf (counter->name, sizeof counter->name, "Total CPU %d", i);
g_snprintf (counter->description, sizeof counter->description,
"Total CPU usage %d", i);
counter = &counters[i*2+1];
counter->id = ids[i*2+1];
counter->type = SYSPROF_CAPTURE_COUNTER_DOUBLE;
counter->value.vdbl = 0;
g_strlcpy (counter->category, "CPU Frequency", sizeof counter->category);
g_snprintf (counter->name, sizeof counter->name, "CPU %d", i);
g_snprintf (counter->description, sizeof counter->description,
"Frequency of CPU %d", i);
cf.stat_fd = open (cur_path, O_RDONLY|O_CLOEXEC);
g_unix_set_fd_nonblocking (cf.stat_fd, TRUE, NULL);
cf.buf[0] = 0;
if (g_file_get_contents (max_path, &max_value, NULL, NULL))
cf.max = g_ascii_strtoll (max_value, NULL, 10);
else
cf.max = 0;
g_array_append_val (freq_info, cf);
}
/* Now create our combined counter */
ids[n_cpu*2] = sysprof_capture_writer_request_counter (writer, 1);
counter = &counters[n_cpu*2];
counter->id = ids[n_cpu*2];
counter->type = SYSPROF_CAPTURE_COUNTER_DOUBLE;
counter->value.vdbl = 0;
g_strlcpy (counter->category, "CPU Percent", sizeof counter->category);
g_snprintf (counter->name, sizeof counter->name, "Combined");
g_snprintf (counter->description, sizeof counter->description, "Combined CPU usage");
/* Register all the counters as a group */
sysprof_capture_writer_define_counters (writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
counters,
n_cpu * 2 + 1);
for (;;)
{
g_autoptr(GPtrArray) futures = g_ptr_array_new_with_free_func (dex_unref);
g_autoptr(DexFuture) cpu_future = NULL;
LineReader reader;
glong total_usage = 0;
gsize line_len;
char *line;
/* First collect all our reads and then wait for them to finish before
* parsing in a pass. With io_uring, this lets us coalesce all the lseek
* and reads into a single set of iops.
*/
for (guint i = 0; i < n_cpu; i++)
{
CpuFreq *cf = &g_array_index (freq_info, CpuFreq, i);
g_ptr_array_add (futures, dex_aio_read (NULL, cf->stat_fd, cf->buf, sizeof cf->buf - 1, 0));
}
cpu_future = dex_aio_read (NULL, stat_fd, read_buffer, PROC_STAT_BUF_SIZE, 0);
g_ptr_array_add (futures, dex_ref (cpu_future));
if (!dex_await (dex_future_first (dex_ref (record->cancellable),
dex_future_allv ((DexFuture **)futures->pdata, futures->len),
NULL),
NULL))
break;
/* Now parse all the contents of the stat files which should be
* populated in the various files.
*/
line_reader_init (&reader, read_buffer, dex_await_int64 (dex_ref (cpu_future), NULL));
while ((line = line_reader_next (&reader, &line_len)))
{
CpuInfo *ci;
char cpu[64];
glong user;
glong sys;
glong nice;
glong idle;
int id;
int ret;
glong iowait;
glong irq;
glong softirq;
glong steal;
glong guest;
glong guest_nice;
glong user_calc;
glong system_calc;
glong nice_calc;
glong idle_calc;
glong iowait_calc;
glong irq_calc;
glong softirq_calc;
glong steal_calc;
glong guest_calc;
glong guest_nice_calc;
glong total;
line[line_len] = 0;
/* CPU lines come first, short-circuit after */
if (!g_str_has_prefix (line, "cpu"))
break;
/* First line is "cpu ..." */
if (!g_ascii_isdigit (line[3]))
continue;
/* Parse the various counters in order */
user = nice = sys = idle = id = 0;
ret = sscanf (line, "%63s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
cpu, &user, &nice, &sys, &idle,
&iowait, &irq, &softirq, &steal, &guest, &guest_nice);
if (ret != 11)
continue;
/* Get the CPU identifier */
ret = sscanf (cpu, "cpu%d", &id);
if (ret != 1 || id < 0 || id >= n_cpu)
continue;
ci = &g_array_index (cpu_info, CpuInfo, id);
user_calc = user - ci->last_user;
nice_calc = nice - ci->last_nice;
system_calc = sys - ci->last_system;
idle_calc = idle - ci->last_idle;
iowait_calc = iowait - ci->last_iowait;
irq_calc = irq - ci->last_irq;
softirq_calc = softirq - ci->last_softirq;
steal_calc = steal - ci->last_steal;
guest_calc = guest - ci->last_guest;
guest_nice_calc = guest_nice - ci->last_guest_nice;
total = user_calc + nice_calc + system_calc + idle_calc + iowait_calc + irq_calc + softirq_calc + steal_calc + guest_calc + guest_nice_calc;
ci->total = ((total - idle_calc) / (double)total) * 100.;
ci->last_user = user;
ci->last_nice = nice;
ci->last_idle = idle;
ci->last_system = sys;
ci->last_iowait = iowait;
ci->last_irq = irq;
ci->last_softirq = softirq;
ci->last_steal = steal;
ci->last_guest = guest;
ci->last_guest_nice = guest_nice;
}
/* Publish counters to the capture file */
for (guint i = 0; i < n_cpu; i++)
{
const CpuInfo *ci = &g_array_index (cpu_info, CpuInfo, i);
CpuFreq *cf = &g_array_index (freq_info, CpuFreq, i);
DexFuture *freq_future = g_ptr_array_index (futures, i);
gssize len = dex_await_int64 (dex_ref (freq_future), NULL);
values[i*2].vdbl = ci->total;
values[i*2+1].vdbl = get_cpu_freq (cf->stat_fd, i, cf->max, cf->buf, len);
total_usage += ci->total;
}
values[n_cpu*2].vdbl = total_usage / (double)n_cpu;
sysprof_capture_writer_set_counters (writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
ids,
values,
n_cpu * 2 + 1);
/* Wait for cancellation or ⅕ second */
dex_await (dex_future_first (dex_ref (record->cancellable),
dex_timeout_new_usec (G_USEC_PER_SEC / 5),
NULL),
NULL);
if (dex_future_get_status (record->cancellable) != DEX_FUTURE_STATUS_PENDING)
break;
}
return dex_future_new_for_boolean (TRUE);
}
static DexFuture *
sysprof_cpu_usage_record (SysprofInstrument *instrument,
SysprofRecording *recording,
GCancellable *cancellable)
{
Record *record;
g_assert (SYSPROF_IS_CPU_USAGE (instrument));
g_assert (SYSPROF_IS_RECORDING (recording));
g_assert (G_IS_CANCELLABLE (cancellable));
record = g_new0 (Record, 1);
record->recording = g_object_ref (recording);
record->cancellable = dex_cancellable_new_from_cancellable (cancellable);
return dex_scheduler_spawn (NULL, 0,
sysprof_cpu_usage_record_fiber,
record,
record_free);
}
static void
sysprof_cpu_usage_class_init (SysprofCpuUsageClass *klass)
{
SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass);
instrument_class->record = sysprof_cpu_usage_record;
}
static void
sysprof_cpu_usage_init (SysprofCpuUsage *self)
{
}
SysprofInstrument *
sysprof_cpu_usage_new (void)
{
return g_object_new (SYSPROF_TYPE_CPU_USAGE, NULL);
}

View File

@ -0,0 +1,42 @@
/* sysprof-cpu-usage.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-instrument.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_CPU_USAGE (sysprof_cpu_usage_get_type())
#define SYSPROF_IS_CPU_USAGE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_CPU_USAGE)
#define SYSPROF_CPU_USAGE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_CPU_USAGE, SysprofCpuUsage)
#define SYSPROF_CPU_USAGE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_CPU_USAGE, SysprofCpuUsageClass)
typedef struct _SysprofCpuUsage SysprofCpuUsage;
typedef struct _SysprofCpuUsageClass SysprofCpuUsageClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_cpu_usage_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
SysprofInstrument *sysprof_cpu_usage_new (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCpuUsage, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,35 @@
/* sysprof-descendants-model-private.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-callgraph.h"
#include "sysprof-symbol.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DESCENDANTS_MODEL (sysprof_descendants_model_get_type())
G_DECLARE_FINAL_TYPE (SysprofDescendantsModel, sysprof_descendants_model, SYSPROF, DESCENDANTS_MODEL, GObject)
GListModel *_sysprof_descendants_model_new (SysprofCallgraph *callgraph,
SysprofSymbol *symbol);
G_END_DECLS

View File

@ -0,0 +1,257 @@
/* sysprof-descendants-model.c
*
* Copyright 2023 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 "sysprof-callgraph-private.h"
#include "sysprof-callgraph-frame-private.h"
#include "sysprof-descendants-model-private.h"
#include "sysprof-document-private.h"
#include "sysprof-symbol-private.h"
#define MAX_STACK_DEPTH 128
struct _SysprofDescendantsModel
{
GObject parent_instance;
SysprofCallgraph *callgraph;
SysprofSymbol *symbol;
SysprofCallgraphNode root;
};
static GType
sysprof_descendants_model_get_item_type (GListModel *model)
{
return SYSPROF_TYPE_CALLGRAPH_FRAME;
}
static guint
sysprof_descendants_model_get_n_items (GListModel *model)
{
return 1;
}
static gpointer
sysprof_descendants_model_get_item (GListModel *model,
guint position)
{
SysprofDescendantsModel *self = SYSPROF_DESCENDANTS_MODEL (model);
if (position != 0)
return NULL;
return _sysprof_callgraph_frame_new_for_node (self->callgraph, G_OBJECT (self), &self->root);
}
static void
list_model_iface_init (GListModelInterface *iface)
{
iface->get_item_type = sysprof_descendants_model_get_item_type;
iface->get_n_items = sysprof_descendants_model_get_n_items;
iface->get_item = sysprof_descendants_model_get_item;
}
G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDescendantsModel, sysprof_descendants_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
static void
sysprof_descendants_model_finalize (GObject *object)
{
SysprofDescendantsModel *self = (SysprofDescendantsModel *)object;
_sysprof_callgraph_node_free (&self->root, FALSE);
g_clear_object (&self->callgraph);
g_clear_object (&self->symbol);
G_OBJECT_CLASS (sysprof_descendants_model_parent_class)->finalize (object);
}
static void
sysprof_descendants_model_class_init (SysprofDescendantsModelClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_descendants_model_finalize;
}
static void
sysprof_descendants_model_init (SysprofDescendantsModel *self)
{
}
static SysprofCallgraphNode *
sysprof_descendants_model_add_trace (SysprofDescendantsModel *self,
SysprofSymbol **symbols,
guint n_symbols)
{
SysprofCallgraphNode *parent = NULL;
g_assert (SYSPROF_IS_DESCENDANTS_MODEL (self));
g_assert (symbols != NULL);
g_assert (n_symbols > 0);
parent = &self->root;
for (guint i = n_symbols; i > 0; i--)
{
SysprofSymbol *symbol = symbols[i-1];
SysprofCallgraphNode *node = NULL;
SysprofCallgraphSummary *summary;
/* Try to find @symbol within the children of @parent */
for (SysprofCallgraphNode *iter = parent->children;
iter != NULL;
iter = iter->next)
{
g_assert (iter != NULL);
g_assert (iter->summary != NULL);
g_assert (iter->summary->symbol != NULL);
g_assert (symbol != NULL);
if (_sysprof_symbol_equal (iter->summary->symbol, symbol))
{
node = iter;
goto next_symbol;
}
}
if (!(summary = g_hash_table_lookup (self->callgraph->symbol_to_summary, symbol)))
{
node = parent;
goto next_symbol;
}
/* Otherwise create a new node */
node = g_new0 (SysprofCallgraphNode, 1);
node->summary = summary;
node->parent = parent;
node->next = parent->children;
if (parent->children)
parent->children->prev = node;
parent->children = node;
g_assert (node->summary != NULL);
next_symbol:
parent = node;
}
return parent;
}
static void
sysprof_descendants_model_add_traceable (SysprofDescendantsModel *self,
SysprofDocument *document,
SysprofDocumentTraceable *traceable,
SysprofSymbol *from_symbol,
gboolean include_threads)
{
SysprofAddressContext final_context;
SysprofSymbol **symbols;
SysprofSymbolKind kind;
guint stack_depth;
guint n_symbols;
g_assert (SYSPROF_IS_DESCENDANTS_MODEL (self));
g_assert (SYSPROF_IS_DOCUMENT (document));
g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable));
g_assert (SYSPROF_IS_SYMBOL (from_symbol));
stack_depth = MIN (MAX_STACK_DEPTH, sysprof_document_traceable_get_stack_depth (traceable));
symbols = g_alloca (sizeof (SysprofSymbol *) * (stack_depth + 2));
n_symbols = sysprof_document_symbolize_traceable (document, traceable, symbols, stack_depth, &final_context);
kind = sysprof_symbol_get_kind (from_symbol);
if (kind == SYSPROF_SYMBOL_KIND_PROCESS || kind == SYSPROF_SYMBOL_KIND_THREAD)
{
int pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable));
if (include_threads)
{
int thread_id = sysprof_document_traceable_get_thread_id (traceable);
symbols[n_symbols++] = _sysprof_document_thread_symbol (document, pid, thread_id);
}
symbols[n_symbols++] = _sysprof_document_process_symbol (document, pid);
}
for (guint i = n_symbols; i > 0; i--)
{
SysprofSymbol *symbol = symbols[i-1];
n_symbols--;
if (_sysprof_symbol_equal (symbol, from_symbol))
break;
}
if (n_symbols > 0)
{
SysprofCallgraphNode *node;
node = sysprof_descendants_model_add_trace (self, symbols, n_symbols);
if (node && self->callgraph->augment_func)
self->callgraph->augment_func (self->callgraph,
node,
SYSPROF_DOCUMENT_FRAME (traceable),
FALSE,
self->callgraph->augment_func_data);
}
}
GListModel *
_sysprof_descendants_model_new (SysprofCallgraph *callgraph,
SysprofSymbol *symbol)
{
SysprofDescendantsModel *self;
g_autoptr(SysprofDocument) document = NULL;
g_autoptr(GListModel) model = NULL;
gboolean include_threads;
guint n_items;
g_return_val_if_fail (SYSPROF_IS_CALLGRAPH (callgraph), NULL);
g_return_val_if_fail (SYSPROF_IS_SYMBOL (symbol), NULL);
model = sysprof_callgraph_list_traceables_for_symbol (callgraph, symbol);
document = g_object_ref (callgraph->document);
self = g_object_new (SYSPROF_TYPE_DESCENDANTS_MODEL, NULL);
self->callgraph = g_object_ref (callgraph);
self->symbol = g_object_ref (symbol);
self->root.summary = g_hash_table_lookup (callgraph->symbol_to_summary, symbol);
g_assert (self->root.summary != NULL);
g_assert (_sysprof_symbol_equal (self->root.summary->symbol, symbol));
include_threads = (callgraph->flags & SYSPROF_CALLGRAPH_FLAGS_INCLUDE_THREADS) != 0;
n_items = g_list_model_get_n_items (model);
for (guint i = 0; i < n_items; i++)
{
g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (model, i);
sysprof_descendants_model_add_traceable (self, document, traceable, symbol, include_threads);
}
return G_LIST_MODEL (self);
}

View File

@ -0,0 +1,31 @@
/* sysprof-diagnostic-private.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-diagnostic.h"
G_BEGIN_DECLS
SysprofDiagnostic *_sysprof_diagnostic_new (char *domain,
char *message,
gboolean fatal);
G_END_DECLS

View File

@ -0,0 +1,151 @@
/* sysprof-diagnostic.c
*
* Copyright 2023 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 "sysprof-diagnostic-private.h"
struct _SysprofDiagnostic
{
GObject parent_instance;
GRefString *domain;
char *message;
guint fatal : 1;
};
enum {
PROP_0,
PROP_DOMAIN,
PROP_MESSAGE,
PROP_FATAL,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDiagnostic, sysprof_diagnostic, G_TYPE_OBJECT)
static GParamSpec *properties [N_PROPS];
static void
sysprof_diagnostic_finalize (GObject *object)
{
SysprofDiagnostic *self = (SysprofDiagnostic *)object;
g_clear_pointer (&self->domain, g_free);
g_clear_pointer (&self->message, g_free);
G_OBJECT_CLASS (sysprof_diagnostic_parent_class)->finalize (object);
}
static void
sysprof_diagnostic_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDiagnostic *self = SYSPROF_DIAGNOSTIC (object);
switch (prop_id)
{
case PROP_DOMAIN:
g_value_set_string (value, self->domain);
break;
case PROP_MESSAGE:
g_value_set_string (value, self->message);
break;
case PROP_FATAL:
g_value_set_boolean (value, self->fatal);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_diagnostic_class_init (SysprofDiagnosticClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_diagnostic_finalize;
object_class->get_property = sysprof_diagnostic_get_property;
properties [PROP_DOMAIN] =
g_param_spec_string ("domain", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_MESSAGE] =
g_param_spec_string ("message", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_FATAL] =
g_param_spec_boolean ("fatal", NULL, NULL,
FALSE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_diagnostic_init (SysprofDiagnostic *self)
{
}
const char *
sysprof_diagnostic_get_domain (SysprofDiagnostic *self)
{
g_return_val_if_fail (SYSPROF_IS_DIAGNOSTIC (self), NULL);
return self->domain;
}
const char *
sysprof_diagnostic_get_message (SysprofDiagnostic *self)
{
g_return_val_if_fail (SYSPROF_IS_DIAGNOSTIC (self), NULL);
return self->message;
}
gboolean
sysprof_diagnostic_get_fatal (SysprofDiagnostic *self)
{
g_return_val_if_fail (SYSPROF_IS_DIAGNOSTIC (self), FALSE);
return self->fatal;
}
SysprofDiagnostic *
_sysprof_diagnostic_new (char *domain,
char *message,
gboolean fatal)
{
SysprofDiagnostic *self;
self = g_object_new (SYSPROF_TYPE_DIAGNOSTIC, NULL);
self->message = message;
self->domain = domain;
self->fatal = !!fatal;
return self;
}

View File

@ -0,0 +1,41 @@
/* sysprof-diagnostic.h
*
* Copyright 2023 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
*/
#pragma once
#include <glib-object.h>
#include <sysprof-capture.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_DIAGNOSTIC (sysprof_diagnostic_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofDiagnostic, sysprof_diagnostic, SYSPROF, DIAGNOSTIC, GObject)
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_diagnostic_get_domain (SysprofDiagnostic *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_diagnostic_get_message (SysprofDiagnostic *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_diagnostic_get_fatal (SysprofDiagnostic *self);
G_END_DECLS

View File

@ -0,0 +1,442 @@
/* sysprof-disk-usage.c
*
* Copyright 2023 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 <fcntl.h>
#include <glib/gstdio.h>
#include "sysprof-disk-usage.h"
#include "sysprof-instrument-private.h"
#include "sysprof-recording-private.h"
#include "line-reader-private.h"
#define ADD_FROM_CHAR(v, c) (((v)*10L)+((c)-'0'))
struct _SysprofDiskUsage
{
SysprofInstrument parent_instance;
};
struct _SysprofDiskUsageClass
{
SysprofInstrumentClass parent_class;
};
typedef struct _DiskUsage
{
/* Counter IDs */
guint reads_total_id;
guint writes_total_id;
/* Index where values are stored */
guint values_at;
char device[32];
gint64 reads_total;
gint64 reads_merged;
gint64 reads_sectors;
gint64 reads_msec;
gint64 writes_total;
gint64 writes_merged;
gint64 writes_sectors;
gint64 writes_msec;
gint64 iops_active;
gint64 iops_msec;
gint64 iops_msec_weighted;
} DiskUsage;
typedef enum _Column
{
/* -2 */ COLUMN_MAJOR,
/* -1 */ COLUMN_MINOR,
/* 0 */ COLUMN_NAME,
/* 1 */ COLUMN_READS_TOTAL,
/* 2 */ COLUMN_READS_MERGED,
/* 3 */ COLUMN_READS_SECTORS,
/* 4 */ COLUMN_READS_MSEC,
/* 5 */ COLUMN_WRITES_TOTAL,
/* 6 */ COLUMN_WRITES_MERGED,
/* 7 */ COLUMN_WRITES_SECTORS,
/* 8 */ COLUMN_WRITES_MSEC,
/* 9 */ COLUMN_IOPS_ACTIVE,
/* 10 */ COLUMN_IOPS_MSEC,
/* 11 */ COLUMN_IOPS_MSEC_WEIGHTED,
} Column;
G_DEFINE_FINAL_TYPE (SysprofDiskUsage, sysprof_disk_usage, SYSPROF_TYPE_INSTRUMENT)
typedef struct _Record
{
SysprofRecording *recording;
DexFuture *cancellable;
GArray *devices;
GArray *ids;
GArray *values;
} Record;
static void
record_free (gpointer data)
{
Record *record = data;
g_clear_object (&record->recording);
g_clear_pointer (&record->devices, g_array_unref);
g_clear_pointer (&record->ids, g_array_unref);
g_clear_pointer (&record->values, g_array_unref);
dex_clear (&record->cancellable);
g_free (record);
}
static DiskUsage *
register_counters_by_name (Record *record,
const char *name)
{
static const SysprofCaptureCounterValue zeroval = {0};
SysprofCaptureCounter ctr[2] = {0};
SysprofCaptureWriter *writer;
DiskUsage ds = {0};
g_assert (record != NULL);
g_assert (name != NULL);
writer = _sysprof_recording_writer (record->recording);
ds.values_at = record->ids->len;
ds.reads_total_id = sysprof_capture_writer_request_counter (writer, 1);
ds.writes_total_id = sysprof_capture_writer_request_counter (writer, 1);
g_strlcpy (ds.device, name, sizeof ds.device);
g_strlcpy (ctr[0].category, "Disk", sizeof ctr[0].category);
g_snprintf (ctr[0].name, sizeof ctr[0].name, "Total Reads (%s)", name);
g_strlcpy (ctr[0].description, name, sizeof ctr[0].description);
ctr[0].id = ds.reads_total_id;
ctr[0].type = SYSPROF_CAPTURE_COUNTER_INT64;
ctr[0].value.v64 = 0;
g_strlcpy (ctr[1].category, "Disk", sizeof ctr[1].category);
g_snprintf (ctr[1].name, sizeof ctr[1].name, "Total Writes (%s)", name);
g_strlcpy (ctr[1].description, name, sizeof ctr[1].description);
ctr[1].id = ds.writes_total_id;
ctr[1].type = SYSPROF_CAPTURE_COUNTER_INT64;
ctr[1].value.v64 = 0;
sysprof_capture_writer_define_counters (writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
ctr,
G_N_ELEMENTS (ctr));
g_array_append_val (record->devices, ds);
g_array_append_val (record->ids, ds.reads_total_id);
g_array_append_val (record->ids, ds.writes_total_id);
g_array_append_val (record->values, zeroval);
g_array_append_val (record->values, zeroval);
return &g_array_index (record->devices, DiskUsage, record->devices->len-1);
}
static DiskUsage *
find_device_by_name (Record *record,
const char *name)
{
g_assert (record != NULL);
g_assert (record->devices != NULL);
g_assert (name != NULL);
for (guint i = 0; i < record->devices->len; i++)
{
DiskUsage *ds = &g_array_index (record->devices, DiskUsage, i);
if (strcmp (name, ds->device) == 0)
return ds;
}
return NULL;
}
static DexFuture *
sysprof_disk_usage_record_fiber (gpointer user_data)
{
g_autoptr(GByteArray) buf = NULL;
Record *record = user_data;
SysprofCaptureWriter *writer;
g_autofd int stat_fd = -1;
LineReader reader;
DiskUsage *combined;
gint64 combined_reads_total = 0;
gint64 combined_writes_total = 0;
gboolean skip_publish = TRUE;
g_assert (record != NULL);
g_assert (SYSPROF_IS_RECORDING (record->recording));
g_assert (DEX_IS_CANCELLABLE (record->cancellable));
buf = g_byte_array_new ();
g_byte_array_set_size (buf, 4096*4);
if (-1 == (stat_fd = open ("/proc/diskstats", O_RDONLY|O_CLOEXEC)))
return dex_future_new_for_errno (errno);
writer = _sysprof_recording_writer (record->recording);
register_counters_by_name (record, "Combined");
for (;;)
{
g_autoptr(DexFuture) read_future = NULL;
gssize n_read;
char *line;
gsize line_len;
/* Read a new copy of our diskstats or bail from our
* recording loop. If cancellation future rejects, then
* we also break out of our recording loop.
*/
read_future = dex_aio_read (NULL, stat_fd, buf->data, buf->len-1, 0);
if (!dex_await (dex_future_first (dex_ref (record->cancellable),
dex_ref (read_future),
NULL),
NULL))
break;
n_read = dex_await_int64 (dex_ref (read_future), NULL);
if (n_read < 0)
break;
line_reader_init (&reader, (char *)buf->data, n_read);
while ((line = line_reader_next (&reader, &line_len)))
{
DiskUsage ds = {0};
DiskUsage *found;
gint64 dummy = 0;
int column = COLUMN_MAJOR;
line[line_len] = 0;
/* Skip past initial space */
while (g_ascii_isspace (*line))
line++;
for (const char *ptr = line; *ptr; ptr++)
{
char ch;
/* Skip past space and advance to next column */
if (g_ascii_isspace (*ptr))
{
while (g_ascii_isspace (*ptr))
ptr++;
column++;
}
ch = *ptr;
switch (column)
{
case COLUMN_MAJOR:
case COLUMN_MINOR:
default:
dummy = ADD_FROM_CHAR (dummy, ch);
break;
case COLUMN_NAME:
{
guint j;
for (j = 0; j < sizeof ds.device && ds.device[j] != 0; j++) { /* Do Nothing */ }
if (j < sizeof ds.device)
ds.device[j] = ch;
ds.device[sizeof ds.device - 1] = 0;
break;
}
case COLUMN_READS_TOTAL:
ds.reads_total = ADD_FROM_CHAR (ds.reads_total, ch);
break;
case COLUMN_READS_MERGED:
ds.reads_merged = ADD_FROM_CHAR (ds.reads_merged, ch);
break;
case COLUMN_READS_SECTORS:
ds.reads_sectors = ADD_FROM_CHAR (ds.reads_sectors, ch);
break;
case COLUMN_READS_MSEC:
ds.reads_msec = ADD_FROM_CHAR (ds.reads_msec, ch);
break;
case COLUMN_WRITES_TOTAL:
ds.writes_total = ADD_FROM_CHAR (ds.writes_total, ch);
break;
case COLUMN_WRITES_MERGED:
ds.writes_merged = ADD_FROM_CHAR (ds.writes_merged, ch);
break;
case COLUMN_WRITES_SECTORS:
ds.writes_sectors = ADD_FROM_CHAR (ds.writes_sectors, ch);
break;
case COLUMN_WRITES_MSEC:
ds.writes_msec = ADD_FROM_CHAR (ds.writes_msec, ch);
break;
case COLUMN_IOPS_ACTIVE:
ds.iops_active = ADD_FROM_CHAR (ds.iops_active, ch);
break;
case COLUMN_IOPS_MSEC:
ds.iops_msec = ADD_FROM_CHAR (ds.iops_msec, ch);
break;
case COLUMN_IOPS_MSEC_WEIGHTED:
ds.iops_msec_weighted = ADD_FROM_CHAR (ds.iops_msec_weighted, ch);
break;
}
}
g_strstrip (ds.device);
if (ds.device[0])
{
guint p;
gint64 reads_total;
gint64 writes_total;
if (!(found = find_device_by_name (record, ds.device)))
found = register_counters_by_name (record, ds.device);
/* Calculate new value, based on diff from previous */
reads_total = ds.reads_total - found->reads_total;
writes_total = ds.writes_total - found->writes_total;
/* Update value for publishing */
p = found->values_at;
g_array_index (record->values, SysprofCaptureCounterValue, p).v64 = reads_total;
g_array_index (record->values, SysprofCaptureCounterValue, p+1).v64 = writes_total;
/* Update combined values */
combined_reads_total += reads_total;
combined_writes_total += writes_total;
/* Save current value for diff on next poll */
found->reads_total = ds.reads_total;
found->writes_total = ds.writes_total;
}
}
if ((combined = find_device_by_name (record, "Combined")))
{
/* TODO: It would be nice to not double count disk ops multiple
* times based on the parition, etc.
*
* For example: nvme0n1 nvme0n1p1 nvme0n1p2 nvme0n1p3 may get
* accounted multiple times even though they are all nvme0n1.
*
* The other option, is to just not do "Combined" counters.
*/
g_array_index (record->values,
SysprofCaptureCounterValue,
combined->values_at).v64
= combined_reads_total - combined->reads_total;
g_array_index (record->values,
SysprofCaptureCounterValue,
combined->values_at+1).v64
= combined_writes_total - combined->writes_total;
combined->reads_total = combined_reads_total;
combined->writes_total = combined_writes_total;
}
g_assert (record->ids->len == record->values->len);
if (skip_publish)
skip_publish = FALSE;
else
sysprof_capture_writer_set_counters (writer,
SYSPROF_CAPTURE_CURRENT_TIME,
-1,
-1,
(const guint *)(gpointer)record->ids->data,
(const SysprofCaptureCounterValue *)(gpointer)record->values->data,
record->ids->len);
dex_await (dex_future_first (dex_ref (record->cancellable),
dex_timeout_new_usec (G_USEC_PER_SEC / 2),
NULL),
NULL);
if (dex_future_get_status (record->cancellable) != DEX_FUTURE_STATUS_PENDING)
break;
}
return dex_future_new_for_boolean (TRUE);
}
static DexFuture *
sysprof_disk_usage_record (SysprofInstrument *instrument,
SysprofRecording *recording,
GCancellable *cancellable)
{
Record *record;
g_assert (SYSPROF_IS_INSTRUMENT (instrument));
g_assert (SYSPROF_IS_RECORDING (recording));
g_assert (G_IS_CANCELLABLE (cancellable));
record = g_new0 (Record, 1);
record->recording = g_object_ref (recording);
record->cancellable = dex_cancellable_new_from_cancellable (cancellable);
record->devices = g_array_new (FALSE, FALSE, sizeof (DiskUsage));
record->ids = g_array_new (FALSE, FALSE, sizeof (guint));
record->values = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounterValue));
return dex_scheduler_spawn (NULL, 0,
sysprof_disk_usage_record_fiber,
record,
record_free);
}
static void
sysprof_disk_usage_class_init (SysprofDiskUsageClass *klass)
{
SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass);
instrument_class->record = sysprof_disk_usage_record;
}
static void
sysprof_disk_usage_init (SysprofDiskUsage *self)
{
}
SysprofInstrument *
sysprof_disk_usage_new (void)
{
return g_object_new (SYSPROF_TYPE_DISK_USAGE, NULL);
}

View File

@ -0,0 +1,42 @@
/* sysprof-disk-usage.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-instrument.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DISK_USAGE (sysprof_disk_usage_get_type())
#define SYSPROF_IS_DISK_USAGE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DISK_USAGE)
#define SYSPROF_DISK_USAGE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DISK_USAGE, SysprofDiskUsage)
#define SYSPROF_DISK_USAGE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DISK_USAGE, SysprofDiskUsageClass)
typedef struct _SysprofDiskUsage SysprofDiskUsage;
typedef struct _SysprofDiskUsageClass SysprofDiskUsageClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_disk_usage_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
SysprofInstrument *sysprof_disk_usage_new (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDiskUsage, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,258 @@
/* sysprof-document-allocation.c
*
* Copyright 2023 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 <glib/gi18n.h>
#include "sysprof-document-frame-private.h"
#include "sysprof-document-allocation.h"
#include "sysprof-document-traceable.h"
struct _SysprofDocumentAllocation
{
SysprofDocumentFrame parent_instance;
};
struct _SysprofDocumentAllocationClass
{
SysprofDocumentFrameClass parent_class;
};
enum {
PROP_0,
PROP_ADDRESS,
PROP_IS_FREE,
PROP_SIZE,
PROP_STACK_DEPTH,
PROP_THREAD_ID,
N_PROPS
};
static guint
sysprof_document_allocation_get_stack_depth (SysprofDocumentTraceable *traceable)
{
const SysprofCaptureAllocation *allocation = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureAllocation);
return SYSPROF_DOCUMENT_FRAME_UINT16 (traceable, allocation->n_addrs);
}
static guint64
sysprof_document_allocation_get_stack_address (SysprofDocumentTraceable *traceable,
guint position)
{
const SysprofCaptureAllocation *allocation = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureAllocation);
return SYSPROF_DOCUMENT_FRAME_UINT16 (traceable, allocation->addrs[position]);
}
static guint
sysprof_document_allocation_get_stack_addresses (SysprofDocumentTraceable *traceable,
guint64 *addresses,
guint n_addresses)
{
const SysprofCaptureAllocation *allocation = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureAllocation);
guint depth = MIN (n_addresses, SYSPROF_DOCUMENT_FRAME_UINT16 (traceable, allocation->n_addrs));
for (guint i = 0; i < depth; i++)
addresses[i] = SYSPROF_DOCUMENT_FRAME_UINT64 (traceable, allocation->addrs[i]);
return depth;
}
static int
sysprof_document_allocation_get_thread_id (SysprofDocumentTraceable *traceable)
{
SysprofDocumentAllocation *self = (SysprofDocumentAllocation *)traceable;
const SysprofCaptureAllocation *allocation;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_ALLOCATION (self), 0);
allocation = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureAllocation);
return SYSPROF_DOCUMENT_FRAME_INT32 (self, allocation->tid);
}
static void
traceable_iface_init (SysprofDocumentTraceableInterface *iface)
{
iface->get_stack_depth = sysprof_document_allocation_get_stack_depth;
iface->get_stack_address = sysprof_document_allocation_get_stack_address;
iface->get_stack_addresses = sysprof_document_allocation_get_stack_addresses;
iface->get_thread_id = sysprof_document_allocation_get_thread_id;
}
G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocumentAllocation, sysprof_document_allocation, SYSPROF_TYPE_DOCUMENT_FRAME,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_DOCUMENT_TRACEABLE, traceable_iface_init))
static GParamSpec *properties [N_PROPS];
static void
sysprof_document_allocation_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentAllocation *self = SYSPROF_DOCUMENT_ALLOCATION (object);
switch (prop_id)
{
case PROP_ADDRESS:
g_value_set_uint64 (value, sysprof_document_allocation_get_address (self));
break;
case PROP_IS_FREE:
g_value_set_boolean (value, sysprof_document_allocation_is_free (self));
break;
case PROP_SIZE:
g_value_set_int64 (value, sysprof_document_allocation_get_size (self));
break;
case PROP_STACK_DEPTH:
g_value_set_uint (value, sysprof_document_traceable_get_stack_depth (SYSPROF_DOCUMENT_TRACEABLE (self)));
break;
case PROP_THREAD_ID:
g_value_set_int (value, sysprof_document_traceable_get_thread_id (SYSPROF_DOCUMENT_TRACEABLE (self)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_allocation_class_init (SysprofDocumentAllocationClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
SysprofDocumentFrameClass *document_frame_class = SYSPROF_DOCUMENT_FRAME_CLASS (klass);
object_class->get_property = sysprof_document_allocation_get_property;
document_frame_class->type_name = N_("Allocation");
/**
* SysprofDocumentAllocation:thread-id:
*
* The thread-id where the stack was traced.
*
* On Linux, this is generally set to the value of `gettid()`.
*
* Since: 45
*/
properties [PROP_THREAD_ID] =
g_param_spec_int ("thread-id", NULL, NULL,
-1, G_MAXINT32, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* SysprofDocumentAllocation:address:
*
* The address that was allocated or freed.
*
* Since: 45
*/
properties [PROP_ADDRESS] =
g_param_spec_uint64 ("address", NULL, NULL,
0, G_MAXUINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* SysprofDocumentAllocation:size:
*
* The size of the memory that was allocated or freed.
*
* Since: 45
*/
properties [PROP_SIZE] =
g_param_spec_int64 ("size", NULL, NULL,
G_MININT64, G_MAXINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* SysprofDocumentAllocation:is-free:
*
* If this allocation record is a call to release
* #SysprofDocumentAllocation:address.
*
* Since: 45
*/
properties [PROP_IS_FREE] =
g_param_spec_boolean ("is-free", NULL, NULL,
FALSE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* SysprofDocumentAllocation:stack-depth:
*
* The depth of the stack trace.
*
* Since: 45
*/
properties [PROP_STACK_DEPTH] =
g_param_spec_uint ("stack-depth", NULL, NULL,
0, G_MAXUINT16, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_allocation_init (SysprofDocumentAllocation *self)
{
}
guint64
sysprof_document_allocation_get_address (SysprofDocumentAllocation *self)
{
const SysprofCaptureAllocation *allocation;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_ALLOCATION (self), 0);
allocation = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureAllocation);
return SYSPROF_DOCUMENT_FRAME_UINT64 (self, allocation->alloc_addr);
}
gint64
sysprof_document_allocation_get_size (SysprofDocumentAllocation *self)
{
const SysprofCaptureAllocation *allocation;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_ALLOCATION (self), 0);
allocation = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureAllocation);
return SYSPROF_DOCUMENT_FRAME_INT64 (self, allocation->alloc_size);
}
gboolean
sysprof_document_allocation_is_free (SysprofDocumentAllocation *self)
{
const SysprofCaptureAllocation *allocation;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_ALLOCATION (self), 0);
allocation = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureAllocation);
return allocation->alloc_size == 0;
}

View File

@ -0,0 +1,48 @@
/* sysprof-document-allocation.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_ALLOCATION (sysprof_document_allocation_get_type())
#define SYSPROF_IS_DOCUMENT_ALLOCATION(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_ALLOCATION)
#define SYSPROF_DOCUMENT_ALLOCATION(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_ALLOCATION, SysprofDocumentAllocation)
#define SYSPROF_DOCUMENT_ALLOCATION_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_ALLOCATION, SysprofDocumentAllocationClass)
typedef struct _SysprofDocumentAllocation SysprofDocumentAllocation;
typedef struct _SysprofDocumentAllocationClass SysprofDocumentAllocationClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_allocation_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
guint64 sysprof_document_allocation_get_address (SysprofDocumentAllocation *self);
SYSPROF_AVAILABLE_IN_ALL
gint64 sysprof_document_allocation_get_size (SysprofDocumentAllocation *self);
SYSPROF_AVAILABLE_IN_ALL
int sysprof_document_allocation_get_tid (SysprofDocumentAllocation *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_document_allocation_is_free (SysprofDocumentAllocation *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentAllocation, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,35 @@
/* sysprof-document-bitset-index.h
*
* Copyright 2023 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
*/
#pragma once
#include <gio/gio.h>
#include <eggbitset.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_BITSET_INDEX (sysprof_document_bitset_index_get_type())
G_DECLARE_FINAL_TYPE (SysprofDocumentBitsetIndex, sysprof_document_bitset_index, SYSPROF, DOCUMENT_BITSET_INDEX, GObject)
GListModel *_sysprof_document_bitset_index_new (GListModel *model,
EggBitset *bitset);
G_END_DECLS

View File

@ -0,0 +1,154 @@
/* sysprof-document-bitset-index.c
*
* Copyright 2023 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 "sysprof-document-bitset-index-private.h"
struct _SysprofDocumentBitsetIndex
{
GObject parent_instance;
GListModel *model;
EggBitset *bitset;
};
static GType
sysprof_document_bitset_index_get_item_type (GListModel *model)
{
SysprofDocumentBitsetIndex *self = SYSPROF_DOCUMENT_BITSET_INDEX (model);
if (self->model != NULL)
return g_list_model_get_item_type (self->model);
return G_TYPE_OBJECT;
}
static guint
sysprof_document_bitset_index_get_n_items (GListModel *model)
{
SysprofDocumentBitsetIndex *self = SYSPROF_DOCUMENT_BITSET_INDEX (model);
if (self->bitset != NULL)
return egg_bitset_get_size (self->bitset);
return 0;
}
static gpointer
sysprof_document_bitset_index_get_item (GListModel *model,
guint position)
{
SysprofDocumentBitsetIndex *self = SYSPROF_DOCUMENT_BITSET_INDEX (model);
if (self->model == NULL || self->bitset == NULL)
return NULL;
if (position >= egg_bitset_get_size (self->bitset))
return NULL;
return g_list_model_get_item (self->model,
egg_bitset_get_nth (self->bitset, position));
}
static void
list_model_iface_init (GListModelInterface *iface)
{
iface->get_item = sysprof_document_bitset_index_get_item;
iface->get_item_type = sysprof_document_bitset_index_get_item_type;
iface->get_n_items = sysprof_document_bitset_index_get_n_items;
}
G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocumentBitsetIndex, sysprof_document_bitset_index, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
enum {
PROP_0,
PROP_N_ITEMS,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
static void
sysprof_document_bitset_index_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentBitsetIndex *self = SYSPROF_DOCUMENT_BITSET_INDEX (object);
switch (prop_id)
{
case PROP_N_ITEMS:
g_value_set_uint (value, g_list_model_get_n_items (G_LIST_MODEL (self)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_bitset_index_dispose (GObject *object)
{
SysprofDocumentBitsetIndex *self = (SysprofDocumentBitsetIndex *)object;
g_clear_pointer (&self->bitset, egg_bitset_unref);
g_clear_object (&self->model);
G_OBJECT_CLASS (sysprof_document_bitset_index_parent_class)->dispose (object);
}
static void
sysprof_document_bitset_index_class_init (SysprofDocumentBitsetIndexClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = sysprof_document_bitset_index_dispose;
object_class->get_property = sysprof_document_bitset_index_get_property;
properties[PROP_N_ITEMS] =
g_param_spec_uint ("n-items", NULL, NULL,
0, G_MAXUINT, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_bitset_index_init (SysprofDocumentBitsetIndex *self)
{
}
GListModel *
_sysprof_document_bitset_index_new (GListModel *model,
EggBitset *bitset)
{
SysprofDocumentBitsetIndex *self;
g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
g_return_val_if_fail (bitset != NULL, NULL);
self = g_object_new (SYSPROF_TYPE_DOCUMENT_BITSET_INDEX, NULL);
self->model = g_object_ref (model);
self->bitset = egg_bitset_ref (bitset);
return G_LIST_MODEL (self);
}

View File

@ -0,0 +1,50 @@
/* sysprof-document-counter-private.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-counter.h"
G_BEGIN_DECLS
struct _SysprofDocumentCounter
{
GObject parent_instance;
GRefString *category;
GRefString *description;
GRefString *name;
GArray *values;
double min_value;
double max_value;
gint64 begin_time;
guint id;
guint type;
};
SysprofDocumentCounter *_sysprof_document_counter_new (guint id,
guint type,
GRefString *category,
GRefString *name,
GRefString *description,
GArray *values,
gint64 begin_time);
void _sysprof_document_counter_calculate_range (SysprofDocumentCounter *self);
G_END_DECLS

View File

@ -0,0 +1,32 @@
/* sysprof-document-counter-value-private.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-counter-value.h"
#include "sysprof-document-private.h"
G_BEGIN_DECLS
SysprofDocumentCounterValue *_sysprof_document_counter_value_new (guint type,
const SysprofDocumentTimedValue *value,
SysprofDocumentCounter *counter);
G_END_DECLS

View File

@ -0,0 +1,220 @@
/* sysprof-document-counter-value.c
*
* Copyright 2023 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 "sysprof-document-counter-private.h"
#include "sysprof-document-counter-value-private.h"
struct _SysprofDocumentCounterValue
{
GObject parent_instance;
SysprofDocumentCounter *counter;
SysprofDocumentTimedValue value;
guint type;
};
enum {
PROP_0,
PROP_COUNTER,
PROP_TIME,
PROP_TIME_OFFSET,
PROP_VALUE_DOUBLE,
PROP_VALUE_INT64,
PROP_VALUE_STRING,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDocumentCounterValue, sysprof_document_counter_value, G_TYPE_OBJECT)
static GParamSpec *properties [N_PROPS];
static void
sysprof_document_counter_value_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentCounterValue *self = SYSPROF_DOCUMENT_COUNTER_VALUE (object);
switch (prop_id)
{
case PROP_COUNTER:
g_value_set_object (value, sysprof_document_counter_value_get_counter (self));
break;
case PROP_TIME:
g_value_set_int64 (value, sysprof_document_counter_value_get_time (self));
break;
case PROP_TIME_OFFSET:
g_value_set_int64 (value, sysprof_document_counter_value_get_time_offset (self));
break;
case PROP_VALUE_DOUBLE:
g_value_set_double (value, sysprof_document_counter_value_get_value_double (self));
break;
case PROP_VALUE_INT64:
g_value_set_int64 (value, sysprof_document_counter_value_get_value_int64 (self));
break;
case PROP_VALUE_STRING:
g_value_take_string (value, sysprof_document_counter_value_format (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_counter_value_class_init (SysprofDocumentCounterValueClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = sysprof_document_counter_value_get_property;
properties [PROP_COUNTER] =
g_param_spec_object ("counter", NULL, NULL,
SYSPROF_TYPE_DOCUMENT_COUNTER,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_TIME] =
g_param_spec_int64 ("time", NULL, NULL,
G_MININT64, G_MAXINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_TIME_OFFSET] =
g_param_spec_int64 ("time-offset", NULL, NULL,
G_MININT64, G_MAXINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties[PROP_VALUE_DOUBLE] =
g_param_spec_double ("value-double", NULL, NULL,
-G_MAXDOUBLE, G_MAXDOUBLE, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties[PROP_VALUE_INT64] =
g_param_spec_int64 ("value-int64", NULL, NULL,
G_MININT64, G_MAXINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties[PROP_VALUE_STRING] =
g_param_spec_string ("value-string", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_counter_value_init (SysprofDocumentCounterValue *self)
{
}
SysprofDocumentCounterValue *
_sysprof_document_counter_value_new (guint type,
const SysprofDocumentTimedValue *value,
SysprofDocumentCounter *counter)
{
SysprofDocumentCounterValue *self;
self = g_object_new (SYSPROF_TYPE_DOCUMENT_COUNTER_VALUE, NULL);
self->type = type;
self->value = *value;
self->counter = g_object_ref (counter);
return self;
}
gint64
sysprof_document_counter_value_get_time (SysprofDocumentCounterValue *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER_VALUE (self), 0);
return self->value.time;
}
gint64
sysprof_document_counter_value_get_time_offset (SysprofDocumentCounterValue *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER_VALUE (self), 0);
return self->value.time - self->counter->begin_time;
}
void
sysprof_document_counter_value_get_value (SysprofDocumentCounterValue *self,
GValue *value)
{
g_return_if_fail (SYSPROF_IS_DOCUMENT_COUNTER_VALUE (self));
g_return_if_fail (G_IS_VALUE (value));
if (G_VALUE_HOLDS_INT64 (value))
g_value_set_int64 (value, self->value.v_int64);
else if (G_VALUE_HOLDS_DOUBLE (value))
g_value_set_double (value, self->value.v_double);
else
g_warning_once ("Unsupported value type %s", G_VALUE_TYPE_NAME (value));
}
gint64
sysprof_document_counter_value_get_value_int64 (SysprofDocumentCounterValue *self)
{
if (self->type == SYSPROF_CAPTURE_COUNTER_INT64)
return self->value.v_int64;
else
return (gint64)self->value.v_double;
}
double
sysprof_document_counter_value_get_value_double (SysprofDocumentCounterValue *self)
{
if (self->type == SYSPROF_CAPTURE_COUNTER_DOUBLE)
return self->value.v_double;
else
return (double)self->value.v_int64;
}
char *
sysprof_document_counter_value_format (SysprofDocumentCounterValue *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER_VALUE (self), NULL);
if (self->type == SYSPROF_CAPTURE_COUNTER_DOUBLE)
return g_strdup_printf ("%lf", self->value.v_double);
else
return g_strdup_printf ("%ld", self->value.v_int64);
}
/**
* sysprof_document_counter_value_get_counter:
* @self: a #SysprofDocumentCounterValue
*
* Returns: (transfer none): a #SysprofDocumentCounter
*/
SysprofDocumentCounter *
sysprof_document_counter_value_get_counter (SysprofDocumentCounterValue *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER_VALUE (self), NULL);
return self->counter;
}

View File

@ -0,0 +1,53 @@
/* sysprof-document-counter-value.h
*
* Copyright 2023 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
*/
#pragma once
#include <glib-object.h>
#include <sysprof-capture.h>
#include "sysprof-document-counter.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_COUNTER_VALUE (sysprof_document_counter_value_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofDocumentCounterValue, sysprof_document_counter_value, SYSPROF, DOCUMENT_COUNTER_VALUE, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofDocumentCounter *sysprof_document_counter_value_get_counter (SysprofDocumentCounterValue *self);
SYSPROF_AVAILABLE_IN_ALL
gint64 sysprof_document_counter_value_get_time (SysprofDocumentCounterValue *self);
SYSPROF_AVAILABLE_IN_ALL
gint64 sysprof_document_counter_value_get_time_offset (SysprofDocumentCounterValue *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_document_counter_value_get_value (SysprofDocumentCounterValue *self,
GValue *value);
SYSPROF_AVAILABLE_IN_ALL
gint64 sysprof_document_counter_value_get_value_int64 (SysprofDocumentCounterValue *self);
SYSPROF_AVAILABLE_IN_ALL
double sysprof_document_counter_value_get_value_double (SysprofDocumentCounterValue *self);
SYSPROF_AVAILABLE_IN_ALL
char *sysprof_document_counter_value_format (SysprofDocumentCounterValue *self);
G_END_DECLS

View File

@ -0,0 +1,409 @@
/* sysprof-document-counter.c
*
* Copyright 2023 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 <math.h>
#include "sysprof-document-counter-private.h"
#include "sysprof-document-counter-value-private.h"
#include "sysprof-document-private.h"
enum {
PROP_0,
PROP_CATEGORY,
PROP_DESCRIPTION,
PROP_ID,
PROP_KEY,
PROP_MAX_VALUE,
PROP_MIN_VALUE,
PROP_NAME,
N_PROPS
};
static guint
sysprof_document_counter_get_n_items (GListModel *model)
{
return sysprof_document_counter_get_n_values (SYSPROF_DOCUMENT_COUNTER (model));
}
static GType
sysprof_document_counter_get_item_type (GListModel *model)
{
return SYSPROF_TYPE_DOCUMENT_COUNTER_VALUE;
}
static gpointer
sysprof_document_counter_get_item (GListModel *model,
guint position)
{
SysprofDocumentCounter *self = SYSPROF_DOCUMENT_COUNTER (model);
const SysprofDocumentTimedValue *value;
if (position >= self->values->len)
return NULL;
value = &g_array_index (self->values, SysprofDocumentTimedValue, position);
return _sysprof_document_counter_value_new (self->type, value, self);
}
static void
list_model_iface_init (GListModelInterface *iface)
{
iface->get_n_items = sysprof_document_counter_get_n_items;
iface->get_item_type = sysprof_document_counter_get_item_type;
iface->get_item = sysprof_document_counter_get_item;
}
G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocumentCounter, sysprof_document_counter, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
static GParamSpec *properties [N_PROPS];
static void
sysprof_document_counter_finalize (GObject *object)
{
SysprofDocumentCounter *self = (SysprofDocumentCounter *)object;
g_clear_pointer (&self->category, g_ref_string_release);
g_clear_pointer (&self->description, g_ref_string_release);
g_clear_pointer (&self->name, g_ref_string_release);
g_clear_pointer (&self->values, g_array_unref);
G_OBJECT_CLASS (sysprof_document_counter_parent_class)->finalize (object);
}
static void
sysprof_document_counter_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentCounter *self = SYSPROF_DOCUMENT_COUNTER (object);
switch (prop_id)
{
case PROP_CATEGORY:
g_value_set_string (value, sysprof_document_counter_get_category (self));
break;
case PROP_DESCRIPTION:
g_value_set_string (value, sysprof_document_counter_get_description (self));
break;
case PROP_MIN_VALUE:
g_value_set_double (value, self->min_value);
break;
case PROP_MAX_VALUE:
g_value_set_double (value, self->max_value);
break;
case PROP_NAME:
g_value_set_string (value, sysprof_document_counter_get_name (self));
break;
case PROP_KEY:
g_value_take_string (value, sysprof_document_counter_dup_key (self));
break;
case PROP_ID:
g_value_set_uint (value, sysprof_document_counter_get_id (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_counter_class_init (SysprofDocumentCounterClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_document_counter_finalize;
object_class->get_property = sysprof_document_counter_get_property;
properties [PROP_ID] =
g_param_spec_uint ("id", NULL, NULL,
0, G_MAXUINT, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_CATEGORY] =
g_param_spec_string ("category", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_DESCRIPTION] =
g_param_spec_string ("description", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_KEY] =
g_param_spec_string ("key", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_NAME] =
g_param_spec_string ("name", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties[PROP_MIN_VALUE] =
g_param_spec_double ("min-value", NULL, NULL,
-G_MAXDOUBLE, G_MAXDOUBLE, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties[PROP_MAX_VALUE] =
g_param_spec_double ("max-value", NULL, NULL,
-G_MAXDOUBLE, G_MAXDOUBLE, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_counter_init (SysprofDocumentCounter *self)
{
}
SysprofDocumentCounter *
_sysprof_document_counter_new (guint id,
guint type,
GRefString *category,
GRefString *name,
GRefString *description,
GArray *values,
gint64 begin_time)
{
SysprofDocumentCounter *self;
self = g_object_new (SYSPROF_TYPE_DOCUMENT_COUNTER, NULL);
self->id = id;
self->type = type;
self->category = category;
self->name = name;
self->description = description;
self->values = values;
self->begin_time = begin_time;
return self;
}
const char *
sysprof_document_counter_get_category (SysprofDocumentCounter *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), NULL);
return self->category;
}
const char *
sysprof_document_counter_get_description (SysprofDocumentCounter *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), NULL);
return self->description;
}
const char *
sysprof_document_counter_get_name (SysprofDocumentCounter *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), NULL);
return self->name;
}
guint
sysprof_document_counter_get_id (SysprofDocumentCounter *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), 0);
return self->id;
}
GType
sysprof_document_counter_get_value_type (SysprofDocumentCounter *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), G_TYPE_INVALID);
if (self->type == SYSPROF_CAPTURE_COUNTER_INT64)
return G_TYPE_INT64;
if (self->type == SYSPROF_CAPTURE_COUNTER_DOUBLE)
return G_TYPE_DOUBLE;
g_return_val_if_reached (G_TYPE_INVALID);
}
guint
sysprof_document_counter_get_n_values (SysprofDocumentCounter *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), 0);
return self->values->len;
}
void
sysprof_document_counter_get_value (SysprofDocumentCounter *self,
guint nth,
gint64 *time,
GValue *value)
{
g_return_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self));
g_return_if_fail (nth < self->values->len);
g_return_if_fail (value == NULL || G_IS_VALUE (value));
if (time != NULL)
*time = g_array_index (self->values, SysprofDocumentTimedValue, nth).time;
if (value == NULL)
return;
if (G_VALUE_HOLDS_INT64 (value))
g_value_set_int64 (value, g_array_index (self->values, SysprofDocumentTimedValue, nth).v_int64);
else if (G_VALUE_HOLDS_DOUBLE (value))
g_value_set_double (value, g_array_index (self->values, SysprofDocumentTimedValue, nth).v_double);
}
gint64
sysprof_document_counter_get_value_int64 (SysprofDocumentCounter *self,
guint nth,
gint64 *time)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), 0);
g_return_val_if_fail (nth < self->values->len, 0);
if (time != NULL)
*time = g_array_index (self->values, SysprofDocumentTimedValue, nth).time;
return g_array_index (self->values, SysprofDocumentTimedValue, nth).v_int64;
}
double
sysprof_document_counter_get_value_double (SysprofDocumentCounter *self,
guint nth,
gint64 *time)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), 0);
g_return_val_if_fail (nth < self->values->len, 0);
if (time != NULL)
*time = g_array_index (self->values, SysprofDocumentTimedValue, nth).time;
return g_array_index (self->values, SysprofDocumentTimedValue, nth).v_double;
}
static inline double
value_as_double (guint type,
const SysprofDocumentTimedValue *value)
{
if (type == SYSPROF_CAPTURE_COUNTER_DOUBLE)
return value->v_double;
else if (type == SYSPROF_CAPTURE_COUNTER_INT64)
return value->v_int64;
else
return .0;
}
static int
sort_by_time (gconstpointer aptr,
gconstpointer bptr)
{
const SysprofDocumentTimedValue *a = aptr;
const SysprofDocumentTimedValue *b = bptr;
if (a->time < b->time)
return -1;
if (a->time > b->time)
return 1;
return 0;
}
void
_sysprof_document_counter_calculate_range (SysprofDocumentCounter *self)
{
const SysprofDocumentTimedValue *values;
gboolean min_value_changed = FALSE;
gboolean max_value_changed = FALSE;
double min_value;
double max_value;
guint n_values;
g_return_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self));
if (self->values->len == 0)
return;
g_array_sort (self->values, sort_by_time);
values = &g_array_index (self->values, SysprofDocumentTimedValue, 0);
n_values = self->values->len;
min_value = value_as_double (self->type, &values[0]);
max_value = min_value;
for (guint i = 1; i < n_values; i++)
{
double value = value_as_double (self->type, &values[i]);
min_value = MIN (min_value, value);
max_value = MAX (max_value, value);
}
min_value_changed = self->min_value != min_value;
max_value_changed = self->max_value != max_value;
self->min_value = min_value;
self->max_value = max_value;
if (min_value_changed)
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MIN_VALUE]);
if (max_value_changed)
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MAX_VALUE]);
}
double
sysprof_document_counter_get_max_value (SysprofDocumentCounter *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), .0);
return self->max_value;
}
double
sysprof_document_counter_get_min_value (SysprofDocumentCounter *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), .0);
return self->min_value;
}
char *
sysprof_document_counter_dup_key (SysprofDocumentCounter *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_COUNTER (self), NULL);
return g_strdup_printf ("%s/%s", self->category, self->name);
}

View File

@ -0,0 +1,66 @@
/* sysprof-document-counter.h
*
* Copyright 2023 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
*/
#pragma once
#include <glib-object.h>
#include <sysprof-capture.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_COUNTER (sysprof_document_counter_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofDocumentCounter, sysprof_document_counter, SYSPROF, DOCUMENT_COUNTER, GObject)
SYSPROF_AVAILABLE_IN_ALL
char *sysprof_document_counter_dup_key (SysprofDocumentCounter *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_counter_get_category (SysprofDocumentCounter *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_counter_get_description (SysprofDocumentCounter *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_counter_get_name (SysprofDocumentCounter *self);
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_document_counter_get_id (SysprofDocumentCounter *self);
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_counter_get_value_type (SysprofDocumentCounter *self);
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_document_counter_get_n_values (SysprofDocumentCounter *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_document_counter_get_value (SysprofDocumentCounter *self,
guint nth,
gint64 *time,
GValue *value);
SYSPROF_AVAILABLE_IN_ALL
gint64 sysprof_document_counter_get_value_int64 (SysprofDocumentCounter *self,
guint nth,
gint64 *time);
SYSPROF_AVAILABLE_IN_ALL
double sysprof_document_counter_get_value_double (SysprofDocumentCounter *self,
guint nth,
gint64 *time);
SYSPROF_AVAILABLE_IN_ALL
double sysprof_document_counter_get_max_value (SysprofDocumentCounter *self);
SYSPROF_AVAILABLE_IN_ALL
double sysprof_document_counter_get_min_value (SysprofDocumentCounter *self);
G_END_DECLS

View File

@ -0,0 +1,105 @@
/* sysprof-document-ctrdef.c
*
* Copyright 2023 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 "sysprof-document-frame-private.h"
#include "sysprof-document-ctrdef.h"
struct _SysprofDocumentCtrdef
{
SysprofDocumentFrame parent_instance;
};
struct _SysprofDocumentCtrdefClass
{
SysprofDocumentFrameClass parent_class;
};
G_DEFINE_FINAL_TYPE (SysprofDocumentCtrdef, sysprof_document_ctrdef, SYSPROF_TYPE_DOCUMENT_FRAME)
static void
sysprof_document_ctrdef_class_init (SysprofDocumentCtrdefClass *klass)
{
}
static void
sysprof_document_ctrdef_init (SysprofDocumentCtrdef *self)
{
}
guint
sysprof_document_ctrdef_get_n_counters (SysprofDocumentCtrdef *self)
{
const SysprofCaptureCounterDefine *ctrdef;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_CTRDEF (self), 0);
ctrdef = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureCounterDefine);
return SYSPROF_DOCUMENT_FRAME_UINT16 (self, ctrdef->n_counters);
}
/**
* sysprof_document_ctrdef_get_counter:
* @self: a #SysprofDocumentCtrdef
* @nth: a value between 0 and sysprof_document_ctrdef_get_n_counters()
* @id: (out): a location for the id of the counter
* @type: (out): a location for either %SYSPROF_CAPTURE_COUNTER_INT64
* or %SYSPROF_CAPTURE_COUNTER_DOUBLE
* @category: (out): a location for the category
* @name: (out): a location for the name
* @description: (out): a location for the description
*
* Gets information about a counter defined in @self.
*/
void
sysprof_document_ctrdef_get_counter (SysprofDocumentCtrdef *self,
guint nth,
guint *id,
guint *type,
const char **category,
const char **name,
const char **description)
{
const SysprofCaptureCounterDefine *ctrdef;
const SysprofCaptureCounter *ctr;
g_return_if_fail (SYSPROF_IS_DOCUMENT_CTRDEF (self));
g_return_if_fail (nth < sysprof_document_ctrdef_get_n_counters (self));
ctrdef = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureCounterDefine);
ctr = &ctrdef->counters[nth];
if (id != NULL)
*id = SYSPROF_DOCUMENT_FRAME_UINT32 (self, ctr->id);
if (type != NULL)
*type = ctr->type;
if (category != NULL)
*category = SYSPROF_DOCUMENT_FRAME_CSTRING (self, ctr->category);
if (name != NULL)
*name = SYSPROF_DOCUMENT_FRAME_CSTRING (self, ctr->name);
if (description != NULL)
*description = SYSPROF_DOCUMENT_FRAME_CSTRING (self, ctr->description);
}

View File

@ -0,0 +1,53 @@
/* sysprof-document-ctrdef.h
*
* Copyright 2023 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
*/
#pragma once
#include <gio/gio.h>
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_CTRDEF (sysprof_document_ctrdef_get_type())
#define SYSPROF_IS_DOCUMENT_CTRDEF(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_CTRDEF)
#define SYSPROF_DOCUMENT_CTRDEF(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_CTRDEF, SysprofDocumentCtrdef)
#define SYSPROF_DOCUMENT_CTRDEF_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_CTRDEF, SysprofDocumentCtrdefClass)
typedef struct _SysprofDocumentCtrdef SysprofDocumentCtrdef;
typedef struct _SysprofDocumentCtrdefClass SysprofDocumentCtrdefClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_ctrdef_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_document_ctrdef_get_n_counters (SysprofDocumentCtrdef *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_document_ctrdef_get_counter (SysprofDocumentCtrdef *self,
guint nth,
guint *id,
guint *type,
const char **category,
const char **name,
const char **description);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentCtrdef, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,116 @@
/* sysprof-document-ctrset.c
*
* Copyright 2023 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 "sysprof-document-frame-private.h"
#include "sysprof-document-ctrset.h"
struct _SysprofDocumentCtrset
{
SysprofDocumentFrame parent_instance;
};
struct _SysprofDocumentCtrsetClass
{
SysprofDocumentFrameClass parent_class;
};
G_DEFINE_FINAL_TYPE (SysprofDocumentCtrset, sysprof_document_ctrset, SYSPROF_TYPE_DOCUMENT_FRAME)
static void
sysprof_document_ctrset_class_init (SysprofDocumentCtrsetClass *klass)
{
}
static void
sysprof_document_ctrset_init (SysprofDocumentCtrset *self)
{
}
guint
sysprof_document_ctrset_get_n_values (SysprofDocumentCtrset *self)
{
const SysprofCaptureCounterSet *ctrset;
gconstpointer endptr;
guint n_groups;
guint n_values = 0;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_CTRSET (self), 0);
endptr = SYSPROF_DOCUMENT_FRAME_ENDPTR (self);
ctrset = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureCounterSet);
n_groups = SYSPROF_DOCUMENT_FRAME_UINT16 (self, ctrset->n_values);
for (guint i = 0; i < n_groups; i++)
{
const SysprofCaptureCounterValues *values = &ctrset->values[i];
/* Don't allow overflowing the frame zone */
if ((gconstpointer)&values[1] > endptr)
break;
for (guint j = 0; j < G_N_ELEMENTS (values->ids); j++)
{
if (values->ids[j] == 0)
break;
n_values++;
}
}
return n_values;
}
/**
* sysprof_document_ctrset_get_raw_value: (skip)
* @self: a #SysprofDocumentCtrset
* @nth: the nth value to get
* @id: (out): a location for the counter id
* @value: a location to store the raw value
*
* The raw value is 8 bytes and may not be converted to local
* byte ordering.
*
* @nth must be less-than sysprof_document_ctrset_get_n_values().
*/
void
sysprof_document_ctrset_get_raw_value (SysprofDocumentCtrset *self,
guint nth,
guint *id,
guint8 value[restrict 8])
{
const SysprofCaptureCounterSet *ctrset;
guint group;
guint pos;
g_return_if_fail (SYSPROF_IS_DOCUMENT_CTRSET (self));
g_return_if_fail (nth < sysprof_document_ctrset_get_n_values (self));
g_return_if_fail (value != NULL);
group = nth / 8;
pos = nth % 8;
ctrset = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureCounterSet);
*id = SYSPROF_DOCUMENT_FRAME_UINT32 (self, ctrset->values[group].ids[pos]);
memcpy (value, &ctrset->values[group].values[pos], 8);
}

View File

@ -0,0 +1,50 @@
/* sysprof-document-ctrset.h
*
* Copyright 2023 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
*/
#pragma once
#include <gio/gio.h>
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_CTRSET (sysprof_document_ctrset_get_type())
#define SYSPROF_IS_DOCUMENT_CTRSET(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_CTRSET)
#define SYSPROF_DOCUMENT_CTRSET(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_CTRSET, SysprofDocumentCtrset)
#define SYSPROF_DOCUMENT_CTRSET_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_CTRSET, SysprofDocumentCtrsetClass)
typedef struct _SysprofDocumentCtrset SysprofDocumentCtrset;
typedef struct _SysprofDocumentCtrsetClass SysprofDocumentCtrsetClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_ctrset_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_document_ctrset_get_n_values (SysprofDocumentCtrset *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_document_ctrset_get_raw_value (SysprofDocumentCtrset *self,
guint nth,
guint *id,
guint8 value[restrict 8]);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentCtrset, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,46 @@
/* sysprof-document-exit.c
*
* Copyright 2023 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 "sysprof-document-frame-private.h"
#include "sysprof-document-exit.h"
struct _SysprofDocumentExit
{
SysprofDocumentFrame parent_instance;
};
struct _SysprofDocumentExitClass
{
SysprofDocumentFrameClass parent_class;
};
G_DEFINE_FINAL_TYPE (SysprofDocumentExit, sysprof_document_exit, SYSPROF_TYPE_DOCUMENT_FRAME)
static void
sysprof_document_exit_class_init (SysprofDocumentExitClass *klass)
{
}
static void
sysprof_document_exit_init (SysprofDocumentExit *self)
{
}

View File

@ -0,0 +1,41 @@
/* sysprof-document-exit.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_EXIT (sysprof_document_exit_get_type())
#define SYSPROF_IS_DOCUMENT_EXIT(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_EXIT)
#define SYSPROF_DOCUMENT_EXIT(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_EXIT, SysprofDocumentExit)
#define SYSPROF_DOCUMENT_EXIT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_EXIT, SysprofDocumentExitClass)
typedef struct _SysprofDocumentExit SysprofDocumentExit;
typedef struct _SysprofDocumentExitClass SysprofDocumentExitClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_exit_get_type (void) G_GNUC_CONST;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentExit, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,156 @@
/* sysprof-document-file-chunk.c
*
* Copyright 2023 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 "sysprof-document-frame-private.h"
#include "sysprof-document-file-chunk.h"
struct _SysprofDocumentFileChunk
{
SysprofDocumentFrame parent_instance;
};
struct _SysprofDocumentFileChunkClass
{
SysprofDocumentFrameClass parent_instance;
};
enum {
PROP_0,
PROP_IS_LAST,
PROP_SIZE,
PROP_PATH,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDocumentFileChunk, sysprof_document_file_chunk, SYSPROF_TYPE_DOCUMENT_FRAME)
static GParamSpec *properties [N_PROPS];
static void
sysprof_document_file_chunk_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentFileChunk *self = SYSPROF_DOCUMENT_FILE_CHUNK (object);
switch (prop_id)
{
case PROP_IS_LAST:
g_value_set_boolean (value, sysprof_document_file_chunk_get_is_last (self));
break;
case PROP_SIZE:
g_value_set_uint (value, sysprof_document_file_chunk_get_size (self));
break;
case PROP_PATH:
g_value_set_string (value, sysprof_document_file_chunk_get_path (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_file_chunk_class_init (SysprofDocumentFileChunkClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = sysprof_document_file_chunk_get_property;
properties [PROP_IS_LAST] =
g_param_spec_boolean ("is-last", NULL, NULL,
FALSE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_SIZE] =
g_param_spec_uint ("size", NULL, NULL,
0, G_MAXUINT16, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_PATH] =
g_param_spec_string ("path", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_file_chunk_init (SysprofDocumentFileChunk *self)
{
}
gboolean
sysprof_document_file_chunk_get_is_last (SysprofDocumentFileChunk *self)
{
const SysprofCaptureFileChunk *file_chunk;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE_CHUNK (self), FALSE);
file_chunk = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureFileChunk);
return file_chunk->is_last;
}
guint
sysprof_document_file_chunk_get_size (SysprofDocumentFileChunk *self)
{
const SysprofCaptureFileChunk *file_chunk;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE_CHUNK (self), FALSE);
file_chunk = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureFileChunk);
return SYSPROF_DOCUMENT_FRAME_UINT16 (self, file_chunk->len);
}
const char *
sysprof_document_file_chunk_get_path (SysprofDocumentFileChunk *self)
{
const SysprofCaptureFileChunk *file_chunk;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE_CHUNK (self), FALSE);
file_chunk = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureFileChunk);
return SYSPROF_DOCUMENT_FRAME_CSTRING (self, file_chunk->path);
}
const guint8 *
sysprof_document_file_chunk_get_data (SysprofDocumentFileChunk *self,
guint *size)
{
const SysprofCaptureFileChunk *file_chunk;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE_CHUNK (self), FALSE);
file_chunk = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureFileChunk);
if (size != NULL)
*size = sysprof_document_file_chunk_get_size (self);
return file_chunk->data;
}

View File

@ -0,0 +1,49 @@
/* sysprof-document-file-chunk.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_FILE_CHUNK (sysprof_document_file_chunk_get_type())
#define SYSPROF_IS_DOCUMENT_FILE_CHUNK(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_FILE_CHUNK)
#define SYSPROF_DOCUMENT_FILE_CHUNK(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_FILE_CHUNK, SysprofDocumentFileChunk)
#define SYSPROF_DOCUMENT_FILE_CHUNK_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_FILE_CHUNK, SysprofDocumentFileChunkClass)
typedef struct _SysprofDocumentFileChunk SysprofDocumentFileChunk;
typedef struct _SysprofDocumentFileChunkClass SysprofDocumentFileChunkClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_file_chunk_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_document_file_chunk_get_is_last (SysprofDocumentFileChunk *self);
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_document_file_chunk_get_size (SysprofDocumentFileChunk *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_file_chunk_get_path (SysprofDocumentFileChunk *self);
SYSPROF_AVAILABLE_IN_ALL
const guint8 *sysprof_document_file_chunk_get_data (SysprofDocumentFileChunk *self,
guint *size);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentFileChunk, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,31 @@
/* sysprof-document-file-private.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-file.h"
G_BEGIN_DECLS
SysprofDocumentFile *_sysprof_document_file_new (const char *path,
GPtrArray *file_chunks,
gboolean compressed);
G_END_DECLS

View File

@ -0,0 +1,279 @@
/* sysprof-document-file.c
*
* Copyright 2023 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 "sysprof-document-file-chunk.h"
#include "sysprof-document-file-private.h"
#include "sysprof-document-frame-private.h"
struct _SysprofDocumentFile
{
GObject parent_instance;
char *path;
GPtrArray *file_chunks;
guint compressed : 1;
};
enum {
PROP_0,
PROP_BYTES,
PROP_IS_COMPRESSED,
PROP_PATH,
PROP_SIZE,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDocumentFile, sysprof_document_file, G_TYPE_OBJECT)
static GParamSpec *properties [N_PROPS];
static void
sysprof_document_file_finalize (GObject *object)
{
SysprofDocumentFile *self = (SysprofDocumentFile *)object;
g_clear_pointer (&self->path, g_free);
g_clear_pointer (&self->file_chunks, g_ptr_array_unref);
G_OBJECT_CLASS (sysprof_document_file_parent_class)->finalize (object);
}
static void
sysprof_document_file_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentFile *self = SYSPROF_DOCUMENT_FILE (object);
switch (prop_id)
{
case PROP_PATH:
g_value_set_string (value, sysprof_document_file_get_path (self));
break;
case PROP_BYTES:
g_value_take_boxed (value, sysprof_document_file_dup_bytes (self));
break;
case PROP_IS_COMPRESSED:
g_value_set_boolean (value, sysprof_document_file_is_compressed (self));
break;
case PROP_SIZE:
g_value_set_uint64 (value, sysprof_document_file_get_size (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_file_class_init (SysprofDocumentFileClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_document_file_finalize;
object_class->get_property = sysprof_document_file_get_property;
properties [PROP_PATH] =
g_param_spec_string ("path", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_BYTES] =
g_param_spec_boxed ("bytes", NULL, NULL,
G_TYPE_BYTES,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_IS_COMPRESSED] =
g_param_spec_boolean ("is-compressed", NULL, NULL,
FALSE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_SIZE] =
g_param_spec_uint64 ("size", NULL, NULL,
0, G_MAXUINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_file_init (SysprofDocumentFile *self)
{
}
SysprofDocumentFile *
_sysprof_document_file_new (const char *path,
GPtrArray *file_chunks,
gboolean compressed)
{
SysprofDocumentFile *self;
g_return_val_if_fail (path != NULL, NULL);
g_return_val_if_fail (file_chunks != NULL, NULL);
self = g_object_new (SYSPROF_TYPE_DOCUMENT_FILE, NULL);
self->path = g_strdup (path);
self->file_chunks = file_chunks;
self->compressed = !!compressed;
return self;
}
const char *
sysprof_document_file_get_path (SysprofDocumentFile *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE (self), NULL);
return self->path;
}
/**
* sysprof_document_file_dup_contents:
* @self: a #SysprofDocumentFile
*
* Creates a new #GBytes containing the contents of the file.
*
* Returns: (transfer full): a #GBytes
*/
GBytes *
sysprof_document_file_dup_bytes (SysprofDocumentFile *self)
{
GArray *ar;
guint len;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE (self), NULL);
ar = g_array_new (TRUE, FALSE, sizeof (char));
for (guint i = 0; i < self->file_chunks->len; i++)
{
SysprofDocumentFileChunk *file_chunk = g_ptr_array_index (self->file_chunks, i);
const guint8 *data = sysprof_document_file_chunk_get_data (file_chunk, &len);
g_array_append_vals (ar, data, len);
}
len = ar->len;
if (self->compressed)
{
guint8 *data = (guint8 *)g_array_free (ar, FALSE);
g_autoptr(GInputStream) input = g_memory_input_stream_new_from_data (data, len, g_free);
g_autoptr(GOutputStream) memory_output = g_memory_output_stream_new_resizable ();
g_autoptr(GZlibDecompressor) zlib = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
g_autoptr(GOutputStream) zlib_output = g_converter_output_stream_new (memory_output, G_CONVERTER (zlib));
g_output_stream_splice (zlib_output,
input,
(G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET),
NULL, NULL);
return g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (memory_output));
}
return g_bytes_new_take (g_array_free (ar, FALSE), len);
}
/**
* sysprof_document_file_read:
* @self: a #SysprofDocumentFile
*
* Creates a new input stream containing the contents of the file
* within the document.
*
* Returns: (transfer full): a #GInputstream
*/
GInputStream *
sysprof_document_file_read (SysprofDocumentFile *self)
{
g_autoptr(GInputStream) input = NULL;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE (self), NULL);
input = g_memory_input_stream_new ();
for (guint i = 0; i < self->file_chunks->len; i++)
{
g_autoptr(GBytes) bytes = NULL;
SysprofDocumentFileChunk *file_chunk;
const guint8 *data;
guint len;
file_chunk = g_ptr_array_index (self->file_chunks, i);
data = sysprof_document_file_chunk_get_data (file_chunk, &len);
bytes = g_bytes_new_with_free_func (data,
len,
(GDestroyNotify)g_mapped_file_unref,
g_mapped_file_ref (SYSPROF_DOCUMENT_FRAME (file_chunk)->mapped_file));
g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (input), bytes);
}
if (self->compressed)
{
g_autoptr(GZlibDecompressor) zlib = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
return g_converter_input_stream_new (input, G_CONVERTER (zlib));
}
return g_steal_pointer (&input);
}
gsize
sysprof_document_file_get_size (SysprofDocumentFile *self)
{
gsize size = 0;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE (self), 0);
for (guint i = 0; i < self->file_chunks->len; i++)
{
SysprofDocumentFileChunk *file_chunk = g_ptr_array_index (self->file_chunks, i);
size += sysprof_document_file_chunk_get_size (file_chunk);
}
return size;
}
/**
* sysprof_document_file_is_compressed:
* @self: a #SysprofDocumentFile
*
* Checks if the embedded file is compressed. If so, %TRUE is returned.
*
* Note that files are transparently decompressed when opening streams.
*
* Returns: %TRUE if the embedded file was compressed
*/
gboolean
sysprof_document_file_is_compressed (SysprofDocumentFile *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FILE (self), FALSE);
return self->compressed;
}

View File

@ -0,0 +1,45 @@
/* sysprof-document-file.h
*
* Copyright 2023 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
*/
#pragma once
#include <gio/gio.h>
#include <sysprof-capture.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_FILE (sysprof_document_file_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofDocumentFile, sysprof_document_file, SYSPROF, DOCUMENT_FILE, GObject)
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_file_get_path (SysprofDocumentFile *self);
SYSPROF_AVAILABLE_IN_ALL
GBytes *sysprof_document_file_dup_bytes (SysprofDocumentFile *self);
SYSPROF_AVAILABLE_IN_ALL
GInputStream *sysprof_document_file_read (SysprofDocumentFile *self);
SYSPROF_AVAILABLE_IN_ALL
gsize sysprof_document_file_get_size (SysprofDocumentFile *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_document_file_is_compressed (SysprofDocumentFile *self);
G_END_DECLS

View File

@ -0,0 +1,95 @@
/* sysprof-document-fork.c
*
* Copyright 2023 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 "sysprof-document-frame-private.h"
#include "sysprof-document-fork.h"
struct _SysprofDocumentFork
{
SysprofDocumentFrame parent_instance;
};
struct _SysprofDocumentForkClass
{
SysprofDocumentFrameClass parent_class;
};
enum {
PROP_0,
PROP_CHILD_PID,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDocumentFork, sysprof_document_fork, SYSPROF_TYPE_DOCUMENT_FRAME)
static GParamSpec *properties [N_PROPS];
static void
sysprof_document_fork_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentFork *self = SYSPROF_DOCUMENT_FORK (object);
switch (prop_id)
{
case PROP_CHILD_PID:
g_value_set_int (value, sysprof_document_fork_get_child_pid (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_fork_class_init (SysprofDocumentForkClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = sysprof_document_fork_get_property;
properties [PROP_CHILD_PID] =
g_param_spec_int ("child-pid", NULL, NULL,
G_MININT, G_MAXINT, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_fork_init (SysprofDocumentFork *self)
{
}
int
sysprof_document_fork_get_child_pid (SysprofDocumentFork *self)
{
const SysprofCaptureFork *fork;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FORK (self), 0);
fork = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureFork);
return SYSPROF_DOCUMENT_FRAME_INT32 (self, fork->child_pid);
}

View File

@ -0,0 +1,43 @@
/* sysprof-document-fork.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_FORK (sysprof_document_fork_get_type())
#define SYSPROF_IS_DOCUMENT_FORK(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_FORK)
#define SYSPROF_DOCUMENT_FORK(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_FORK, SysprofDocumentFork)
#define SYSPROF_DOCUMENT_FORK_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_FORK, SysprofDocumentForkClass)
typedef struct _SysprofDocumentFork SysprofDocumentFork;
typedef struct _SysprofDocumentForkClass SysprofDocumentForkClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_fork_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
int sysprof_document_fork_get_child_pid (SysprofDocumentFork *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentFork, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,112 @@
/* sysprof-document-frame-private.h
*
* Copyright 2023 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
*/
#pragma once
#include <sysprof-capture.h>
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
struct _SysprofDocumentFrame
{
GObject parent;
GMappedFile *mapped_file;
const SysprofCaptureFrame *frame;
gint64 time_offset;
guint32 frame_len : 16;
guint32 needs_swap : 1;
guint32 padding : 15;
};
struct _SysprofDocumentFrameClass
{
GObjectClass parent_class;
const char *type_name;
char *(*dup_tooltip) (SysprofDocumentFrame *self);
};
SysprofDocumentFrame *_sysprof_document_frame_new (GMappedFile *mapped,
const SysprofCaptureFrame *frame,
guint16 frame_len,
gboolean needs_swap,
gint64 begin_time,
gint64 end_time);
#define SYSPROF_DOCUMENT_FRAME_GET_CLASS(obj) \
G_TYPE_INSTANCE_GET_CLASS(obj, SYSPROF_TYPE_DOCUMENT_FRAME, SysprofDocumentFrameClass)
#define SYSPROF_DOCUMENT_FRAME_ENDPTR(obj) \
(&((const guint8 *)SYSPROF_DOCUMENT_FRAME(obj)->frame)[SYSPROF_DOCUMENT_FRAME(obj)->frame_len])
#define SYSPROF_DOCUMENT_FRAME_GET(obj, type) \
((const type *)(SYSPROF_DOCUMENT_FRAME(obj)->frame))
#define SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) \
(SYSPROF_DOCUMENT_FRAME (obj)->needs_swap)
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
# define SYSPROF_DOCUMENT_FRAME_UINT16(obj, val) \
(SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GUINT16_TO_LE(val) : (val))
# define SYSPROF_DOCUMENT_FRAME_INT16(obj, val) \
(SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GINT16_TO_LE(val) : (val))
# define SYSPROF_DOCUMENT_FRAME_UINT32(obj, val) \
(SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GUINT32_TO_LE(val) : (val))
# define SYSPROF_DOCUMENT_FRAME_INT32(obj, val) \
(SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GINT32_TO_LE(val) : (val))
# define SYSPROF_DOCUMENT_FRAME_UINT64(obj, val) \
(SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GUINT64_TO_LE(val) : (val))
# define SYSPROF_DOCUMENT_FRAME_INT64(obj, val) \
(SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GINT64_TO_LE(val) : (val))
#else
# define SYSPROF_DOCUMENT_FRAME_UINT16(obj, val) \
(SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GUINT16_TO_BE(val) : (val))
# define SYSPROF_DOCUMENT_FRAME_INT16(obj, val) \
(SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GINT16_TO_BE(val) : (val))
# define SYSPROF_DOCUMENT_FRAME_UINT32(obj, val) \
(SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GUINT32_TO_BE(val) : (val))
# define SYSPROF_DOCUMENT_FRAME_INT32(obj, val) \
(SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GINT32_TO_BE(val) : (val))
# define SYSPROF_DOCUMENT_FRAME_UINT64(obj, val) \
(SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GUINT64_TO_BE(val) : (val))
# define SYSPROF_DOCUMENT_FRAME_INT64(obj, val) \
(SYSPROF_DOCUMENT_FRAME_NEEDS_SWAP(obj) ? GINT64_TO_BE(val) : (val))
#endif
static inline const char *
_SYSPROF_DOCUMENT_FRAME_CSTRING (SysprofDocumentFrame *self,
const char *str)
{
const char *endptr = (const char *)self->frame + self->frame_len;
for (const char *c = str; c < endptr; c++)
{
if (*c == 0)
return str;
}
return NULL;
}
#define SYSPROF_DOCUMENT_FRAME_CSTRING(obj,str) \
_SYSPROF_DOCUMENT_FRAME_CSTRING(SYSPROF_DOCUMENT_FRAME(obj),str)
G_END_DECLS

View File

@ -0,0 +1,363 @@
/* sysprof-document-frame.c
*
* Copyright 2023 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 <glib/gi18n.h>
#include "sysprof-document-frame-private.h"
#include "sysprof-document-allocation.h"
#include "sysprof-document-ctrdef.h"
#include "sysprof-document-ctrset.h"
#include "sysprof-document-exit.h"
#include "sysprof-document-file-chunk.h"
#include "sysprof-document-fork.h"
#include "sysprof-document-log.h"
#include "sysprof-document-jitmap.h"
#include "sysprof-document-mark.h"
#include "sysprof-document-metadata.h"
#include "sysprof-document-mmap.h"
#include "sysprof-document-overlay.h"
#include "sysprof-document-process.h"
#include "sysprof-document-sample.h"
G_DEFINE_TYPE (SysprofDocumentFrame, sysprof_document_frame, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_CPU,
PROP_PID,
PROP_TIME,
PROP_TIME_OFFSET,
PROP_TIME_STRING,
PROP_TOOLTIP,
PROP_TYPE_NAME,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
static char *
sysprof_document_frame_real_dup_tooltip (SysprofDocumentFrame *self)
{
g_autofree char *time_string = sysprof_document_frame_dup_time_string (self);
return g_strdup_printf ("%s: %s",
time_string,
SYSPROF_DOCUMENT_FRAME_GET_CLASS (self)->type_name);
}
static const char *
sysprof_document_frame_get_type_name (SysprofDocumentFrame *self)
{
return g_dgettext (GETTEXT_PACKAGE, SYSPROF_DOCUMENT_FRAME_GET_CLASS (self)->type_name);
}
static void
sysprof_document_frame_finalize (GObject *object)
{
SysprofDocumentFrame *self = (SysprofDocumentFrame *)object;
g_clear_pointer (&self->mapped_file, g_mapped_file_unref);
self->frame = NULL;
self->needs_swap = 0;
G_OBJECT_CLASS (sysprof_document_frame_parent_class)->finalize (object);
}
static void
sysprof_document_frame_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentFrame *self = SYSPROF_DOCUMENT_FRAME (object);
switch (prop_id)
{
case PROP_CPU:
g_value_set_int (value, sysprof_document_frame_get_cpu (self));
break;
case PROP_PID:
g_value_set_int (value, sysprof_document_frame_get_pid (self));
break;
case PROP_TIME:
g_value_set_int64 (value, sysprof_document_frame_get_time (self));
break;
case PROP_TIME_OFFSET:
g_value_set_int64 (value, sysprof_document_frame_get_time_offset (self));
break;
case PROP_TIME_STRING:
g_value_take_string (value, sysprof_document_frame_dup_time_string (self));
break;
case PROP_TOOLTIP:
g_value_take_string (value, sysprof_document_frame_dup_tooltip (self));
break;
case PROP_TYPE_NAME:
g_value_set_static_string (value, sysprof_document_frame_get_type_name (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_frame_class_init (SysprofDocumentFrameClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_document_frame_finalize;
object_class->get_property = sysprof_document_frame_get_property;
klass->type_name = N_("Frame");
klass->dup_tooltip = sysprof_document_frame_real_dup_tooltip;
properties[PROP_CPU] =
g_param_spec_int ("cpu", NULL, NULL,
G_MININT32,
G_MAXINT32,
-1,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties[PROP_PID] =
g_param_spec_int ("pid", NULL, NULL,
G_MININT32,
G_MAXINT32,
-1,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties[PROP_TIME] =
g_param_spec_int64 ("time", NULL, NULL,
G_MININT64,
G_MAXINT64,
0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties[PROP_TIME_OFFSET] =
g_param_spec_int64 ("time-offset", NULL, NULL,
G_MININT64,
G_MAXINT64,
0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties[PROP_TIME_STRING] =
g_param_spec_string ("time-string", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties[PROP_TYPE_NAME] =
g_param_spec_string ("type-name", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties[PROP_TOOLTIP] =
g_param_spec_string ("tooltip", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_frame_init (SysprofDocumentFrame *self)
{
}
SysprofDocumentFrame *
_sysprof_document_frame_new (GMappedFile *mapped_file,
const SysprofCaptureFrame *frame,
guint16 frame_len,
gboolean needs_swap,
gint64 begin_time,
gint64 end_time)
{
SysprofDocumentFrame *self;
GType gtype;
switch (frame->type)
{
case SYSPROF_CAPTURE_FRAME_SAMPLE:
gtype = SYSPROF_TYPE_DOCUMENT_SAMPLE;
break;
case SYSPROF_CAPTURE_FRAME_MAP:
gtype = SYSPROF_TYPE_DOCUMENT_MMAP;
break;
case SYSPROF_CAPTURE_FRAME_LOG:
gtype = SYSPROF_TYPE_DOCUMENT_LOG;
break;
case SYSPROF_CAPTURE_FRAME_MARK:
gtype = SYSPROF_TYPE_DOCUMENT_MARK;
break;
case SYSPROF_CAPTURE_FRAME_METADATA:
gtype = SYSPROF_TYPE_DOCUMENT_METADATA;
break;
case SYSPROF_CAPTURE_FRAME_PROCESS:
gtype = SYSPROF_TYPE_DOCUMENT_PROCESS;
break;
case SYSPROF_CAPTURE_FRAME_EXIT:
gtype = SYSPROF_TYPE_DOCUMENT_EXIT;
break;
case SYSPROF_CAPTURE_FRAME_FORK:
gtype = SYSPROF_TYPE_DOCUMENT_FORK;
break;
case SYSPROF_CAPTURE_FRAME_ALLOCATION:
gtype = SYSPROF_TYPE_DOCUMENT_ALLOCATION;
break;
case SYSPROF_CAPTURE_FRAME_FILE_CHUNK:
gtype = SYSPROF_TYPE_DOCUMENT_FILE_CHUNK;
break;
case SYSPROF_CAPTURE_FRAME_OVERLAY:
gtype = SYSPROF_TYPE_DOCUMENT_OVERLAY;
break;
case SYSPROF_CAPTURE_FRAME_JITMAP:
gtype = SYSPROF_TYPE_DOCUMENT_JITMAP;
break;
case SYSPROF_CAPTURE_FRAME_CTRDEF:
gtype = SYSPROF_TYPE_DOCUMENT_CTRDEF;
break;
case SYSPROF_CAPTURE_FRAME_CTRSET:
gtype = SYSPROF_TYPE_DOCUMENT_CTRSET;
break;
default:
gtype = SYSPROF_TYPE_DOCUMENT_FRAME;
break;
}
self = g_object_new (gtype, NULL);
self->mapped_file = g_mapped_file_ref (mapped_file);
self->frame = frame;
self->frame_len = frame_len;
self->needs_swap = !!needs_swap;
self->time_offset = CLAMP (sysprof_document_frame_get_time (self) - begin_time, 0, G_MAXINT64);
return self;
}
int
sysprof_document_frame_get_cpu (SysprofDocumentFrame *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0);
return SYSPROF_DOCUMENT_FRAME_INT32 (self, self->frame->cpu);
}
int
sysprof_document_frame_get_pid (SysprofDocumentFrame *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0);
return SYSPROF_DOCUMENT_FRAME_INT32 (self, self->frame->pid);
}
gint64
sysprof_document_frame_get_time (SysprofDocumentFrame *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0);
return SYSPROF_DOCUMENT_FRAME_INT64 (self, self->frame->time);
}
gint64
sysprof_document_frame_get_time_offset (SysprofDocumentFrame *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), 0);
return self->time_offset;
}
gboolean
sysprof_document_frame_equal (const SysprofDocumentFrame *a,
const SysprofDocumentFrame *b)
{
return a->frame == b->frame;
}
/**
* sysprof_document_frame_dup_time_string:
* @self: a #SysprofDocumentFrame
*
* Gets the time formatted as a string such as `00:00:00.1234`.
*
* Returns: (transfer full): a new string
*/
char *
sysprof_document_frame_dup_time_string (SysprofDocumentFrame *self)
{
int hours;
int minutes;
int seconds;
double time;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), NULL);
time = self->time_offset / (double)SYSPROF_NSEC_PER_SEC;
hours = time / (60 * 60);
time -= hours * (60 * 60);
minutes = time / 60;
time -= minutes * 60;
seconds = time / SYSPROF_NSEC_PER_SEC;
time -= seconds * SYSPROF_NSEC_PER_SEC;
return g_strdup_printf ("%02d:%02d:%02d.%04d", hours, minutes, seconds, (int)(time * 10000));
}
/**
* sysprof_document_frame_dup_tooltip:
* @self: a #SysprofDocumentFrame
*
* Returns a new string containing suggested tooltip text.
*
* Returns: (transfer full): a string
*/
char *
sysprof_document_frame_dup_tooltip (SysprofDocumentFrame *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_FRAME (self), NULL);
return SYSPROF_DOCUMENT_FRAME_GET_CLASS (self)->dup_tooltip (self);
}

View File

@ -0,0 +1,57 @@
/* sysprof-document-frame.h
*
* Copyright 2023 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
*/
#pragma once
#include <glib-object.h>
#include <sysprof-capture.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_FRAME (sysprof_document_frame_get_type())
#define SYSPROF_IS_DOCUMENT_FRAME(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_FRAME)
#define SYSPROF_DOCUMENT_FRAME(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_FRAME, SysprofDocumentFrame)
#define SYSPROF_DOCUMENT_FRAME_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_FRAME, SysprofDocumentFrameClass)
typedef struct _SysprofDocumentFrame SysprofDocumentFrame;
typedef struct _SysprofDocumentFrameClass SysprofDocumentFrameClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_frame_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
int sysprof_document_frame_get_cpu (SysprofDocumentFrame *self);
SYSPROF_AVAILABLE_IN_ALL
int sysprof_document_frame_get_pid (SysprofDocumentFrame *self);
SYSPROF_AVAILABLE_IN_ALL
gint64 sysprof_document_frame_get_time (SysprofDocumentFrame *self);
SYSPROF_AVAILABLE_IN_ALL
gint64 sysprof_document_frame_get_time_offset (SysprofDocumentFrame *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_document_frame_equal (const SysprofDocumentFrame *a,
const SysprofDocumentFrame *b);
SYSPROF_AVAILABLE_IN_ALL
char *sysprof_document_frame_dup_tooltip (SysprofDocumentFrame *self);
SYSPROF_AVAILABLE_IN_ALL
char *sysprof_document_frame_dup_time_string (SysprofDocumentFrame *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentFrame, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,169 @@
/* sysprof-document-jitmap.c
*
* Copyright 2023 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 "sysprof-document-frame-private.h"
#include "sysprof-document-jitmap.h"
typedef struct _Jitmap
{
SysprofAddress address;
const char *name;
} Jitmap;
struct _SysprofDocumentJitmap
{
SysprofDocumentFrame parent_instance;
GArray *jitmaps;
guint initialized : 1;
};
struct _SysprofDocumentJitmapClass
{
SysprofDocumentFrameClass parent_class;
};
enum {
PROP_0,
PROP_SIZE,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDocumentJitmap, sysprof_document_jitmap, SYSPROF_TYPE_DOCUMENT_FRAME)
static GParamSpec *properties [N_PROPS];
static void
sysprof_document_jitmap_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentJitmap *self = SYSPROF_DOCUMENT_JITMAP (object);
switch (prop_id)
{
case PROP_SIZE:
g_value_set_uint (value, sysprof_document_jitmap_get_size (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_jitmap_class_init (SysprofDocumentJitmapClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = sysprof_document_jitmap_get_property;
properties [PROP_SIZE] =
g_param_spec_string ("size", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_jitmap_init (SysprofDocumentJitmap *self)
{
self->jitmaps = g_array_new (FALSE, FALSE, sizeof (Jitmap));
}
guint
sysprof_document_jitmap_get_size (SysprofDocumentJitmap *self)
{
const SysprofCaptureJitmap *jitmap;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_JITMAP (self), 0);
jitmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureJitmap);
return SYSPROF_DOCUMENT_FRAME_UINT32 (self, jitmap->n_jitmaps);
}
/**
* sysprof_document_jitmap_get_mapping:
* @self: a #SysprofDocumentJitmap
* @nth: the index of the mapping
* @address: (out): a location for the address
*
* Gets the @nth mapping and returns it as @address and the return value
* of this function.
*
* Returns: (nullable): the name of the symbol, or %NULL if @nth is
* out of bounds.
*/
const char *
sysprof_document_jitmap_get_mapping (SysprofDocumentJitmap *self,
guint nth,
SysprofAddress *address)
{
const SysprofCaptureJitmap *frame;
const Jitmap *j;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_JITMAP (self), NULL);
g_return_val_if_fail (address != NULL, NULL);
if G_UNLIKELY (!self->initialized)
{
const guint8 *pos;
const guint8 *endptr;
self->initialized = TRUE;
frame = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureJitmap);
endptr = SYSPROF_DOCUMENT_FRAME_ENDPTR (self);
pos = frame->data;
while (pos < endptr)
{
SysprofAddress addr;
Jitmap map;
if (pos + sizeof addr >= endptr)
break;
memcpy (&addr, pos, sizeof addr);
pos += sizeof addr;
map.address = SYSPROF_DOCUMENT_FRAME_UINT64 (self, addr);
if (!(map.name = SYSPROF_DOCUMENT_FRAME_CSTRING (self, (const char *)pos)))
break;
pos += strlen (map.name) + 1;
g_array_append_val (self->jitmaps, map);
}
}
if (nth >= self->jitmaps->len)
return NULL;
j = &g_array_index (self->jitmaps, Jitmap, nth);
*address = j->address;
return j->name;
}

View File

@ -0,0 +1,49 @@
/* sysprof-document-jitmap.h
*
* Copyright 2023 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
*/
#pragma once
#include <gio/gio.h>
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_JITMAP (sysprof_document_jitmap_get_type())
#define SYSPROF_IS_DOCUMENT_JITMAP(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_JITMAP)
#define SYSPROF_DOCUMENT_JITMAP(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_JITMAP, SysprofDocumentJitmap)
#define SYSPROF_DOCUMENT_JITMAP_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_JITMAP, SysprofDocumentJitmapClass)
typedef struct _SysprofDocumentJitmap SysprofDocumentJitmap;
typedef struct _SysprofDocumentJitmapClass SysprofDocumentJitmapClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_jitmap_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_document_jitmap_get_size (SysprofDocumentJitmap *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_jitmap_get_mapping (SysprofDocumentJitmap *self,
guint position,
SysprofAddress *address);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentJitmap, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,659 @@
/* sysprof-document-loader.c
*
* Copyright 2023 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 <unistd.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include "sysprof-bundled-symbolizer.h"
#include "sysprof-document-bitset-index-private.h"
#include "sysprof-document-loader.h"
#include "sysprof-document-private.h"
#include "sysprof-elf-symbolizer.h"
#include "sysprof-jitmap-symbolizer.h"
#include "sysprof-kallsyms-symbolizer.h"
#include "sysprof-multi-symbolizer.h"
struct _SysprofDocumentLoader
{
GObject parent_instance;
GMutex mutex;
SysprofSymbolizer *symbolizer;
char *filename;
char *message;
double fraction;
int fd;
guint notify_source;
guint symbolizing : 1;
};
enum {
PROP_0,
PROP_FRACTION,
PROP_MESSAGE,
PROP_SYMBOLIZER,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDocumentLoader, sysprof_document_loader, G_TYPE_OBJECT)
static GParamSpec *properties [N_PROPS];
static gboolean
progress_notify_in_idle (gpointer data)
{
SysprofDocumentLoader *self = data;
g_assert (SYSPROF_IS_DOCUMENT_LOADER (self));
g_mutex_lock (&self->mutex);
self->notify_source = 0;
g_mutex_unlock (&self->mutex);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FRACTION]);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MESSAGE]);
return G_SOURCE_REMOVE;
}
static void
set_progress (double fraction,
const char *message,
gpointer user_data)
{
SysprofDocumentLoader *self = user_data;
g_assert (SYSPROF_IS_DOCUMENT_LOADER (self));
g_mutex_lock (&self->mutex);
self->fraction = fraction * .5;
if (self->symbolizing)
self->fraction += .5;
g_set_str (&self->message, message);
if (!self->notify_source)
self->notify_source = g_idle_add_full (G_PRIORITY_LOW,
progress_notify_in_idle,
g_object_ref (self),
g_object_unref);
g_mutex_unlock (&self->mutex);
}
static void
mapped_file_by_filename (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
g_autoptr(GError) error = NULL;
g_autoptr(GMappedFile) mapped_file = g_mapped_file_new (task_data, FALSE, &error);
if (mapped_file != NULL)
g_task_return_pointer (task,
g_steal_pointer (&mapped_file),
(GDestroyNotify)g_mapped_file_unref);
else
g_task_return_error (task, g_steal_pointer (&error));
}
static void
mapped_file_new_async (const char *filename,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_task_data (task, g_strdup (filename), g_free);
g_task_run_in_thread (task, mapped_file_by_filename);
}
static void
mapped_file_by_fd (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
g_autoptr(GError) error = NULL;
g_autoptr(GMappedFile) mapped_file = g_mapped_file_new_from_fd (GPOINTER_TO_INT (task_data), FALSE, &error);
if (mapped_file != NULL)
g_task_return_pointer (task,
g_steal_pointer (&mapped_file),
(GDestroyNotify)g_mapped_file_unref);
else
g_task_return_error (task, g_steal_pointer (&error));
}
static void
close_fd (gpointer data)
{
int fd = GPOINTER_TO_INT (data);
close (fd);
}
static void
mapped_file_new_from_fd_async (int fd,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
int copy_fd;
task = g_task_new (NULL, cancellable, callback, user_data);
if (-1 == (copy_fd = dup (fd)))
{
int errsv = errno;
g_task_return_new_error (task,
G_IO_ERROR,
g_io_error_from_errno (errsv),
"%s",
g_strerror (errsv));
return;
}
g_task_set_task_data (task, GINT_TO_POINTER (copy_fd), close_fd);
g_task_run_in_thread (task, mapped_file_by_fd);
}
static GMappedFile *
mapped_file_new_finish (GAsyncResult *result,
GError **error)
{
return g_task_propagate_pointer (G_TASK (result), error);
}
static void
set_default_symbolizer (SysprofDocumentLoader *self)
{
g_autoptr(SysprofMultiSymbolizer) multi = NULL;
g_assert (SYSPROF_IS_DOCUMENT_LOADER (self));
g_clear_object (&self->symbolizer);
multi = sysprof_multi_symbolizer_new ();
sysprof_multi_symbolizer_take (multi, sysprof_bundled_symbolizer_new ());
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 ());
self->symbolizer = SYSPROF_SYMBOLIZER (g_steal_pointer (&multi));
}
static void
sysprof_document_loader_finalize (GObject *object)
{
SysprofDocumentLoader *self = (SysprofDocumentLoader *)object;
g_clear_handle_id (&self->notify_source, g_source_remove);
g_clear_object (&self->symbolizer);
g_clear_pointer (&self->filename, g_free);
g_clear_pointer (&self->message, g_free);
g_clear_fd (&self->fd, NULL);
g_mutex_clear (&self->mutex);
G_OBJECT_CLASS (sysprof_document_loader_parent_class)->finalize (object);
}
static void
sysprof_document_loader_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentLoader *self = SYSPROF_DOCUMENT_LOADER (object);
switch (prop_id)
{
case PROP_FRACTION:
g_value_set_double (value, sysprof_document_loader_get_fraction (self));
break;
case PROP_MESSAGE:
g_value_set_string (value, sysprof_document_loader_get_message (self));
break;
case PROP_SYMBOLIZER:
g_value_set_object (value, sysprof_document_loader_get_symbolizer (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_loader_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofDocumentLoader *self = SYSPROF_DOCUMENT_LOADER (object);
switch (prop_id)
{
case PROP_SYMBOLIZER:
sysprof_document_loader_set_symbolizer (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_loader_class_init (SysprofDocumentLoaderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_document_loader_finalize;
object_class->get_property = sysprof_document_loader_get_property;
object_class->set_property = sysprof_document_loader_set_property;
properties [PROP_FRACTION] =
g_param_spec_double ("fraction", NULL, NULL,
0, 1, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_MESSAGE] =
g_param_spec_string ("message", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_SYMBOLIZER] =
g_param_spec_object ("symbolizer", NULL, NULL,
SYSPROF_TYPE_SYMBOLIZER,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
g_type_ensure (SYSPROF_TYPE_DOCUMENT);
g_type_ensure (SYSPROF_TYPE_DOCUMENT_BITSET_INDEX);
}
static void
sysprof_document_loader_init (SysprofDocumentLoader *self)
{
g_mutex_init (&self->mutex);
self->fd = -1;
set_default_symbolizer (self);
}
SysprofDocumentLoader *
sysprof_document_loader_new (const char *filename)
{
SysprofDocumentLoader *self;
g_return_val_if_fail (filename != NULL, NULL);
self = g_object_new (SYSPROF_TYPE_DOCUMENT_LOADER, NULL);
self->filename = g_strdup (filename);
return self;
}
SysprofDocumentLoader *
sysprof_document_loader_new_for_fd (int fd,
GError **error)
{
g_autoptr(SysprofDocumentLoader) self = NULL;
self = g_object_new (SYSPROF_TYPE_DOCUMENT_LOADER, NULL);
if (-1 == (self->fd = dup (fd)))
{
int errsv = errno;
g_set_error_literal (error,
G_IO_ERROR,
g_io_error_from_errno (errsv),
g_strerror (errsv));
return NULL;
}
return g_steal_pointer (&self);
}
/**
* sysprof_document_loader_get_symbolizer:
* @self: a #SysprofDocumentLoader
*
* Gets the #SysprofSymbolizer to use to symbolize traces within
* the document.
*
* Returns: (transfer none) (nullable): a #SysprofSymbolizer or %NULL
*/
SysprofSymbolizer *
sysprof_document_loader_get_symbolizer (SysprofDocumentLoader *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self), NULL);
return self->symbolizer;
}
/**
* sysprof_document_loader_set_symbolizer:
* @self: a #SysprofDocumentLoader
* @symbolizer: (nullable): a #SysprofSymbolizer or %NULL
*
* Sets the symbolizer to use to convert instruction pointers to
* symbol names in the document.
*
* If set to %NULL, a sensible default will be chosen when loading.
*/
void
sysprof_document_loader_set_symbolizer (SysprofDocumentLoader *self,
SysprofSymbolizer *symbolizer)
{
g_return_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self));
if (g_set_object (&self->symbolizer, symbolizer))
{
if (self->symbolizer == NULL)
set_default_symbolizer (self);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SYMBOLIZER]);
}
}
/**
* sysprof_document_loader_get_fraction:
* @self: a #SysprofDocumentLoader
*
* Gets the fraction between 0 and 1 for the loading that has occurred.
*
* Returns: A value between 0 and 1.
*/
double
sysprof_document_loader_get_fraction (SysprofDocumentLoader *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self), .0);
return self->fraction;
}
/**
* sysprof_document_loader_get_message:
* @self: a #SysprofDocumentLoader
*
* Gets a text message representing what is happenin with loading.
*
* This only updates between calls of sysprof_document_loader_load_async()
* and sysprof_document_loader_load_finish().
*
* Returns: (nullable): a string containing a load message
*/
const char *
sysprof_document_loader_get_message (SysprofDocumentLoader *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self), NULL);
return self->message;
}
static void
sysprof_document_loader_load_symbols_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofDocument *document = (SysprofDocument *)object;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = user_data;
SysprofDocumentLoader *self;
g_assert (SYSPROF_IS_DOCUMENT (document));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
set_progress (0., _("Document loaded"), self);
if (!_sysprof_document_symbolize_finish (document, result, &error))
g_task_return_error (task, g_steal_pointer (&error));
else
g_task_return_pointer (task, g_object_ref (document), g_object_unref);
}
static void
sysprof_document_loader_load_document_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(SysprofDocument) document = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = user_data;
SysprofDocumentLoader *self;
SysprofSymbolizer *symbolizer;
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
symbolizer = g_task_get_task_data (task);
g_assert (symbolizer != NULL);
g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer));
if (!(document = _sysprof_document_new_finish (result, &error)))
{
g_task_return_error (task, g_steal_pointer (&error));
set_progress (1., _("Loading failed"), self);
return;
}
if (self->filename != NULL)
{
g_autofree char *title = g_path_get_basename (self->filename);
_sysprof_document_set_title (document, title);
}
self->symbolizing = TRUE;
set_progress (.0, _("Symbolizing stack traces"), self);
_sysprof_document_symbolize_async (document,
symbolizer,
set_progress,
g_object_ref (self),
g_object_unref,
g_task_get_cancellable (task),
sysprof_document_loader_load_symbols_cb,
g_object_ref (task));
}
static void
sysprof_document_loader_load_mapped_file_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(GMappedFile) mapped_file = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = user_data;
SysprofDocumentLoader *self;
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
if (!(mapped_file = mapped_file_new_finish (result, &error)))
g_task_return_error (task, g_steal_pointer (&error));
else
_sysprof_document_new_async (mapped_file,
set_progress,
g_object_ref (self),
g_object_unref,
g_task_get_cancellable (task),
sysprof_document_loader_load_document_cb,
g_object_ref (task));
}
/**
* sysprof_document_loader_load_async:
* @self: a #SysprofDocumentLoader
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: a callback to execute upon completion
* @user_data: closure data for @callback
*
* Asynchronously loads the document.
*
* @callback should call sysprof_document_loader_load_finish().
*/
void
sysprof_document_loader_load_async (SysprofDocumentLoader *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(SysprofSymbolizer) symbolizer = NULL;
g_autoptr(GMappedFile) mapped_file = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GTask) task = NULL;
g_return_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
g_return_if_fail (self->filename != NULL || self->fd != -1);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, g_object_ref (self->symbolizer), g_object_unref);
g_task_set_source_tag (task, sysprof_document_loader_load_async);
set_progress (0., _("Loading document"), self);
if (self->fd != -1)
mapped_file_new_from_fd_async (self->fd,
cancellable,
sysprof_document_loader_load_mapped_file_cb,
g_steal_pointer (&task));
else
mapped_file_new_async (self->filename,
cancellable,
sysprof_document_loader_load_mapped_file_cb,
g_steal_pointer (&task));
}
/**
* sysprof_document_loader_load_finish:
* @self: a #SysprofDocumentLoader
* @result: a #GAsyncResult
* @error: a location for a #GError, or %NULL
*
* Completes a request to load a document asynchronously.
*
* Returns: (transfer full): a #SysprofDocumentLoader or %NULL
* and @error is set.
*/
SysprofDocument *
sysprof_document_loader_load_finish (SysprofDocumentLoader *self,
GAsyncResult *result,
GError **error)
{
SysprofDocument *ret;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self), NULL);
g_return_val_if_fail (G_IS_TASK (result), NULL);
g_return_val_if_fail (g_task_is_valid (result, self), NULL);
set_progress (1., NULL, self);
ret = g_task_propagate_pointer (G_TASK (result), error);
g_return_val_if_fail (!ret || SYSPROF_IS_DOCUMENT (ret), NULL);
return ret;
}
typedef struct SysprofDocumentLoaderSync
{
GMainContext *main_context;
SysprofDocument *document;
GError *error;
} SysprofDocumentLoaderSync;
static void
sysprof_document_loader_load_sync_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
SysprofDocumentLoader *loader = (SysprofDocumentLoader *)object;
SysprofDocumentLoaderSync *state = user_data;
g_assert (SYSPROF_IS_DOCUMENT_LOADER (loader));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (state != NULL);
g_assert (state->main_context != NULL);
state->document = sysprof_document_loader_load_finish (loader, result, &state->error);
g_main_context_wakeup (state->main_context);
}
/**
* sysprof_document_loader_load:
* @self: a #SysprofDocumentLoader
* @cancellable: (nullable): a #GCancellable or %NULL
* @error: a location for a #GError, or %NULL
*
* Synchronously loads the document.
*
* This function requires a #GMainContext to be set for the current
* thread and uses the asynchronously loader API underneath.
*
* Returns: (transfer full): a #SysprofDocument if successful; otherwise
* %NULL and @error is set.
*/
SysprofDocument *
sysprof_document_loader_load (SysprofDocumentLoader *self,
GCancellable *cancellable,
GError **error)
{
SysprofDocumentLoaderSync state;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self), NULL);
g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
state.main_context = g_main_context_ref_thread_default ();
state.document = NULL;
state.error = NULL;
sysprof_document_loader_load_async (self,
cancellable,
sysprof_document_loader_load_sync_cb,
&state);
while (state.document == NULL && state.error == NULL)
g_main_context_iteration (state.main_context, TRUE);
g_main_context_unref (state.main_context);
if (state.error != NULL)
g_propagate_error (error, state.error);
return state.document;
}

View File

@ -0,0 +1,65 @@
/* sysprof-document-loader.h
*
* Copyright 2023 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
*/
#pragma once
#include <gio/gio.h>
#include <sysprof-capture.h>
#include "sysprof-document.h"
#include "sysprof-symbolizer.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_LOADER (sysprof_document_loader_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofDocumentLoader, sysprof_document_loader, SYSPROF, DOCUMENT_LOADER, GObject)
SYSPROF_AVAILABLE_IN_ALL
SysprofDocumentLoader *sysprof_document_loader_new (const char *filename);
SYSPROF_AVAILABLE_IN_ALL
SysprofDocumentLoader *sysprof_document_loader_new_for_fd (int fd,
GError **error);
SYSPROF_AVAILABLE_IN_ALL
SysprofSymbolizer *sysprof_document_loader_get_symbolizer (SysprofDocumentLoader *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_document_loader_set_symbolizer (SysprofDocumentLoader *self,
SysprofSymbolizer *symbolizer);
SYSPROF_AVAILABLE_IN_ALL
double sysprof_document_loader_get_fraction (SysprofDocumentLoader *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_loader_get_message (SysprofDocumentLoader *self);
SYSPROF_AVAILABLE_IN_ALL
SysprofDocument *sysprof_document_loader_load (SysprofDocumentLoader *self,
GCancellable *cancellable,
GError **error);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_document_loader_load_async (SysprofDocumentLoader *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
SysprofDocument *sysprof_document_loader_load_finish (SysprofDocumentLoader *self,
GAsyncResult *result,
GError **error);
G_END_DECLS

View File

@ -0,0 +1,144 @@
/* sysprof-document-log.c
*
* Copyright 2023 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 <glib/gi18n.h>
#include "sysprof-document-frame-private.h"
#include "sysprof-document-log.h"
struct _SysprofDocumentLog
{
SysprofDocumentFrame parent_instance;
};
struct _SysprofDocumentLogClass
{
SysprofDocumentFrameClass parent_class;
};
enum {
PROP_0,
PROP_DOMAIN,
PROP_MESSAGE,
PROP_SEVERITY,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDocumentLog, sysprof_document_log, SYSPROF_TYPE_DOCUMENT_FRAME)
static GParamSpec *properties [N_PROPS];
static void
sysprof_document_log_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentLog *self = SYSPROF_DOCUMENT_LOG (object);
switch (prop_id)
{
case PROP_SEVERITY:
g_value_set_uint (value, sysprof_document_log_get_severity (self));
break;
case PROP_MESSAGE:
g_value_set_string (value, sysprof_document_log_get_message (self));
break;
case PROP_DOMAIN:
g_value_set_string (value, sysprof_document_log_get_domain (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_log_class_init (SysprofDocumentLogClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
SysprofDocumentFrameClass *document_frame_class = SYSPROF_DOCUMENT_FRAME_CLASS (klass);
object_class->get_property = sysprof_document_log_get_property;
document_frame_class->type_name = N_("Log");
properties [PROP_SEVERITY] =
g_param_spec_uint ("severity", NULL, NULL,
0, G_MAXUINT16, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_DOMAIN] =
g_param_spec_string ("domain", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_MESSAGE] =
g_param_spec_string ("message", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_log_init (SysprofDocumentLog *self)
{
}
GLogLevelFlags
sysprof_document_log_get_severity (SysprofDocumentLog *self)
{
const SysprofCaptureLog *log;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOG (self), 0);
log = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureLog);
return SYSPROF_DOCUMENT_FRAME_UINT16 (self, log->severity);
}
const char *
sysprof_document_log_get_message (SysprofDocumentLog *self)
{
const SysprofCaptureLog *log;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOG (self), 0);
log = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureLog);
return SYSPROF_DOCUMENT_FRAME_CSTRING (self, log->message);
}
const char *
sysprof_document_log_get_domain (SysprofDocumentLog *self)
{
const SysprofCaptureLog *log;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOG (self), 0);
log = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureLog);
return SYSPROF_DOCUMENT_FRAME_CSTRING (self, log->domain);
}

View File

@ -0,0 +1,46 @@
/* sysprof-document-log.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_LOG (sysprof_document_log_get_type())
#define SYSPROF_IS_DOCUMENT_LOG(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_LOG)
#define SYSPROF_DOCUMENT_LOG(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_LOG, SysprofDocumentLog)
#define SYSPROF_DOCUMENT_LOG_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_LOG, SysprofDocumentLogClass)
typedef struct _SysprofDocumentLog SysprofDocumentLog;
typedef struct _SysprofDocumentLogClass SysprofDocumentLogClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_log_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_log_get_message (SysprofDocumentLog *self);
SYSPROF_AVAILABLE_IN_ALL
GLogLevelFlags sysprof_document_log_get_severity (SysprofDocumentLog *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_log_get_domain (SysprofDocumentLog *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentLog, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,204 @@
/* sysprof-document-mark.c
*
* Copyright 2023 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 <glib/gi18n.h>
#include "sysprof-document-frame-private.h"
#include "sysprof-document-mark.h"
struct _SysprofDocumentMark
{
SysprofDocumentFrame parent_instance;
};
struct _SysprofDocumentMarkClass
{
SysprofDocumentFrameClass parent_class;
};
enum {
PROP_0,
PROP_DURATION,
PROP_END_TIME,
PROP_GROUP,
PROP_MESSAGE,
PROP_NAME,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDocumentMark, sysprof_document_mark, SYSPROF_TYPE_DOCUMENT_FRAME)
static GParamSpec *properties [N_PROPS];
static char *
sysprof_document_mark_dup_tooltip (SysprofDocumentFrame *frame)
{
SysprofDocumentMark *self = (SysprofDocumentMark *)frame;
g_autofree char *time_string = NULL;
g_assert (SYSPROF_IS_DOCUMENT_MARK (self));
time_string = sysprof_document_frame_dup_time_string (SYSPROF_DOCUMENT_FRAME (self));
return g_strdup_printf ("%s: %s: %s: %s",
time_string,
sysprof_document_mark_get_group (self),
sysprof_document_mark_get_name (self),
sysprof_document_mark_get_message (self));
}
static void
sysprof_document_mark_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentMark *self = SYSPROF_DOCUMENT_MARK (object);
switch (prop_id)
{
case PROP_DURATION:
g_value_set_int64 (value, sysprof_document_mark_get_duration (self));
break;
case PROP_END_TIME:
g_value_set_int64 (value, sysprof_document_mark_get_end_time (self));
break;
case PROP_NAME:
g_value_set_string (value, sysprof_document_mark_get_name (self));
break;
case PROP_GROUP:
g_value_set_string (value, sysprof_document_mark_get_group (self));
break;
case PROP_MESSAGE:
g_value_set_string (value, sysprof_document_mark_get_message (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_mark_class_init (SysprofDocumentMarkClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
SysprofDocumentFrameClass *document_frame_class = SYSPROF_DOCUMENT_FRAME_CLASS (klass);
object_class->get_property = sysprof_document_mark_get_property;
document_frame_class->type_name = N_("Mark");
document_frame_class->dup_tooltip = sysprof_document_mark_dup_tooltip;
properties [PROP_DURATION] =
g_param_spec_int64 ("duration", NULL, NULL,
G_MININT64, G_MAXINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_END_TIME] =
g_param_spec_int64 ("end-time", NULL, NULL,
G_MININT64, G_MAXINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_GROUP] =
g_param_spec_string ("group", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_NAME] =
g_param_spec_string ("name", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_MESSAGE] =
g_param_spec_string ("message", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_mark_init (SysprofDocumentMark *self)
{
}
gint64
sysprof_document_mark_get_duration (SysprofDocumentMark *self)
{
const SysprofCaptureMark *mark;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MARK (self), 0);
mark = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMark);
return SYSPROF_DOCUMENT_FRAME_INT64 (self, mark->duration);
}
const char *
sysprof_document_mark_get_group (SysprofDocumentMark *self)
{
const SysprofCaptureMark *mark;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MARK (self), 0);
mark = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMark);
return SYSPROF_DOCUMENT_FRAME_CSTRING (self, mark->group);
}
const char *
sysprof_document_mark_get_name (SysprofDocumentMark *self)
{
const SysprofCaptureMark *mark;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MARK (self), 0);
mark = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMark);
return SYSPROF_DOCUMENT_FRAME_CSTRING (self, mark->name);
}
const char *
sysprof_document_mark_get_message (SysprofDocumentMark *self)
{
const SysprofCaptureMark *mark;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MARK (self), 0);
mark = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMark);
return SYSPROF_DOCUMENT_FRAME_CSTRING (self, mark->message);
}
gint64
sysprof_document_mark_get_end_time (SysprofDocumentMark *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MARK (self), 0);
return sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (self))
+ sysprof_document_mark_get_duration (self);
}

View File

@ -0,0 +1,51 @@
/* sysprof-document-mark.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_MARK (sysprof_document_mark_get_type())
#define SYSPROF_IS_DOCUMENT_MARK(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_MARK)
#define SYSPROF_DOCUMENT_MARK(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_MARK, SysprofDocumentMark)
#define SYSPROF_DOCUMENT_MARK_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_MARK, SysprofDocumentMarkClass)
typedef struct _SysprofDocumentMark SysprofDocumentMark;
typedef struct _SysprofDocumentMarkClass SysprofDocumentMarkClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_mark_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
gint64 sysprof_document_mark_get_duration (SysprofDocumentMark *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_mark_get_group (SysprofDocumentMark *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_mark_get_name (SysprofDocumentMark *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_mark_get_message (SysprofDocumentMark *self);
SYSPROF_AVAILABLE_IN_ALL
gint64 sysprof_document_mark_get_end_time (SysprofDocumentMark *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentMark, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,117 @@
/* sysprof-document-metadata.c
*
* Copyright 2023 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 "sysprof-document-frame-private.h"
#include "sysprof-document-metadata.h"
struct _SysprofDocumentMetadata
{
SysprofDocumentFrame parent_instance;
};
struct _SysprofDocumentMetadataClass
{
SysprofDocumentFrameClass parent_class;
};
enum {
PROP_0,
PROP_ID,
PROP_VALUE,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDocumentMetadata, sysprof_document_metadata, SYSPROF_TYPE_DOCUMENT_FRAME)
static GParamSpec *properties [N_PROPS];
static void
sysprof_document_metadata_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentMetadata *self = SYSPROF_DOCUMENT_METADATA (object);
switch (prop_id)
{
case PROP_ID:
g_value_set_string (value, sysprof_document_metadata_get_id (self));
break;
case PROP_VALUE:
g_value_set_string (value, sysprof_document_metadata_get_value (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_metadata_class_init (SysprofDocumentMetadataClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = sysprof_document_metadata_get_property;
properties [PROP_ID] =
g_param_spec_string ("id", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_VALUE] =
g_param_spec_string ("value", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_metadata_init (SysprofDocumentMetadata *self)
{
}
const char *
sysprof_document_metadata_get_id (SysprofDocumentMetadata *self)
{
const SysprofCaptureMetadata *meta;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_METADATA (self), 0);
meta = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMetadata);
return SYSPROF_DOCUMENT_FRAME_CSTRING (self, meta->id);
}
const char *
sysprof_document_metadata_get_value (SysprofDocumentMetadata *self)
{
const SysprofCaptureMetadata *meta;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_METADATA (self), 0);
meta = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMetadata);
return SYSPROF_DOCUMENT_FRAME_CSTRING (self, meta->metadata);
}

View File

@ -0,0 +1,45 @@
/* sysprof-document-metadata.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_METADATA (sysprof_document_metadata_get_type())
#define SYSPROF_IS_DOCUMENT_METADATA(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_METADATA)
#define SYSPROF_DOCUMENT_METADATA(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_METADATA, SysprofDocumentMetadata)
#define SYSPROF_DOCUMENT_METADATA_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_METADATA, SysprofDocumentMetadataClass)
typedef struct _SysprofDocumentMetadata SysprofDocumentMetadata;
typedef struct _SysprofDocumentMetadataClass SysprofDocumentMetadataClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_metadata_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_metadata_get_id (SysprofDocumentMetadata *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_metadata_get_value (SysprofDocumentMetadata *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentMetadata, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,223 @@
/*
* sysprof-document-mmap.c
*
* Copyright 2023 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 "sysprof-document-frame-private.h"
#include "sysprof-document-mmap.h"
struct _SysprofDocumentMmap
{
SysprofDocumentFrame parent_instance;
};
struct _SysprofDocumentMmapClass
{
SysprofDocumentFrameClass parent_class;
};
enum {
PROP_0,
PROP_BUILD_ID,
PROP_END_ADDRESS,
PROP_FILE,
PROP_FILE_INODE,
PROP_FILE_OFFSET,
PROP_START_ADDRESS,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDocumentMmap, sysprof_document_mmap, SYSPROF_TYPE_DOCUMENT_FRAME)
static GParamSpec *properties [N_PROPS];
static void
sysprof_document_mmap_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentMmap *self = SYSPROF_DOCUMENT_MMAP (object);
switch (prop_id)
{
case PROP_START_ADDRESS:
g_value_set_uint64 (value, sysprof_document_mmap_get_start_address (self));
break;
case PROP_END_ADDRESS:
g_value_set_uint64 (value, sysprof_document_mmap_get_end_address (self));
break;
case PROP_FILE:
g_value_set_string (value, sysprof_document_mmap_get_file (self));
break;
case PROP_FILE_OFFSET:
g_value_set_uint64 (value, sysprof_document_mmap_get_file_offset (self));
break;
case PROP_FILE_INODE:
g_value_set_uint64 (value, sysprof_document_mmap_get_file_inode (self));
break;
case PROP_BUILD_ID:
g_value_set_string (value, sysprof_document_mmap_get_build_id (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_mmap_class_init (SysprofDocumentMmapClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = sysprof_document_mmap_get_property;
properties [PROP_START_ADDRESS] =
g_param_spec_uint64 ("start-address", NULL, NULL,
0, G_MAXUINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_END_ADDRESS] =
g_param_spec_uint64 ("end-address", NULL, NULL,
0, G_MAXUINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_FILE] =
g_param_spec_string ("file", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_FILE_INODE] =
g_param_spec_uint64 ("file-inode", NULL, NULL,
0, G_MAXUINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_FILE_OFFSET] =
g_param_spec_uint64 ("file-offset", NULL, NULL,
0, G_MAXUINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_BUILD_ID] =
g_param_spec_string ("build-id", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_mmap_init (SysprofDocumentMmap *self)
{
}
guint64
sysprof_document_mmap_get_start_address (SysprofDocumentMmap *self)
{
const SysprofCaptureMap *mmap;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), 0);
mmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMap);
return SYSPROF_DOCUMENT_FRAME_UINT64 (self, mmap->start);
}
guint64
sysprof_document_mmap_get_end_address (SysprofDocumentMmap *self)
{
const SysprofCaptureMap *mmap;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), 0);
mmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMap);
return SYSPROF_DOCUMENT_FRAME_UINT64 (self, mmap->end);
}
guint64
sysprof_document_mmap_get_file_inode (SysprofDocumentMmap *self)
{
const SysprofCaptureMap *mmap;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), 0);
mmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMap);
return SYSPROF_DOCUMENT_FRAME_UINT64 (self, mmap->inode);
}
guint64
sysprof_document_mmap_get_file_offset (SysprofDocumentMmap *self)
{
const SysprofCaptureMap *mmap;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), 0);
mmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMap);
return SYSPROF_DOCUMENT_FRAME_UINT64 (self, mmap->offset);
}
const char *
sysprof_document_mmap_get_file (SysprofDocumentMmap *self)
{
const SysprofCaptureMap *mmap;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), NULL);
mmap = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureMap);
return SYSPROF_DOCUMENT_FRAME_CSTRING (self, mmap->filename);
}
const char *
sysprof_document_mmap_get_build_id (SysprofDocumentMmap *self)
{
const char *file;
const char *build_id;
gsize len;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_MMAP (self), NULL);
if (!(file = sysprof_document_mmap_get_file (self)))
return NULL;
/* The build-id may be tacked on after the filename if after the
* Nil byte we get '@'. SYSPROF_DOCUMENT_FRAME_CSTRING() will check
* for bounds so we can feed it a position we don't know is part
* of our frame or not. We expect "FILE\0@BUILD_ID_IN_HEX\0".
*/
len = strlen (file);
if (!(build_id = SYSPROF_DOCUMENT_FRAME_CSTRING (self, &file[len+1])))
return NULL;
if (build_id[0] != '@')
return NULL;
return &build_id[1];
}

View File

@ -0,0 +1,53 @@
/*
* sysprof-document-mmap.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_MMAP (sysprof_document_mmap_get_type())
#define SYSPROF_IS_DOCUMENT_MMAP(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_MMAP)
#define SYSPROF_DOCUMENT_MMAP(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_MMAP, SysprofDocumentMmap)
#define SYSPROF_DOCUMENT_MMAP_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_MMAP, SysprofDocumentMmapClass)
typedef struct _SysprofDocumentMmap SysprofDocumentMmap;
typedef struct _SysprofDocumentMmapClass SysprofDocumentMmapClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_mmap_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
guint64 sysprof_document_mmap_get_start_address (SysprofDocumentMmap *self);
SYSPROF_AVAILABLE_IN_ALL
guint64 sysprof_document_mmap_get_end_address (SysprofDocumentMmap *self);
SYSPROF_AVAILABLE_IN_ALL
guint64 sysprof_document_mmap_get_file_inode (SysprofDocumentMmap *self);
SYSPROF_AVAILABLE_IN_ALL
guint64 sysprof_document_mmap_get_file_offset (SysprofDocumentMmap *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_mmap_get_file (SysprofDocumentMmap *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_mmap_get_build_id (SysprofDocumentMmap *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentMmap, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,142 @@
/* sysprof-document-overlay.c
*
* Copyright 2023 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 "sysprof-document-frame-private.h"
#include "sysprof-document-overlay.h"
struct _SysprofDocumentOverlay
{
SysprofDocumentFrame parent_instance;
};
struct _SysprofDocumentOverlayClass
{
SysprofDocumentFrameClass parent_class;
};
enum {
PROP_0,
PROP_DESTINATION,
PROP_LAYER,
PROP_SOURCE,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDocumentOverlay, sysprof_document_overlay, SYSPROF_TYPE_DOCUMENT_FRAME)
static GParamSpec *properties [N_PROPS];
static void
sysprof_document_overlay_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentOverlay *self = SYSPROF_DOCUMENT_OVERLAY (object);
switch (prop_id)
{
case PROP_DESTINATION:
g_value_set_string (value, sysprof_document_overlay_get_destination (self));
break;
case PROP_LAYER:
g_value_set_uint (value, sysprof_document_overlay_get_layer (self));
break;
case PROP_SOURCE:
g_value_set_string (value, sysprof_document_overlay_get_source (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_overlay_class_init (SysprofDocumentOverlayClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = sysprof_document_overlay_get_property;
properties [PROP_LAYER] =
g_param_spec_uint ("layer", NULL, NULL,
0, G_MAXUINT, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_DESTINATION] =
g_param_spec_string ("destination", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_SOURCE] =
g_param_spec_string ("source", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_overlay_init (SysprofDocumentOverlay *self)
{
}
guint
sysprof_document_overlay_get_layer (SysprofDocumentOverlay *self)
{
const SysprofCaptureOverlay *overlay;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_OVERLAY (self), 0);
overlay = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureOverlay);
return overlay->layer;
}
const char *
sysprof_document_overlay_get_source (SysprofDocumentOverlay *self)
{
const SysprofCaptureOverlay *overlay;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_OVERLAY (self), 0);
overlay = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureOverlay);
return SYSPROF_DOCUMENT_FRAME_CSTRING (self, overlay->data);
}
const char *
sysprof_document_overlay_get_destination (SysprofDocumentOverlay *self)
{
const SysprofCaptureOverlay *overlay;
guint16 offset;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_OVERLAY (self), 0);
overlay = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureOverlay);
offset = SYSPROF_DOCUMENT_FRAME_UINT16 (self, overlay->src_len);
return SYSPROF_DOCUMENT_FRAME_CSTRING (self, &overlay->data[offset+1]);
}

View File

@ -0,0 +1,47 @@
/* sysprof-document-overlay.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_OVERLAY (sysprof_document_overlay_get_type())
#define SYSPROF_IS_DOCUMENT_OVERLAY(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_OVERLAY)
#define SYSPROF_DOCUMENT_OVERLAY(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_OVERLAY, SysprofDocumentOverlay)
#define SYSPROF_DOCUMENT_OVERLAY_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_OVERLAY, SysprofDocumentOverlayClass)
typedef struct _SysprofDocumentOverlay SysprofDocumentOverlay;
typedef struct _SysprofDocumentOverlayClass SysprofDocumentOverlayClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_overlay_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_document_overlay_get_layer (SysprofDocumentOverlay *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_overlay_get_source (SysprofDocumentOverlay *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_overlay_get_destination (SysprofDocumentOverlay *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentOverlay, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,78 @@
/* sysprof-document-private.h
*
* Copyright 2023 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
*/
#pragma once
#include <eggbitset.h>
#include "sysprof-document.h"
#include "sysprof-symbolizer.h"
#include "sysprof-symbol.h"
G_BEGIN_DECLS
typedef struct _SysprofDocumentTimedValue
{
gint64 time;
union {
gint64 v_int64;
double v_double;
guint8 v_raw[8];
};
} SysprofDocumentTimedValue;
typedef void (*ProgressFunc) (double fraction,
const char *message,
gpointer user_data);
void _sysprof_document_new_async (GMappedFile *mapped_file,
ProgressFunc progress,
gpointer progress_data,
GDestroyNotify progress_data_destroy,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SysprofDocument *_sysprof_document_new_finish (GAsyncResult *result,
GError **error);
void _sysprof_document_set_title (SysprofDocument *self,
const char *title);
void _sysprof_document_symbolize_async (SysprofDocument *self,
SysprofSymbolizer *symbolizer,
ProgressFunc progress_func,
gpointer progress_data,
GDestroyNotify progress_data_destroy,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean _sysprof_document_symbolize_finish (SysprofDocument *self,
GAsyncResult *result,
GError **error);
gboolean _sysprof_document_is_native (SysprofDocument *self);
GRefString *_sysprof_document_ref_string (SysprofDocument *self,
const char *name);
EggBitset *_sysprof_document_traceables (SysprofDocument *self);
SysprofSymbol *_sysprof_document_process_symbol (SysprofDocument *self,
int pid);
SysprofSymbol *_sysprof_document_thread_symbol (SysprofDocument *self,
int pid,
int tid);
SysprofSymbol *_sysprof_document_kernel_symbol (SysprofDocument *self);
G_END_DECLS

View File

@ -0,0 +1,32 @@
/* sysprof-document-process-private.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-process.h"
#include "sysprof-process-info-private.h"
G_BEGIN_DECLS
SysprofProcessInfo *_sysprof_document_process_get_info (SysprofDocumentProcess *self);
void _sysprof_document_process_set_info (SysprofDocumentProcess *self,
SysprofProcessInfo *process_info);
G_END_DECLS

View File

@ -0,0 +1,323 @@
/* sysprof-document-process.c
*
* Copyright 2023 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 <glib/gi18n.h>
#include "sysprof-document-frame-private.h"
#include "sysprof-document-process-private.h"
#include "sysprof-mount.h"
#include "sysprof-thread-info.h"
struct _SysprofDocumentProcess
{
SysprofDocumentFrame parent_instance;
SysprofProcessInfo *process_info;
};
struct _SysprofDocumentProcessClass
{
SysprofDocumentFrameClass parent_class;
};
enum {
PROP_0,
PROP_COMMAND_LINE,
PROP_DURATION,
PROP_MEMORY_MAPS,
PROP_MOUNTS,
PROP_EXIT_TIME,
PROP_THREADS,
PROP_TITLE,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDocumentProcess, sysprof_document_process, SYSPROF_TYPE_DOCUMENT_FRAME)
static GParamSpec *properties [N_PROPS];
static void
sysprof_document_process_finalize (GObject *object)
{
SysprofDocumentProcess *self = (SysprofDocumentProcess *)object;
g_clear_pointer (&self->process_info, sysprof_process_info_unref);
G_OBJECT_CLASS (sysprof_document_process_parent_class)->finalize (object);
}
static void
sysprof_document_process_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentProcess *self = SYSPROF_DOCUMENT_PROCESS (object);
switch (prop_id)
{
case PROP_COMMAND_LINE:
g_value_set_string (value, sysprof_document_process_get_command_line (self));
break;
case PROP_DURATION:
g_value_set_int64 (value, sysprof_document_process_get_duration (self));
break;
case PROP_EXIT_TIME:
g_value_set_int64 (value, sysprof_document_process_get_exit_time (self));
break;
case PROP_MEMORY_MAPS:
g_value_take_object (value, sysprof_document_process_list_memory_maps (self));
break;
case PROP_MOUNTS:
g_value_take_object (value, sysprof_document_process_list_mounts (self));
break;
case PROP_THREADS:
g_value_take_object (value, sysprof_document_process_list_threads (self));
break;
case PROP_TITLE:
g_value_take_string (value, sysprof_document_process_dup_title (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_process_class_init (SysprofDocumentProcessClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_document_process_finalize;
object_class->get_property = sysprof_document_process_get_property;
properties [PROP_COMMAND_LINE] =
g_param_spec_string ("command-line", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_DURATION] =
g_param_spec_int64 ("duration", NULL, NULL,
G_MININT64, G_MAXINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_EXIT_TIME] =
g_param_spec_int64 ("exit-time", NULL, NULL,
G_MININT64, G_MAXINT64, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_MEMORY_MAPS] =
g_param_spec_object ("memory-maps", NULL, NULL,
G_TYPE_LIST_MODEL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_MOUNTS] =
g_param_spec_object ("mounts", NULL, NULL,
G_TYPE_LIST_MODEL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_THREADS] =
g_param_spec_object ("threads", NULL, NULL,
G_TYPE_LIST_MODEL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_TITLE] =
g_param_spec_string ("title", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_process_init (SysprofDocumentProcess *self)
{
}
const char *
sysprof_document_process_get_command_line (SysprofDocumentProcess *self)
{
const SysprofCaptureProcess *proc;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), 0);
proc = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureProcess);
return SYSPROF_DOCUMENT_FRAME_CSTRING (self, proc->cmdline);
}
/**
* sysprof_document_process_list_memory_maps:
* @self: a #SysprofDocumentProcess
*
* Lists the #SysprofDocumentMmap that are associated with the process.
*
* Returns: (transfer full): a #GListModel of #SysprofDocumentMmap
*/
GListModel *
sysprof_document_process_list_memory_maps (SysprofDocumentProcess *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), NULL);
if (self->process_info == NULL)
return G_LIST_MODEL (g_list_store_new (SYSPROF_TYPE_DOCUMENT_MMAP));
return g_object_ref (G_LIST_MODEL (self->process_info->address_layout));
}
/**
* sysprof_document_process_list_mounts:
* @self: a #SysprofDocumentProcess
*
* Lists the #SysprofMount that are associated with the process.
*
* Returns: (transfer full): a #GListModel of #SysprofMount
*/
GListModel *
sysprof_document_process_list_mounts (SysprofDocumentProcess *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), NULL);
if (self->process_info == NULL)
return G_LIST_MODEL (g_list_store_new (SYSPROF_TYPE_MOUNT));
return g_object_ref (G_LIST_MODEL (self->process_info->mount_namespace));
}
SysprofProcessInfo *
_sysprof_document_process_get_info (SysprofDocumentProcess *self)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), NULL);
return self->process_info;
}
void
_sysprof_document_process_set_info (SysprofDocumentProcess *self,
SysprofProcessInfo *process_info)
{
g_return_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self));
g_return_if_fail (process_info != NULL);
g_return_if_fail (self->process_info == NULL);
self->process_info = sysprof_process_info_ref (process_info);
}
gint64
sysprof_document_process_get_exit_time (SysprofDocumentProcess *self)
{
gint64 exit_time = 0;
gint64 t;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), 0);
if (self->process_info != NULL)
exit_time = self->process_info->exit_time;
t = sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (self));
return MAX (t, exit_time);
}
gint64
sysprof_document_process_get_duration (SysprofDocumentProcess *self)
{
gint64 t;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), 0);
t = sysprof_document_frame_get_time (SYSPROF_DOCUMENT_FRAME (self));
return sysprof_document_process_get_exit_time (self) - t;
}
/**
* sysprof_document_process_dup_title:
* @self: a #SysprofDocumentProcess
*
* Gets a suitable title for the process.
*
* Returns: (transfer full): a string containing a process title
*/
char *
sysprof_document_process_dup_title (SysprofDocumentProcess *self)
{
const char *command_line;
int pid;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), NULL);
pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (self));
if ((command_line = sysprof_document_process_get_command_line (self)))
return g_strdup_printf (_("%s [Process %d]"), command_line, pid);
return g_strdup_printf (_("Process %d"), pid);
}
/**
* sysprof_document_process_list_threads:
* @self: a #SysprofDocumentProcess
*
* Gets the list of threads for the process.
*
* Returns: (transfer full): a #GListModel of #SysprofThreadInfo.
*/
GListModel *
sysprof_document_process_list_threads (SysprofDocumentProcess *self)
{
GListStore *store;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_PROCESS (self), NULL);
store = g_list_store_new (SYSPROF_TYPE_THREAD_INFO);
if (self->process_info != NULL)
{
g_autoptr(GPtrArray) threads = g_ptr_array_new_with_free_func (g_object_unref);
EggBitsetIter iter;
guint i;
if (egg_bitset_iter_init_first (&iter, self->process_info->thread_ids, &i))
{
do
{
g_ptr_array_add (threads,
g_object_new (SYSPROF_TYPE_THREAD_INFO,
"process", self,
"thread-id", i,
NULL));
}
while (egg_bitset_iter_next (&iter, &i));
}
if (threads->len > 0)
g_list_store_splice (store, 0, 0, threads->pdata, threads->len);
}
return G_LIST_MODEL (store);
}

View File

@ -0,0 +1,57 @@
/* sysprof-document-process.h
*
* Copyright 2023 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
*/
#pragma once
#include <gio/gio.h>
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_PROCESS (sysprof_document_process_get_type())
#define SYSPROF_IS_DOCUMENT_PROCESS(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_PROCESS)
#define SYSPROF_DOCUMENT_PROCESS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_PROCESS, SysprofDocumentProcess)
#define SYSPROF_DOCUMENT_PROCESS_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_PROCESS, SysprofDocumentProcessClass)
typedef struct _SysprofDocumentProcess SysprofDocumentProcess;
typedef struct _SysprofDocumentProcessClass SysprofDocumentProcessClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_process_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_document_process_get_command_line (SysprofDocumentProcess *self);
SYSPROF_AVAILABLE_IN_ALL
gint64 sysprof_document_process_get_exit_time (SysprofDocumentProcess *self);
SYSPROF_AVAILABLE_IN_ALL
gint64 sysprof_document_process_get_duration (SysprofDocumentProcess *self);
SYSPROF_AVAILABLE_IN_ALL
char *sysprof_document_process_dup_title (SysprofDocumentProcess *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_process_list_memory_maps (SysprofDocumentProcess *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_process_list_mounts (SysprofDocumentProcess *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_process_list_threads (SysprofDocumentProcess *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentProcess, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,170 @@
/* sysprof-document-sample.c
*
* Copyright 2023 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 <glib/gi18n.h>
#include "sysprof-document-frame-private.h"
#include "sysprof-document-sample.h"
#include "sysprof-document-traceable.h"
struct _SysprofDocumentSample
{
SysprofDocumentFrame parent_instance;
};
struct _SysprofDocumentSampleClass
{
SysprofDocumentFrameClass parent_class;
};
enum {
PROP_0,
PROP_STACK_DEPTH,
PROP_THREAD_ID,
N_PROPS
};
static guint
sysprof_document_sample_get_stack_depth (SysprofDocumentTraceable *traceable)
{
const SysprofCaptureSample *sample = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureSample);
return SYSPROF_DOCUMENT_FRAME_UINT16 (traceable, sample->n_addrs);
}
static guint64
sysprof_document_sample_get_stack_address (SysprofDocumentTraceable *traceable,
guint position)
{
const SysprofCaptureSample *sample = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureSample);
return SYSPROF_DOCUMENT_FRAME_UINT16 (traceable, sample->addrs[position]);
}
static guint
sysprof_document_sample_get_stack_addresses (SysprofDocumentTraceable *traceable,
guint64 *addresses,
guint n_addresses)
{
const SysprofCaptureSample *sample = SYSPROF_DOCUMENT_FRAME_GET (traceable, SysprofCaptureSample);
guint depth = MIN (n_addresses, SYSPROF_DOCUMENT_FRAME_UINT16 (traceable, sample->n_addrs));
for (guint i = 0; i < depth; i++)
addresses[i] = SYSPROF_DOCUMENT_FRAME_UINT64 (traceable, sample->addrs[i]);
return depth;
}
static int
sysprof_document_sample_get_thread_id (SysprofDocumentTraceable *traceable)
{
SysprofDocumentSample *self = (SysprofDocumentSample *)traceable;
const SysprofCaptureSample *sample;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_SAMPLE (self), -1);
sample = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureSample);
return SYSPROF_DOCUMENT_FRAME_INT32 (self, sample->tid);
}
static void
traceable_iface_init (SysprofDocumentTraceableInterface *iface)
{
iface->get_stack_depth = sysprof_document_sample_get_stack_depth;
iface->get_stack_address = sysprof_document_sample_get_stack_address;
iface->get_stack_addresses = sysprof_document_sample_get_stack_addresses;
iface->get_thread_id = sysprof_document_sample_get_thread_id;
}
G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofDocumentSample, sysprof_document_sample, SYSPROF_TYPE_DOCUMENT_FRAME,
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_DOCUMENT_TRACEABLE, traceable_iface_init))
static GParamSpec *properties [N_PROPS];
static void
sysprof_document_sample_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDocumentSample *self = SYSPROF_DOCUMENT_SAMPLE (object);
switch (prop_id)
{
case PROP_STACK_DEPTH:
g_value_set_uint (value, sysprof_document_traceable_get_stack_depth (SYSPROF_DOCUMENT_TRACEABLE (self)));
break;
case PROP_THREAD_ID:
g_value_set_int (value, sysprof_document_sample_get_thread_id (SYSPROF_DOCUMENT_TRACEABLE (self)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_document_sample_class_init (SysprofDocumentSampleClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
SysprofDocumentFrameClass *document_frame_class = SYSPROF_DOCUMENT_FRAME_CLASS (klass);
object_class->get_property = sysprof_document_sample_get_property;
document_frame_class->type_name = N_("Sample");
/**
* SysprofDocumentSample:thread-id:
*
* The thread-id where the sample occurred.
*
* On Linux, this is generally set to the value of gettid().
*
* Since: 45
*/
properties [PROP_THREAD_ID] =
g_param_spec_int ("thread-id", NULL, NULL,
-1, G_MAXINT32, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* SysprofDocumentSample:stack-depth:
*
* The depth of the stack trace.
*
* Since: 45
*/
properties [PROP_STACK_DEPTH] =
g_param_spec_uint ("stack-depth", NULL, NULL,
0, G_MAXUINT32, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_document_sample_init (SysprofDocumentSample *self)
{
}

View File

@ -0,0 +1,42 @@
/* sysprof-document-sample.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_SAMPLE (sysprof_document_sample_get_type())
#define SYSPROF_IS_DOCUMENT_SAMPLE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_SAMPLE)
#define SYSPROF_DOCUMENT_SAMPLE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_SAMPLE, SysprofDocumentSample)
#define SYSPROF_DOCUMENT_SAMPLE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_SAMPLE, SysprofDocumentSampleClass)
typedef struct _SysprofDocumentSample SysprofDocumentSample;
typedef struct _SysprofDocumentSampleClass SysprofDocumentSampleClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_document_sample_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
int sysprof_document_sample_get_tid (SysprofDocumentSample *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentSample, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,59 @@
/* sysprof-document-symbols-private.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document.h"
#include "sysprof-process-info-private.h"
#include "sysprof-symbol-cache-private.h"
#include "sysprof-symbol.h"
#include "sysprof-symbolizer.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_SYMBOLS (sysprof_document_symbols_get_type())
G_DECLARE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, SYSPROF, DOCUMENT_SYMBOLS, GObject)
struct _SysprofDocumentSymbols
{
GObject parent_instance;
SysprofSymbol *context_switches[SYSPROF_ADDRESS_CONTEXT_GUEST_USER+1];
SysprofSymbolCache *kernel_symbols;
};
void _sysprof_document_symbols_new (SysprofDocument *document,
SysprofStrings *strings,
SysprofSymbolizer *symbolizer,
GHashTable *pid_to_process_info,
ProgressFunc progress_func,
gpointer progress_data,
GDestroyNotify progress_data_destroy,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SysprofDocumentSymbols *_sysprof_document_symbols_new_finish (GAsyncResult *result,
GError **error);
SysprofSymbol *_sysprof_document_symbols_lookup (SysprofDocumentSymbols *symbols,
const SysprofProcessInfo *process_info,
SysprofAddressContext context,
SysprofAddress address);
G_END_DECLS

View File

@ -0,0 +1,307 @@
/* sysprof-document-symbols.c
*
* Copyright 2023 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 <glib/gi18n.h>
#include "sysprof-address-layout-private.h"
#include "sysprof-document-private.h"
#include "sysprof-document-symbols-private.h"
#include "sysprof-document-traceable.h"
#include "sysprof-mount-namespace-private.h"
#include "sysprof-no-symbolizer.h"
#include "sysprof-symbol-private.h"
#include "sysprof-symbolizer-private.h"
G_DEFINE_FINAL_TYPE (SysprofDocumentSymbols, sysprof_document_symbols, G_TYPE_OBJECT)
static void
sysprof_document_symbols_finalize (GObject *object)
{
SysprofDocumentSymbols *self = (SysprofDocumentSymbols *)object;
for (guint i = 0; i < G_N_ELEMENTS (self->context_switches); i++)
g_clear_object (&self->context_switches[i]);
g_clear_object (&self->kernel_symbols);
G_OBJECT_CLASS (sysprof_document_symbols_parent_class)->finalize (object);
}
static void
sysprof_document_symbols_class_init (SysprofDocumentSymbolsClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_document_symbols_finalize;
}
static void
sysprof_document_symbols_init (SysprofDocumentSymbols *self)
{
self->kernel_symbols = sysprof_symbol_cache_new ();
}
typedef struct _Symbolize
{
SysprofDocument *document;
SysprofSymbolizer *symbolizer;
SysprofDocumentSymbols *symbols;
SysprofStrings *strings;
GHashTable *pid_to_process_info;
ProgressFunc progress_func;
gpointer progress_data;
GDestroyNotify progress_data_destroy;
} Symbolize;
static void
symbolize_free (Symbolize *state)
{
if (state->progress_data_destroy)
state->progress_data_destroy (state->progress_data);
g_clear_object (&state->document);
g_clear_object (&state->symbolizer);
g_clear_object (&state->symbols);
g_clear_pointer (&state->strings, sysprof_strings_unref);
g_clear_pointer (&state->pid_to_process_info, g_hash_table_unref);
g_free (state);
}
static void
add_traceable (SysprofDocumentSymbols *self,
SysprofStrings *strings,
SysprofProcessInfo *process_info,
SysprofDocumentTraceable *traceable,
SysprofSymbolizer *symbolizer)
{
SysprofAddressContext last_context;
guint64 *addresses;
guint n_addresses;
g_assert (SYSPROF_IS_DOCUMENT_TRACEABLE (traceable));
g_assert (SYSPROF_IS_SYMBOLIZER (symbolizer));
n_addresses = sysprof_document_traceable_get_stack_depth (traceable);
addresses = g_alloca (sizeof (guint64) * n_addresses);
sysprof_document_traceable_get_stack_addresses (traceable, addresses, n_addresses);
last_context = SYSPROF_ADDRESS_CONTEXT_NONE;
for (guint i = 0; i < n_addresses; i++)
{
SysprofAddress address = addresses[i];
SysprofAddressContext context = SYSPROF_ADDRESS_CONTEXT_NONE;
if (sysprof_address_is_context_switch (address, &context))
{
last_context = context;
continue;
}
if (last_context == SYSPROF_ADDRESS_CONTEXT_KERNEL)
{
g_autoptr(SysprofSymbol) symbol = NULL;
if (sysprof_symbol_cache_lookup (self->kernel_symbols, address) != NULL)
continue;
if ((symbol = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, last_context, address)))
sysprof_symbol_cache_take (self->kernel_symbols, g_steal_pointer (&symbol));
}
else
{
g_autoptr(SysprofSymbol) symbol = NULL;
if (process_info != NULL &&
sysprof_symbol_cache_lookup (process_info->symbol_cache, address) != NULL)
continue;
if ((symbol = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, last_context, address)))
sysprof_symbol_cache_take (process_info->symbol_cache, g_steal_pointer (&symbol));
}
}
}
static void
sysprof_document_symbols_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
static const struct {
const char *name;
guint value;
} context_switches[] = {
{ "- - Hypervisor - -", SYSPROF_ADDRESS_CONTEXT_HYPERVISOR },
{ "- - Kernel - -", SYSPROF_ADDRESS_CONTEXT_KERNEL },
{ "- - User - -", SYSPROF_ADDRESS_CONTEXT_USER },
{ "- - Guest - -", SYSPROF_ADDRESS_CONTEXT_GUEST },
{ "- - Guest Kernel - -", SYSPROF_ADDRESS_CONTEXT_GUEST_KERNEL },
{ "- - Guest User - -", SYSPROF_ADDRESS_CONTEXT_GUEST_USER },
};
g_autoptr(GRefString) context_switch = g_ref_string_new_intern ("Context Switch");
Symbolize *state = task_data;
EggBitsetIter iter;
EggBitset *bitset;
GListModel *model;
guint count = 0;
guint i;
g_assert (source_object == NULL);
g_assert (G_IS_TASK (task));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
g_assert (state != NULL);
g_assert (SYSPROF_IS_DOCUMENT (state->document));
g_assert (SYSPROF_IS_SYMBOLIZER (state->symbolizer));
g_assert (SYSPROF_IS_DOCUMENT_SYMBOLS (state->symbols));
bitset = _sysprof_document_traceables (state->document);
model = G_LIST_MODEL (state->document);
/* Create static symbols for context switch use */
for (guint cs = 0; cs < G_N_ELEMENTS (context_switches); cs++)
{
g_autoptr(SysprofSymbol) symbol = _sysprof_symbol_new (g_ref_string_new_intern (context_switches[cs].name),
NULL,
g_ref_string_acquire (context_switch),
0, 0,
SYSPROF_SYMBOL_KIND_CONTEXT_SWITCH);
/* TODO: It would be nice if we had enough insight from the capture header
* as to the host system, so we can show "vmlinuz" and "Linux" respectively
* for binary-path and binary-nick when the capture came from Linux.
*/
state->symbols->context_switches[context_switches[cs].value] = g_steal_pointer (&symbol);
}
/* Walk through the available traceables which need symbols extracted */
if (!SYSPROF_IS_NO_SYMBOLIZER (state->symbolizer) &&
egg_bitset_iter_init_first (&iter, bitset, &i))
{
guint n_items = egg_bitset_get_size (bitset);
do
{
g_autoptr(SysprofDocumentTraceable) traceable = g_list_model_get_item (model, i);
int pid = sysprof_document_frame_get_pid (SYSPROF_DOCUMENT_FRAME (traceable));
SysprofProcessInfo *process_info = g_hash_table_lookup (state->pid_to_process_info, GINT_TO_POINTER (pid));
add_traceable (state->symbols,
state->strings,
process_info,
traceable,
state->symbolizer);
count++;
if (state->progress_func != NULL && count % 100 == 0)
state->progress_func (count / (double)n_items, _("Symbolizing stack traces"), state->progress_data);
}
while (egg_bitset_iter_next (&iter, &i));
}
g_task_return_pointer (task,
g_object_ref (state->symbols),
g_object_unref);
}
void
_sysprof_document_symbols_new (SysprofDocument *document,
SysprofStrings *strings,
SysprofSymbolizer *symbolizer,
GHashTable *pid_to_process_info,
ProgressFunc progress_func,
gpointer progress_data,
GDestroyNotify progress_data_destroy,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
Symbolize *state;
g_return_if_fail (SYSPROF_IS_DOCUMENT (document));
g_return_if_fail (SYSPROF_IS_SYMBOLIZER (symbolizer));
state = g_new0 (Symbolize, 1);
state->document = g_object_ref (document);
state->symbolizer = g_object_ref (symbolizer);
state->symbols = g_object_new (SYSPROF_TYPE_DOCUMENT_SYMBOLS, NULL);
state->strings = sysprof_strings_ref (strings);
state->pid_to_process_info = g_hash_table_ref (pid_to_process_info);
state->progress_func = progress_func;
state->progress_data = progress_data;
state->progress_data_destroy = progress_data_destroy;
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, _sysprof_document_symbols_new);
g_task_set_task_data (task, state, (GDestroyNotify)symbolize_free);
g_task_run_in_thread (task, sysprof_document_symbols_worker);
}
SysprofDocumentSymbols *
_sysprof_document_symbols_new_finish (GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (G_IS_TASK (result), NULL);
g_return_val_if_fail (g_task_is_valid (result, NULL), NULL);
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == _sysprof_document_symbols_new, NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
/**
* _sysprof_document_symbols_lookup:
* @self: a #SysprofDocumentSymbols
* @process_info: (nullable): the process info if necessary
* @context: the #SysprofAddressContext for the address
* @address: a #SysprofAddress to lookup the symbol for
*
* Locates the symbol that is found at @address within @context of @pid.
*
* Returns: (transfer none) (nullable): a #SysprofSymbol or %NULL
*/
SysprofSymbol *
_sysprof_document_symbols_lookup (SysprofDocumentSymbols *self,
const SysprofProcessInfo *process_info,
SysprofAddressContext context,
SysprofAddress address)
{
SysprofAddressContext new_context;
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_SYMBOLS (self), NULL);
g_return_val_if_fail (context <= SYSPROF_ADDRESS_CONTEXT_GUEST_USER, NULL);
if (context == SYSPROF_ADDRESS_CONTEXT_NONE)
context = SYSPROF_ADDRESS_CONTEXT_USER;
if (sysprof_address_is_context_switch (address, &new_context))
return self->context_switches[context];
if (context == SYSPROF_ADDRESS_CONTEXT_KERNEL)
return sysprof_symbol_cache_lookup (self->kernel_symbols, address);
if (process_info != NULL)
return sysprof_symbol_cache_lookup (process_info->symbol_cache, address);
return NULL;
}

View File

@ -0,0 +1,90 @@
/*
* sysprof-document-traceable.c
*
* Copyright 2023 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 "sysprof-document-traceable.h"
G_DEFINE_INTERFACE (SysprofDocumentTraceable, sysprof_document_traceable, SYSPROF_TYPE_DOCUMENT_FRAME)
static void
sysprof_document_traceable_default_init (SysprofDocumentTraceableInterface *iface)
{
/**
* SysprofDocumentTraceable:stack-depth:
*
* The "stack-depth" property contains the number of addresses collected
* in the backtrace.
*
* You may use this value to retrieve the addresses from 0 to ("stack-depth"-1)
* by calling sysprof_document_traceable_get_stack_address().
*
* Since: 45
*/
g_object_interface_install_property (iface,
g_param_spec_uint ("stack-depth", NULL, NULL,
0, G_MAXUINT16, 0,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
/**
* SysprofDocumentTraceable:thread-id:
*
* The "thread-id" property contains the thread identifier for the traceable.
*
* Since: 45
*/
g_object_interface_install_property (iface,
g_param_spec_int ("thread-id", NULL, NULL,
-1, G_MAXINT, -1,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
}
guint
sysprof_document_traceable_get_stack_depth (SysprofDocumentTraceable *self)
{
return SYSPROF_DOCUMENT_TRACEABLE_GET_IFACE (self)->get_stack_depth (self);
}
guint64
sysprof_document_traceable_get_stack_address (SysprofDocumentTraceable *self,
guint position)
{
return SYSPROF_DOCUMENT_TRACEABLE_GET_IFACE (self)->get_stack_address (self, position);
}
int
sysprof_document_traceable_get_thread_id (SysprofDocumentTraceable *self)
{
return SYSPROF_DOCUMENT_TRACEABLE_GET_IFACE (self)->get_thread_id (self);
}
guint
sysprof_document_traceable_get_stack_addresses (SysprofDocumentTraceable *self,
guint64 *addresses,
guint n_addresses)
{
g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TRACEABLE (self), 0);
if (addresses == NULL || n_addresses == 0)
return 0;
return SYSPROF_DOCUMENT_TRACEABLE_GET_IFACE (self)->get_stack_addresses (self, addresses, n_addresses);
}

View File

@ -0,0 +1,58 @@
/*
* sysprof-document-traceable.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-document-frame.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT_TRACEABLE (sysprof_document_traceable_get_type ())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_INTERFACE (SysprofDocumentTraceable, sysprof_document_traceable, SYSPROF, DOCUMENT_TRACEABLE, SysprofDocumentFrame)
struct _SysprofDocumentTraceableInterface
{
GTypeInterface parent;
guint (*get_stack_depth) (SysprofDocumentTraceable *self);
guint64 (*get_stack_address) (SysprofDocumentTraceable *self,
guint position);
guint (*get_stack_addresses) (SysprofDocumentTraceable *self,
guint64 *addresses,
guint n_addresses);
int (*get_thread_id) (SysprofDocumentTraceable *self);
};
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_document_traceable_get_stack_depth (SysprofDocumentTraceable *self);
SYSPROF_AVAILABLE_IN_ALL
guint64 sysprof_document_traceable_get_stack_address (SysprofDocumentTraceable *self,
guint position);
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_document_traceable_get_stack_addresses (SysprofDocumentTraceable *self,
guint64 *addresses,
guint n_addresses);
SYSPROF_AVAILABLE_IN_ALL
int sysprof_document_traceable_get_thread_id (SysprofDocumentTraceable *self);
G_END_DECLS

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,118 @@
/* sysprof-document.h
*
* Copyright 2023 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
*/
#pragma once
#include <gio/gio.h>
#include <sysprof-capture.h>
#include "sysprof-callgraph.h"
#include "sysprof-document-counter.h"
#include "sysprof-document-file.h"
#include "sysprof-document-traceable.h"
#include "sysprof-mark-catalog.h"
#include "sysprof-symbol.h"
#include "sysprof-time-span.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_DOCUMENT (sysprof_document_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofDocument, sysprof_document, SYSPROF, DOCUMENT, GObject)
SYSPROF_AVAILABLE_IN_ALL
char *sysprof_document_dup_title (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
const SysprofTimeSpan *sysprof_document_get_time_span (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
SysprofDocumentFile *sysprof_document_lookup_file (SysprofDocument *self,
const char *path);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_cpu_info (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_files (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_traceables (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_allocations (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_logs (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_metadata (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_samples (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_samples_with_context_switch (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_samples_without_context_switch (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_processes (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_jitmaps (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_counters (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_marks (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_marks_by_group (SysprofDocument *self,
const char *group);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_catalog_marks (SysprofDocument *self);
SYSPROF_AVAILABLE_IN_ALL
SysprofDocumentCounter *sysprof_document_find_counter (SysprofDocument *self,
const char *category,
const char *name);
SYSPROF_AVAILABLE_IN_ALL
GListModel *sysprof_document_list_symbols_in_traceable (SysprofDocument *self,
SysprofDocumentTraceable *traceable);
SYSPROF_AVAILABLE_IN_ALL
guint sysprof_document_symbolize_traceable (SysprofDocument *self,
SysprofDocumentTraceable *traceable,
SysprofSymbol **symbols,
guint n_symbols,
SysprofAddressContext *final_context);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_document_callgraph_async (SysprofDocument *self,
SysprofCallgraphFlags flags,
GListModel *traceables,
gsize augment_size,
SysprofAugmentationFunc augment_func,
gpointer augment_func_data,
GDestroyNotify augment_func_data_destroy,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
SysprofCallgraph *sysprof_document_callgraph_finish (SysprofDocument *self,
GAsyncResult *result,
GError **error);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_document_serialize_symbols_async (SysprofDocument *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
GBytes *sysprof_document_serialize_symbols_finish (SysprofDocument *self,
GAsyncResult *result,
GError **error);
G_END_DECLS

View File

@ -0,0 +1,50 @@
/*
* sysprof-elf-loader-private.h
*
* Copyright 2023 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
*/
#pragma once
#include <glib-object.h>
#include "sysprof-elf-private.h"
#include "sysprof-mount-namespace-private.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_ELF_LOADER (sysprof_elf_loader_get_type())
G_DECLARE_FINAL_TYPE (SysprofElfLoader, sysprof_elf_loader, SYSPROF, ELF_LOADER, GObject)
SysprofElfLoader *sysprof_elf_loader_new (void);
const char * const *sysprof_elf_loader_get_debug_dirs (SysprofElfLoader *self);
void sysprof_elf_loader_set_debug_dirs (SysprofElfLoader *self,
const char * const *debug_dirs);
const char * const *sysprof_elf_loader_get_external_debug_dirs (SysprofElfLoader *self);
void sysprof_elf_loader_set_external_debug_dirs (SysprofElfLoader *self,
const char * const *debug_dirs);
SysprofElf *sysprof_elf_loader_load (SysprofElfLoader *self,
SysprofMountNamespace *mount_namespace,
const char *file,
const char *build_id,
guint64 file_inode,
GError **error);
G_END_DECLS

View File

@ -0,0 +1,499 @@
/* sysprof-elf-loader.c
*
* Copyright 2023 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 <fcntl.h>
#include <sys/stat.h>
#include "sysprof-elf-private.h"
#include "sysprof-elf-loader-private.h"
#include "sysprof-strings-private.h"
#define DEFAULT_DEBUG_DIRS SYSPROF_STRV_INIT("/usr/lib/debug")
struct _SysprofElfLoader
{
GObject parent_instance;
GHashTable *cache;
char **debug_dirs;
char **external_debug_dirs;
};
enum {
PROP_0,
PROP_DEBUG_DIRS,
PROP_EXTERNAL_DEBUG_DIRS,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofElfLoader, sysprof_elf_loader, G_TYPE_OBJECT)
static GParamSpec *properties [N_PROPS];
static gboolean in_flatpak;
static gboolean in_podman;
SysprofElfLoader *
sysprof_elf_loader_new (void)
{
return g_object_new (SYSPROF_TYPE_ELF_LOADER, NULL);
}
static void
_g_object_xunref (gpointer data)
{
if (data != NULL)
g_object_unref (data);
}
static void
sysprof_elf_loader_finalize (GObject *object)
{
SysprofElfLoader *self = (SysprofElfLoader *)object;
g_clear_pointer (&self->debug_dirs, g_strfreev);
g_clear_pointer (&self->external_debug_dirs, g_strfreev);
g_clear_pointer (&self->cache, g_hash_table_unref);
G_OBJECT_CLASS (sysprof_elf_loader_parent_class)->finalize (object);
}
static void
sysprof_elf_loader_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofElfLoader *self = SYSPROF_ELF_LOADER (object);
switch (prop_id)
{
case PROP_DEBUG_DIRS:
g_value_set_boxed (value, sysprof_elf_loader_get_debug_dirs (self));
break;
case PROP_EXTERNAL_DEBUG_DIRS:
g_value_set_boxed (value, sysprof_elf_loader_get_external_debug_dirs (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_elf_loader_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofElfLoader *self = SYSPROF_ELF_LOADER (object);
switch (prop_id)
{
case PROP_DEBUG_DIRS:
sysprof_elf_loader_set_debug_dirs (self, g_value_get_boxed (value));
break;
case PROP_EXTERNAL_DEBUG_DIRS:
sysprof_elf_loader_set_external_debug_dirs (self, g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_elf_loader_class_init (SysprofElfLoaderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_elf_loader_finalize;
object_class->get_property = sysprof_elf_loader_get_property;
object_class->set_property = sysprof_elf_loader_set_property;
properties [PROP_DEBUG_DIRS] =
g_param_spec_boxed ("debug-dirs", NULL, NULL,
G_TYPE_STRV,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
properties [PROP_EXTERNAL_DEBUG_DIRS] =
g_param_spec_boxed ("external-debug-dirs", NULL, NULL,
G_TYPE_STRV,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
in_flatpak = g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS);
in_podman = g_file_test ("/run/.containerenv", G_FILE_TEST_EXISTS);
}
static void
sysprof_elf_loader_init (SysprofElfLoader *self)
{
self->debug_dirs = g_strdupv ((char **)DEFAULT_DEBUG_DIRS);
self->cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _g_object_xunref);
}
/**
* sysprof_elf_loader_get_debug_dirs:
* @self: a #SysprofElfLoader
*
* Gets the #SysprofElfLoader:debug-dirs property.
*
* See sysprof_elf_loader_set_debug_dirs() for information on how
* these directories are used.
*
* Returns: (nullable): an array of debug directories, or %NULL
*/
const char * const *
sysprof_elf_loader_get_debug_dirs (SysprofElfLoader *self)
{
g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL);
return (const char * const *)self->debug_dirs;
}
/**
* sysprof_elf_loader_set_debug_dirs:
* @self: a #SysprofElfLoader
* @debug_dirs: the new debug directories to use
*
* Sets the #SysprofElfLoader:debug-dirs prpoerty.
*
* If @debug_dirs is %NULL, the default debug directories will
* be used.
*
* These directories will be used to resolve debug symbols within
* the mount namespace of the process whose symbols are being
* resolved.
*
* To set a global directory that may contain debug symbols, use
* sysprof_elf_loader_set_external_debug_dirs() which can be searched
* regardless of what mount namespace the resolving process was in.
*/
void
sysprof_elf_loader_set_debug_dirs (SysprofElfLoader *self,
const char * const *debug_dirs)
{
g_return_if_fail (SYSPROF_IS_ELF_LOADER (self));
g_return_if_fail (self->debug_dirs != NULL);
if (sysprof_set_strv (&self->debug_dirs, debug_dirs))
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEBUG_DIRS]);
}
/**
* sysprof_elf_loader_get_external_debug_dirs:
* @self: a #SysprofElfLoader
*
* Gets the #SysprofElfLoader:external-debug-dirs property.
*
* See sysprof_elf_loader_set_external_debug_dirs() for how this
* property is used to locate ELF files.
*
* Returns: (nullable): an array of external debug directories, or %NULL
*/
const char * const *
sysprof_elf_loader_get_external_debug_dirs (SysprofElfLoader *self)
{
g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL);
return (const char * const *)self->external_debug_dirs;
}
/**
* sysprof_elf_loader_set_external_debug_dirs:
* @self: a #SysprofElfLoader
* @external_debug_dirs: (nullable): array of debug directories to resolve
* `.gnu_debuglink` references in ELF files.
*
* Sets the #SysprofElfLoader:external-debug-dirs property.
*
* This is used to resolve `.gnu_debuglink` files across any process that is
* loading ELF files with this #SysprofElfLoader. That allows for symbolizing
* a capture file that was recording on a different system for which the
* debug symbols only exist locally.
*
* Note that this has no effect on stack trace unwinding, that must occur
* independently of this, when the samples are recorded by the specific
* unwinder in use.
*/
void
sysprof_elf_loader_set_external_debug_dirs (SysprofElfLoader *self,
const char * const *external_debug_dirs)
{
g_return_if_fail (SYSPROF_IS_ELF_LOADER (self));
if (sysprof_set_strv (&self->external_debug_dirs, external_debug_dirs))
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_EXTERNAL_DEBUG_DIRS]);
}
static char *
access_path_from_container (const char *path)
{
if ((in_flatpak && !g_str_has_prefix (path, "/home/")) || in_podman)
return g_build_filename ("/var/run/host", path, NULL);
return g_strdup (path);
}
static SysprofElf *
get_deepest_debuglink (SysprofElf *elf)
{
SysprofElf *debug_link = sysprof_elf_get_debug_link_elf (elf);
return debug_link ? get_deepest_debuglink (debug_link) : elf;
}
static gboolean
try_load_build_id (SysprofElfLoader *self,
SysprofMountNamespace *mount_namespace,
SysprofElf *elf,
const char *build_id,
const char *debug_dir)
{
g_assert (SYSPROF_IS_ELF_LOADER (self));
g_assert (!mount_namespace || SYSPROF_IS_MOUNT_NAMESPACE (mount_namespace));
g_assert (SYSPROF_IS_ELF (elf));
if (build_id && build_id[0] && build_id[1])
{
char prefix[3] = {build_id[0], build_id[1], 0};
g_autofree char *build_id_path = g_build_filename (debug_dir, ".build-id", prefix, build_id, NULL);
g_autoptr(SysprofElf) debug_link_elf = sysprof_elf_loader_load (self, mount_namespace, build_id_path, build_id, 0, NULL);
if (debug_link_elf != NULL)
{
sysprof_elf_set_debug_link_elf (elf, debug_link_elf);
return TRUE;
}
}
return FALSE;
}
static void
sysprof_elf_loader_annotate (SysprofElfLoader *self,
SysprofMountNamespace *mount_namespace,
const char *orig_file,
SysprofElf *elf,
const char *debug_link)
{
g_assert (SYSPROF_IS_ELF_LOADER (self));
g_assert (SYSPROF_IS_MOUNT_NAMESPACE (mount_namespace));
g_assert (SYSPROF_IS_ELF (elf));
g_assert (debug_link != NULL);
if (self->debug_dirs != NULL)
{
for (guint i = 0; self->debug_dirs[i]; i++)
{
g_autoptr(SysprofElf) debug_link_elf = NULL;
g_autofree char *directory_name = NULL;
g_autofree char *debug_path = NULL;
g_autofree char *container_path = NULL;
const char *debug_dir = self->debug_dirs[i];
const char *build_id;
directory_name = g_path_get_dirname (orig_file);
debug_path = g_build_filename (debug_dir, directory_name, debug_link, NULL);
build_id = sysprof_elf_get_build_id (elf);
if (try_load_build_id (self, mount_namespace, elf, build_id, debug_dir))
return;
if ((debug_link_elf = sysprof_elf_loader_load (self, mount_namespace, debug_path, build_id, 0, NULL)))
{
sysprof_elf_set_debug_link_elf (elf, get_deepest_debuglink (debug_link_elf));
return;
}
}
}
if (self->external_debug_dirs != NULL)
{
for (guint i = 0; self->external_debug_dirs[i]; i++)
{
g_autoptr(SysprofElf) debug_link_elf = NULL;
g_autofree char *directory_name = NULL;
g_autofree char *debug_path = NULL;
g_autofree char *container_path = NULL;
const char *debug_dir = self->external_debug_dirs[i];
const char *build_id;
directory_name = g_path_get_dirname (orig_file);
debug_path = g_build_filename (debug_dir, directory_name, debug_link, NULL);
build_id = sysprof_elf_get_build_id (elf);
if (try_load_build_id (self, NULL, elf, build_id, debug_dir))
return;
if ((debug_link_elf = sysprof_elf_loader_load (self, NULL, debug_path, build_id, 0, NULL)))
{
sysprof_elf_set_debug_link_elf (elf, get_deepest_debuglink (debug_link_elf));
return;
}
}
}
}
static gboolean
get_file_and_inode (const char *path,
GMappedFile **mapped_file,
guint64 *file_inode)
{
struct stat stbuf;
int fd;
g_assert (path != NULL);
g_assert (mapped_file != NULL);
g_assert (file_inode != NULL);
*mapped_file = NULL;
*file_inode = 0;
fd = open (path, O_RDONLY|O_CLOEXEC, 0);
if (fd == -1)
return FALSE;
if (fstat (fd, &stbuf) == 0)
*file_inode = (guint64)stbuf.st_ino;
*mapped_file = g_mapped_file_new_from_fd (fd, FALSE, NULL);
close (fd);
return *mapped_file != NULL;
}
/**
* sysprof_elf_loader_load:
* @self: a #SysprofElfLoader
* @mount_namespace: a #SysprofMountNamespace for path resolving
* @file: the path of the file to load within the mount namespace
* @build_id: (nullable): an optional build-id that can be used to resolve
* the file alternatively to the file path
* @file_inode: expected inode for @file
* @error: a location for a #GError, or %NULL
*
* Attempts to load a #SysprofElf for @file (or optionally by @build_id).
*
* This attempts to follow `.gnu_debuglink` ELF section headers and attach
* them to the resulting #SysprofElf so that additional symbol information
* is available.
*
* Returns: (transfer full): a #SysprofElf, or %NULL if the file could
* not be resolved.
*/
SysprofElf *
sysprof_elf_loader_load (SysprofElfLoader *self,
SysprofMountNamespace *mount_namespace,
const char *file,
const char *build_id,
guint64 file_inode,
GError **error)
{
const char * const fallback_paths[2] = { file, NULL };
g_auto(GStrv) paths = NULL;
g_return_val_if_fail (SYSPROF_IS_ELF_LOADER (self), NULL);
g_return_val_if_fail (!mount_namespace || SYSPROF_IS_MOUNT_NAMESPACE (mount_namespace), NULL);
/* We must translate the file into a number of paths that may possibly
* locate the file in the case that there are overlays in the mount
* namespace. Each of the paths could be in a lower overlay layer.
*
* To allow for zero-translation, we allow a NULL mount namespace.
* sysprof_elf_loader_annotate() will use that to load from external
* directories for which on additional translation is necessary.
*/
if (mount_namespace == NULL)
paths = g_strdupv ((char **)fallback_paths);
else
paths = sysprof_mount_namespace_translate (mount_namespace, file);
if (paths == NULL)
goto failure;
for (guint i = 0; paths[i]; i++)
{
g_autoptr(GMappedFile) mapped_file = NULL;
g_autoptr(SysprofElf) elf = NULL;
g_autoptr(GError) local_error = NULL;
g_autofree char *container_path = NULL;
SysprofElf *cached_elf = NULL;
const char *path = paths[i];
const char *debug_link;
guint64 mapped_file_inode;
if (in_flatpak || in_podman)
path = container_path = access_path_from_container (path);
/* Lookup to see if we've already parsed this ELF and handle cases where
* we've failed to load it too. In the case we failed to load a key is
* stored in the cache with a NULL value.
*/
if (g_hash_table_lookup_extended (self->cache, path, NULL, (gpointer *)&cached_elf))
{
if (cached_elf != NULL)
{
if (sysprof_elf_matches (cached_elf, file_inode, build_id))
return g_object_ref (cached_elf);
}
continue;
}
/* Try to mmap the file and parse it. If the parser fails to parse the
* section headers, then this probably isn't an ELF file and we should
* store a failure record in the cache so that we don't attempt to load
* it again.
*/
if (get_file_and_inode (path, &mapped_file, &mapped_file_inode))
elf = sysprof_elf_new (path, g_steal_pointer (&mapped_file), mapped_file_inode, &local_error);
g_hash_table_insert (self->cache,
g_strdup (path),
elf ? g_object_ref (elf) : NULL);
if (elf != NULL)
{
if (mount_namespace && (debug_link = sysprof_elf_get_debug_link (elf)))
sysprof_elf_loader_annotate (self, mount_namespace, file, elf, debug_link);
/* If we loaded the ELF, but it doesn't match what this request is looking
* for in terms of inode/build-id, then we need to bail and not return it.
* We can, however, leave it in the cache incase another process/sample
* will need the ELF.
*/
if (!sysprof_elf_matches (elf, file_inode, build_id))
g_clear_object (&elf);
}
if (elf != NULL)
return g_steal_pointer (&elf);
}
failure:
g_set_error_literal (error,
G_FILE_ERROR,
G_FILE_ERROR_NOENT,
"Failed to locate file");
return NULL;
}

View File

@ -0,0 +1,51 @@
/* sysprof-elf-private.h
*
* Copyright 2023 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
*/
#pragma once
#include <glib-object.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_ELF (sysprof_elf_get_type())
G_DECLARE_FINAL_TYPE (SysprofElf, sysprof_elf, SYSPROF, ELF, GObject)
SysprofElf *sysprof_elf_new (const char *filename,
GMappedFile *mapped_file,
guint64 file_inode,
GError **error);
gboolean sysprof_elf_matches (SysprofElf *self,
guint64 file_inode,
const char *build_id);
const char *sysprof_elf_get_nick (SysprofElf *self);
const char *sysprof_elf_get_file (SysprofElf *self);
const char *sysprof_elf_get_build_id (SysprofElf *self);
const char *sysprof_elf_get_debug_link (SysprofElf *self);
char *sysprof_elf_get_symbol_at_address (SysprofElf *self,
guint64 address,
guint64 *begin_address,
guint64 *end_address,
gboolean *is_fallback);
SysprofElf *sysprof_elf_get_debug_link_elf (SysprofElf *self);
void sysprof_elf_set_debug_link_elf (SysprofElf *self,
SysprofElf *debug_link_elf);
G_END_DECLS

View File

@ -0,0 +1,312 @@
/* sysprof-elf-symbolizer.c
*
* Copyright 2023 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 "sysprof-elf-private.h"
#include "sysprof-elf-loader-private.h"
#include "sysprof-elf-symbolizer.h"
#include "sysprof-document-private.h"
#include "sysprof-strings-private.h"
#include "sysprof-symbolizer-private.h"
#include "sysprof-symbol-private.h"
struct _SysprofElfSymbolizer
{
SysprofSymbolizer parent_instance;
SysprofElfLoader *loader;
};
struct _SysprofElfSymbolizerClass
{
SysprofSymbolizerClass parent_class;
};
G_DEFINE_FINAL_TYPE (SysprofElfSymbolizer, sysprof_elf_symbolizer, SYSPROF_TYPE_SYMBOLIZER)
enum {
PROP_0,
PROP_DEBUG_DIRS,
PROP_EXTERNAL_DEBUG_DIRS,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
static SysprofSymbol *
sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer,
SysprofStrings *strings,
const SysprofProcessInfo *process_info,
SysprofAddressContext context,
SysprofAddress address)
{
SysprofElfSymbolizer *self = (SysprofElfSymbolizer *)symbolizer;
g_autoptr(SysprofElf) elf = NULL;
SysprofDocumentMmap *map;
g_autofree char *name = NULL;
const char *path;
const char *build_id;
gboolean is_fallback = FALSE;
guint64 map_begin;
guint64 map_end;
guint64 relative_address;
guint64 begin_address;
guint64 end_address;
guint64 file_inode;
guint64 file_offset;
SysprofSymbol *ret;
if (process_info == NULL ||
process_info->address_layout == NULL ||
process_info->mount_namespace == NULL ||
(context != SYSPROF_ADDRESS_CONTEXT_NONE &&
context != SYSPROF_ADDRESS_CONTEXT_USER))
return NULL;
/* Always ignore jitmap functions, no matter the ordering */
if ((address & 0xFFFFFFFF00000000) == 0xE000000000000000)
return NULL;
/* First find out what was mapped at that address */
if (!(map = sysprof_address_layout_lookup (process_info->address_layout, address)))
return NULL;
map_begin = sysprof_document_mmap_get_start_address (map);
map_end = sysprof_document_mmap_get_end_address (map);
g_assert (address >= map_begin);
g_assert (address < map_end);
file_offset = sysprof_document_mmap_get_file_offset (map);
relative_address = address;
relative_address -= map_begin;
relative_address += file_offset;
path = sysprof_document_mmap_get_file (map);
build_id = sysprof_document_mmap_get_build_id (map);
file_inode = sysprof_document_mmap_get_file_inode (map);
/* See if we can load an ELF at the path . It will be translated from the
* mount namespace into something hopefully we can access.
*/
if (!(elf = sysprof_elf_loader_load (self->loader,
process_info->mount_namespace,
path,
build_id,
file_inode,
NULL)))
goto fallback;
/* Try to get the symbol name at the address and the begin/end address
* so that it can be inserted into our symbol cache.
*/
if (!(name = sysprof_elf_get_symbol_at_address (elf,
relative_address,
&begin_address,
&end_address,
&is_fallback)))
goto fallback;
/* Sanitize address ranges if we have to. Sometimes that can happen
* for us, but it seems to be limited to glibc.
*/
begin_address = CLAMP (begin_address, file_offset, file_offset + (map_end - map_begin));
end_address = CLAMP (end_address, file_offset, file_offset + (map_end - map_begin));
if (end_address == begin_address)
end_address++;
ret = _sysprof_symbol_new (sysprof_strings_get (strings, name),
sysprof_strings_get (strings, path),
sysprof_strings_get (strings, sysprof_elf_get_nick (elf)),
map_begin + (begin_address - file_offset),
map_begin + (end_address - file_offset),
SYSPROF_SYMBOL_KIND_USER);
ret->is_fallback = is_fallback;
return ret;
fallback:
/* Fallback, we failed to locate the symbol within a file we can
* access, so tell the user about what file contained the symbol
* and where (relative to that file) the IP was.
*/
name = g_strdup_printf ("In File %s+0x%"G_GINT64_MODIFIER"x",
sysprof_document_mmap_get_file (map),
relative_address);
begin_address = address;
end_address = address + 1;
ret = _sysprof_symbol_new (sysprof_strings_get (strings, name),
NULL, NULL, begin_address, end_address,
SYSPROF_SYMBOL_KIND_USER);
ret->is_fallback = TRUE;
return ret;
}
static void
sysprof_elf_symbolizer_loader_notify_cb (SysprofElfSymbolizer *self,
GParamSpec *pspec,
SysprofElfLoader *loader)
{
g_assert (SYSPROF_IS_ELF_SYMBOLIZER (self));
g_assert (pspec != NULL);
g_assert (SYSPROF_IS_ELF_LOADER (loader));
if (0) {}
else if (g_strcmp0 (pspec->name, "debug-dirs") == 0)
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DEBUG_DIRS]);
else if (g_strcmp0 (pspec->name, "external-debug-dirs") == 0)
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EXTERNAL_DEBUG_DIRS]);
}
static void
sysprof_elf_symbolizer_finalize (GObject *object)
{
SysprofElfSymbolizer *self = (SysprofElfSymbolizer *)object;
g_clear_object (&self->loader);
G_OBJECT_CLASS (sysprof_elf_symbolizer_parent_class)->finalize (object);
}
static void
sysprof_elf_symbolizer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofElfSymbolizer *self = SYSPROF_ELF_SYMBOLIZER (object);
switch (prop_id)
{
case PROP_DEBUG_DIRS:
g_value_set_boxed (value, sysprof_elf_symbolizer_get_debug_dirs (self));
break;
case PROP_EXTERNAL_DEBUG_DIRS:
g_value_set_boxed (value, sysprof_elf_symbolizer_get_external_debug_dirs (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_elf_symbolizer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofElfSymbolizer *self = SYSPROF_ELF_SYMBOLIZER (object);
switch (prop_id)
{
case PROP_DEBUG_DIRS:
sysprof_elf_symbolizer_set_debug_dirs (self, g_value_get_boxed (value));
break;
case PROP_EXTERNAL_DEBUG_DIRS:
sysprof_elf_symbolizer_set_external_debug_dirs (self, g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_elf_symbolizer_class_init (SysprofElfSymbolizerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass);
object_class->finalize = sysprof_elf_symbolizer_finalize;
object_class->get_property = sysprof_elf_symbolizer_get_property;
object_class->set_property = sysprof_elf_symbolizer_set_property;
symbolizer_class->symbolize = sysprof_elf_symbolizer_symbolize;
properties[PROP_DEBUG_DIRS] =
g_param_spec_boxed ("debug-dirs", NULL, NULL,
G_TYPE_STRV,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
properties[PROP_EXTERNAL_DEBUG_DIRS] =
g_param_spec_boxed ("external-debug-dirs", NULL, NULL,
G_TYPE_STRV,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_elf_symbolizer_init (SysprofElfSymbolizer *self)
{
self->loader = sysprof_elf_loader_new ();
g_signal_connect_object (self->loader,
"notify",
G_CALLBACK (sysprof_elf_symbolizer_loader_notify_cb),
self,
G_CONNECT_SWAPPED);
}
SysprofSymbolizer *
sysprof_elf_symbolizer_new (void)
{
return g_object_new (SYSPROF_TYPE_ELF_SYMBOLIZER, NULL);
}
const char * const *
sysprof_elf_symbolizer_get_debug_dirs (SysprofElfSymbolizer *self)
{
g_return_val_if_fail (SYSPROF_IS_ELF_SYMBOLIZER (self), NULL);
return sysprof_elf_loader_get_debug_dirs (self->loader);
}
void
sysprof_elf_symbolizer_set_debug_dirs (SysprofElfSymbolizer *self,
const char * const *debug_dirs)
{
g_return_if_fail (SYSPROF_IS_ELF_SYMBOLIZER (self));
sysprof_elf_loader_set_debug_dirs (self->loader, debug_dirs);
}
const char * const *
sysprof_elf_symbolizer_get_external_debug_dirs (SysprofElfSymbolizer *self)
{
g_return_val_if_fail (SYSPROF_IS_ELF_SYMBOLIZER (self), NULL);
return sysprof_elf_loader_get_external_debug_dirs (self->loader);
}
void
sysprof_elf_symbolizer_set_external_debug_dirs (SysprofElfSymbolizer *self,
const char * const *external_debug_dirs)
{
g_return_if_fail (SYSPROF_IS_ELF_SYMBOLIZER (self));
sysprof_elf_loader_set_external_debug_dirs (self->loader, external_debug_dirs);
}

View File

@ -0,0 +1,52 @@
/* sysprof-elf-symbolizer.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-symbolizer.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_ELF_SYMBOLIZER (sysprof_elf_symbolizer_get_type())
#define SYSPROF_IS_ELF_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_ELF_SYMBOLIZER)
#define SYSPROF_ELF_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_ELF_SYMBOLIZER, SysprofElfSymbolizer)
#define SYSPROF_ELF_SYMBOLIZER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_ELF_SYMBOLIZER, SysprofElfSymbolizerClass)
typedef struct _SysprofElfSymbolizer SysprofElfSymbolizer;
typedef struct _SysprofElfSymbolizerClass SysprofElfSymbolizerClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_elf_symbolizer_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
SysprofSymbolizer *sysprof_elf_symbolizer_new (void);
SYSPROF_AVAILABLE_IN_ALL
const char * const *sysprof_elf_symbolizer_get_debug_dirs (SysprofElfSymbolizer *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_elf_symbolizer_set_debug_dirs (SysprofElfSymbolizer *self,
const char * const *debug_dirs);
SYSPROF_AVAILABLE_IN_ALL
const char * const *sysprof_elf_symbolizer_get_external_debug_dirs (SysprofElfSymbolizer *self);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_elf_symbolizer_set_external_debug_dirs (SysprofElfSymbolizer *self,
const char * const *external_debug_dirs);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofElfSymbolizer, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,498 @@
/* sysprof-elf.c
*
* Copyright 2023 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 "elfparser.h"
#include "sysprof-elf-private.h"
struct _SysprofElf
{
GObject parent_instance;
const char *nick;
char *build_id;
char *file;
SysprofElf *debug_link_elf;
ElfParser *parser;
guint64 file_inode;
gulong text_offset;
};
enum {
PROP_0,
PROP_BUILD_ID,
PROP_DEBUG_LINK,
PROP_DEBUG_LINK_ELF,
PROP_FILE,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofElf, sysprof_elf, G_TYPE_OBJECT)
static GParamSpec *properties [N_PROPS];
static GHashTable *nicks;
static const struct {
const char *library;
const char *nick;
} nick_table[] = {
{ "libc.so", "libc" },
{ "libffi.so", "libffi" },
{ "ld-linux-x86-64.so", "glibc" },
{ "libnss_sss.so", "NSS" },
{ "libsss_debug.so", "SSSD" },
{ "libsss_util.so", "SSSD" },
{ "libnss_systemd.so", "NSS" },
{ "libpcre2-8.so", "PCRE" },
{ "libselinux.so", "SELinux" },
{ "libssl3.so", "NSS" },
{ "libstdc++.so", "libc" },
{ "libsystemd.so", "systemd" },
{ "libudev.so", "udev" },
{ "libxul.so", "XUL" },
{ "libz.so", "Zlib" },
{ "libzstd.so", "Zstd" },
/* GObject */
{ "libglib-2.0.so", "GLib" },
{ "libgobject-2.0.so", "GObject" },
{ "libgio-2.0.so", "Gio" },
{ "libgirepository-1.0.so", "Introspection" },
/* Cairo/Pixman */
{ "libcairo-gobject.so", "Cairo" },
{ "libcairo.so", "Cairo" },
{ "libpixman-1.so", "Pixman" },
/* Various GNOME Platform Libraries */
{ "libdex-1.so", "Dex" },
{ "libgjs.so", "GJS" },
{ "libgstreamer-1-0.so", "GStreamer" },
{ "libgudev-1.0.so", "udev" },
{ "libibus-1.0.so", "IBus" },
{ "libjson-glib-1.0.so", "JSON-GLib" },
{ "libjsonrpc-glib-1.0.so", "JSONRPC-GLib" },
{ "libpolkit-agent-1.so", "PolicyKit" },
{ "libpolkit-gobject-1.so", "PolicyKit" },
{ "libvte-2.91-gtk4.so", "VTE" },
{ "libvte-2.91.so", "VTE" },
/* Pango and Harfbuzz */
{ "libfribidi.so", "Fribidi" },
{ "libpango-1.0.so", "Pango" },
{ "libpangocairo-1.0.so", "Pango" },
{ "libpangoft2-1.0.so", "Pango" },
{ "libharfbuzz-cairo.so", "Harfbuzz" },
{ "libharfbuzz-gobject.so", "Harfbuzz" },
{ "libharfbuzz-icu.so", "Harfbuzz" },
{ "libharfbuzz-subset.so", "Harfbuzz" },
{ "libharfbuzz.so", "Harfbuzz" },
/* GTK */
{ "libgtk-3.so", "GTK 3" },
{ "libgdk-3.so", "GTK 3" },
{ "libgtk-4.so", "GTK 4" },
{ "libgraphene-1.0.so", "Graphene" },
{ "libgdk_pixbuf-2.0.so", "GdkPixbuf" },
{ "librsvg-2.so", "rsvg" },
/* Xorg/X11 */
{ "libX11-xcb.so", "X11" },
{ "libX11.so", "X11" },
{ "libxcb.so", "X11" },
{ "libxkbcommon.so", "XKB" },
/* Wayland */
{ "libwayland-client.so", "Wayland Client" },
{ "libwayland-cursor.so", "Wayland Cursor" },
{ "libwayland-egl.so", "Wayland EGL" },
{ "libwayland-server.so", "Wayland Server" },
/* Mutter/Clutter/Shell */
{ "libclutter-1.0.so", "Clutter" },
{ "libclutter-glx-1.0.so", "Clutter" },
{ "libinput.so", "libinput" },
{ "libmutter-12.so", "Mutter" },
{ "libmutter-cogl-12.so", "Mutter" },
{ "libmutter-clutter-12.so", "Mutter" },
{ "libst-12.so", "GNOME Shell" },
/* GtkSourceView */
{ "libgtksourceview-3.0.so", "GtkSourceView" },
{ "libgtksourceview-4.so", "GtkSourceView" },
{ "libgtksourceview-5.so", "GtkSourceView" },
/* Pipewire and Legacy Audio modules */
{ "libasound.so", "ALSA" },
{ "libpipewire-0.3.so", "Pipewire" },
{ "libpipewire-module-client-node.so", "Pipewire" },
{ "libpipewire-module-protocol-pulse.so", "Pipewire" },
{ "libpulse.so", "PulseAudio" },
{ "libpulsecommon-16.1.so", "PulseAudio" },
{ "libspa-alsa.so", "Pipewire" },
{ "libspa-audioconvert.so", "Pipewire" },
{ "libspa-support.so", "Pipewire" },
/* OpenGL base libraries */
{ "libEGL.so", "EGL" },
{ "libGL.so", "GL" },
/* Mesa and DRI Drivers */
{ "crocus_dri.so", "Mesa" },
{ "i915_dri.so", "Mesa" },
{ "i965_drv_video.so", "Mesa" },
{ "iHD_drv_video.so", "Mesa" },
{ "iris_dri.so", "Mesa" },
{ "kms_swrast_dri.so", "Mesa" },
{ "libEGL_mesa.so", "Mesa" },
{ "libdrm.so", "Mesa" },
{ "nouveau_dri.so", "Mesa" },
{ "r300_dri.so", "Mesa" },
{ "r600_dri.so", "Mesa" },
{ "radeonsi_dri.so", "Mesa" },
{ "swrast_dri.so", "Mesa" },
{ "virtio_gpu_dri.so", "Mesa" },
{ "vmwgfx_dri.so", "Mesa" },
{ "zink_dri.so", "Mesa" },
};
static void
sysprof_elf_finalize (GObject *object)
{
SysprofElf *self = (SysprofElf *)object;
g_clear_pointer (&self->file, g_free);
g_clear_pointer (&self->parser, elf_parser_free);
g_clear_object (&self->debug_link_elf);
G_OBJECT_CLASS (sysprof_elf_parent_class)->finalize (object);
}
static void
sysprof_elf_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofElf *self = SYSPROF_ELF (object);
switch (prop_id)
{
case PROP_BUILD_ID:
g_value_set_string (value, sysprof_elf_get_build_id (self));
break;
case PROP_DEBUG_LINK:
g_value_set_string (value, sysprof_elf_get_debug_link (self));
break;
case PROP_DEBUG_LINK_ELF:
g_value_set_object (value, sysprof_elf_get_debug_link_elf (self));
break;
case PROP_FILE:
g_value_set_string (value, sysprof_elf_get_file (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_elf_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SysprofElf *self = SYSPROF_ELF (object);
switch (prop_id)
{
case PROP_DEBUG_LINK_ELF:
sysprof_elf_set_debug_link_elf (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_elf_class_init (SysprofElfClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_elf_finalize;
object_class->get_property = sysprof_elf_get_property;
object_class->set_property = sysprof_elf_set_property;
properties [PROP_BUILD_ID] =
g_param_spec_string ("build-id", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_DEBUG_LINK] =
g_param_spec_string ("debug-link", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_DEBUG_LINK_ELF] =
g_param_spec_object ("debug-link-elf", NULL, NULL,
SYSPROF_TYPE_ELF,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
properties [PROP_FILE] =
g_param_spec_string ("file", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
nicks = g_hash_table_new (g_str_hash, g_str_equal);
for (guint i = 0; i < G_N_ELEMENTS (nick_table); i++)
{
g_assert (g_str_has_suffix (nick_table[i].library, ".so"));
g_hash_table_insert (nicks,
(char *)nick_table[i].library,
(char *)nick_table[i].nick);
}
}
static void
sysprof_elf_init (SysprofElf *self)
{
}
static void
guess_nick (SysprofElf *self,
const char *name,
const char *endptr)
{
char key[32];
if (endptr <= name)
return;
if (endptr - name >= sizeof key)
return;
memcpy (key, name, endptr-name);
key[endptr-name] = 0;
self->nick = g_hash_table_lookup (nicks, key);
}
SysprofElf *
sysprof_elf_new (const char *filename,
GMappedFile *mapped_file,
guint64 file_inode,
GError **error)
{
SysprofElf *self;
ElfParser *parser;
g_return_val_if_fail (mapped_file != NULL, NULL);
if (!(parser = elf_parser_new_from_mmap (g_steal_pointer (&mapped_file), error)))
return NULL;
self = g_object_new (SYSPROF_TYPE_ELF, NULL);
self->file = g_strdup (filename);
self->parser = g_steal_pointer (&parser);
self->file_inode = file_inode;
self->text_offset = elf_parser_get_text_offset (self->parser);
if (filename != NULL)
{
const char *base;
const char *endptr;
if ((base = strrchr (filename, '/')))
{
endptr = strstr (++base, ".so");
if (endptr != NULL && (endptr[3] == 0 || endptr[3] == '.'))
guess_nick (self, base, &endptr[3]);
}
}
return self;
}
const char *
sysprof_elf_get_file (SysprofElf *self)
{
g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL);
return self->file;
}
const char *
sysprof_elf_get_build_id (SysprofElf *self)
{
g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL);
return elf_parser_get_build_id (self->parser);
}
const char *
sysprof_elf_get_debug_link (SysprofElf *self)
{
guint crc32;
g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL);
return elf_parser_get_debug_link (self->parser, &crc32);
}
static char *
sysprof_elf_get_symbol_at_address_internal (SysprofElf *self,
const char *filename,
guint64 address,
guint64 *begin_address,
guint64 *end_address,
guint64 text_offset,
gboolean *is_fallback)
{
const ElfSym *symbol;
char *ret = NULL;
gulong begin = 0;
gulong end = 0;
g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL);
if (self->debug_link_elf != NULL)
{
ret = sysprof_elf_get_symbol_at_address_internal (self->debug_link_elf, filename, address, begin_address, end_address, text_offset, is_fallback);
if (ret != NULL)
return ret;
}
if ((symbol = elf_parser_lookup_symbol (self->parser, address - text_offset)))
{
const char *name;
if (begin_address || end_address)
{
elf_parser_get_sym_address_range (self->parser, symbol, &begin, &end);
begin += text_offset;
end += text_offset;
}
name = elf_parser_get_sym_name (self->parser, symbol);
if (name != NULL && name[0] == '_' && name[1] == 'Z')
ret = elf_demangle (name);
else
ret = g_strdup (name);
}
else
{
begin = address;
end = address + 1;
ret = g_strdup_printf ("In File %s+0x%"G_GINT64_MODIFIER"x", filename, address);
if (is_fallback)
*is_fallback = TRUE;
}
if (begin_address)
*begin_address = begin;
if (end_address)
*end_address = end;
return ret;
}
char *
sysprof_elf_get_symbol_at_address (SysprofElf *self,
guint64 address,
guint64 *begin_address,
guint64 *end_address,
gboolean *is_fallback)
{
return sysprof_elf_get_symbol_at_address_internal (self,
self->file,
address,
begin_address,
end_address,
self->text_offset,
is_fallback);
}
/**
* sysprof_elf_get_debug_link_elf:
* @self: a #SysprofElf
*
* Gets a #SysprofElf that was resolved from the `.gnu_debuglink`
* ELF section header.
*
* Returns: (transfer none) (nullable): a #SysprofElf or %NULL
*/
SysprofElf *
sysprof_elf_get_debug_link_elf (SysprofElf *self)
{
g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL);
return self->debug_link_elf;
}
void
sysprof_elf_set_debug_link_elf (SysprofElf *self,
SysprofElf *debug_link_elf)
{
g_return_if_fail (SYSPROF_IS_ELF (self));
g_return_if_fail (!debug_link_elf || SYSPROF_IS_ELF (debug_link_elf));
if (g_set_object (&self->debug_link_elf, debug_link_elf))
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEBUG_LINK_ELF]);
}
gboolean
sysprof_elf_matches (SysprofElf *self,
guint64 file_inode,
const char *build_id)
{
g_return_val_if_fail (SYSPROF_IS_ELF (self), FALSE);
if (build_id != NULL)
{
const char *elf_build_id = elf_parser_get_build_id (self->parser);
/* Not matching build-id, you definitely don't want this ELF */
if (elf_build_id != NULL && !g_str_equal (build_id, elf_build_id))
return FALSE;
}
if (file_inode && self->file_inode && file_inode != self->file_inode)
return FALSE;
return TRUE;
}
const char *
sysprof_elf_get_nick (SysprofElf *self)
{
g_return_val_if_fail (SYSPROF_IS_ELF (self), NULL);
return self->nick;
}

View File

@ -0,0 +1,57 @@
/* sysprof-energy-usage.c
*
* Copyright 2023 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 "sysprof-proxied-instrument-private.h"
#include "sysprof-energy-usage.h"
struct _SysprofEnergyUsage
{
SysprofProxiedInstrument parent_instance;
};
struct _SysprofEnergyUsageClass
{
SysprofProxiedInstrumentClass parent_class;
};
G_DEFINE_FINAL_TYPE (SysprofEnergyUsage, sysprof_energy_usage, SYSPROF_TYPE_PROXIED_INSTRUMENT)
static void
sysprof_energy_usage_class_init (SysprofEnergyUsageClass *klass)
{
}
static void
sysprof_energy_usage_init (SysprofEnergyUsage *self)
{
SYSPROF_PROXIED_INSTRUMENT (self)->call_stop_first = TRUE;
}
SysprofInstrument *
sysprof_energy_usage_new (void)
{
return g_object_new (SYSPROF_TYPE_ENERGY_USAGE,
"bus-type", G_BUS_TYPE_SYSTEM,
"bus-name", "org.gnome.Sysprof3",
"object-path", "/org/gnome/Sysprof3/RAPL",
NULL);
}

View File

@ -0,0 +1,42 @@
/* sysprof-energy-usage.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-instrument.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_ENERGY_USAGE (sysprof_energy_usage_get_type())
#define SYSPROF_IS_ENERGY_USAGE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_ENERGY_USAGE)
#define SYSPROF_ENERGY_USAGE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_ENERGY_USAGE, SysprofEnergyUsage)
#define SYSPROF_ENERGY_USAGE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_ENERGY_USAGE, SysprofEnergyUsageClass)
typedef struct _SysprofEnergyUsage SysprofEnergyUsage;
typedef struct _SysprofEnergyUsageClass SysprofEnergyUsageClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_energy_usage_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
SysprofInstrument *sysprof_energy_usage_new (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofEnergyUsage, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,67 @@
/* sysprof-instrument-private.h
*
* Copyright 2023 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
*/
#pragma once
#include <libdex.h>
#include "sysprof-instrument.h"
#include "sysprof-recording.h"
G_BEGIN_DECLS
#define SYSPROF_INSTRUMENT_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS(obj, SYSPROF_TYPE_INSTRUMENT, SysprofInstrumentClass)
struct _SysprofInstrument
{
GObject parent;
};
struct _SysprofInstrumentClass
{
GObjectClass parent_class;
char **(*list_required_policy) (SysprofInstrument *self);
DexFuture *(*prepare) (SysprofInstrument *self,
SysprofRecording *recording);
DexFuture *(*record) (SysprofInstrument *self,
SysprofRecording *recording,
GCancellable *cancellable);
DexFuture *(*augment) (SysprofInstrument *self,
SysprofRecording *recording);
DexFuture *(*process_started) (SysprofInstrument *self,
SysprofRecording *recording,
int pid);
};
DexFuture *_sysprof_instruments_acquire_policy (GPtrArray *instruments,
SysprofRecording *recording);
DexFuture *_sysprof_instruments_prepare (GPtrArray *instruments,
SysprofRecording *recording);
DexFuture *_sysprof_instruments_record (GPtrArray *instruments,
SysprofRecording *recording,
GCancellable *cancellable);
DexFuture *_sysprof_instruments_augment (GPtrArray *instruments,
SysprofRecording *recording);
DexFuture *_sysprof_instruments_process_started (GPtrArray *instruments,
SysprofRecording *recording,
int pid);
G_END_DECLS

View File

@ -0,0 +1,311 @@
/* sysprof-instrument.c
*
* Copyright 2023 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 "sysprof-instrument-private.h"
#if HAVE_POLKIT
# include "sysprof-polkit-private.h"
#endif
G_DEFINE_ABSTRACT_TYPE (SysprofInstrument, sysprof_instrument, G_TYPE_OBJECT)
static char **
sysprof_instrument_real_list_required_policy (SysprofInstrument *instrument)
{
g_assert (SYSPROF_IS_INSTRUMENT (instrument));
return NULL;
}
static DexFuture *
sysprof_instrument_real_prepare (SysprofInstrument *instrument,
SysprofRecording *recording)
{
g_assert (SYSPROF_IS_INSTRUMENT (instrument));
g_assert (SYSPROF_IS_RECORDING (recording));
return dex_future_new_for_boolean (TRUE);
}
static DexFuture *
sysprof_instrument_real_record (SysprofInstrument *instrument,
SysprofRecording *recording,
GCancellable *cancellable)
{
g_assert (SYSPROF_IS_INSTRUMENT (instrument));
g_assert (SYSPROF_IS_RECORDING (recording));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
return dex_future_new_for_boolean (TRUE);
}
static void
sysprof_instrument_class_init (SysprofInstrumentClass *klass)
{
klass->list_required_policy = sysprof_instrument_real_list_required_policy;
klass->prepare = sysprof_instrument_real_prepare;
klass->record = sysprof_instrument_real_record;
}
static void
sysprof_instrument_init (SysprofInstrument *self)
{
}
static char **
_sysprof_instrument_list_required_policy (SysprofInstrument *self)
{
g_assert (SYSPROF_IS_INSTRUMENT (self));
if (SYSPROF_INSTRUMENT_GET_CLASS (self)->list_required_policy)
return SYSPROF_INSTRUMENT_GET_CLASS (self)->list_required_policy (self);
return NULL;
}
static DexFuture *
_sysprof_instrument_prepare (SysprofInstrument *self,
SysprofRecording *recording)
{
g_assert (SYSPROF_IS_INSTRUMENT (self));
g_assert (SYSPROF_IS_RECORDING (recording));
if (SYSPROF_INSTRUMENT_GET_CLASS (self)->prepare)
return SYSPROF_INSTRUMENT_GET_CLASS (self)->prepare (self, recording);
return dex_future_new_for_boolean (TRUE);
}
static DexFuture *
_sysprof_instrument_record (SysprofInstrument *self,
SysprofRecording *recording,
GCancellable *cancellable)
{
g_assert (SYSPROF_IS_INSTRUMENT (self));
g_assert (SYSPROF_IS_RECORDING (recording));
g_assert (G_IS_CANCELLABLE (cancellable));
if (SYSPROF_INSTRUMENT_GET_CLASS (self)->record)
return SYSPROF_INSTRUMENT_GET_CLASS (self)->record (self, recording, cancellable);
return dex_future_new_for_boolean (TRUE);
}
static DexFuture *
_sysprof_instrument_augment (SysprofInstrument *self,
SysprofRecording *recording)
{
g_assert (SYSPROF_IS_INSTRUMENT (self));
g_assert (SYSPROF_IS_RECORDING (recording));
if (SYSPROF_INSTRUMENT_GET_CLASS (self)->augment)
return SYSPROF_INSTRUMENT_GET_CLASS (self)->augment (self, recording);
return dex_future_new_for_boolean (TRUE);
}
static DexFuture *
_sysprof_instrument_process_started (SysprofInstrument *self,
SysprofRecording *recording,
int pid)
{
g_assert (SYSPROF_IS_INSTRUMENT (self));
g_assert (SYSPROF_IS_RECORDING (recording));
if (SYSPROF_INSTRUMENT_GET_CLASS (self)->process_started)
return SYSPROF_INSTRUMENT_GET_CLASS (self)->process_started (self, recording, pid);
return dex_future_new_for_boolean (TRUE);
}
static char **
_sysprof_instruments_list_required_policy (GPtrArray *instruments)
{
g_autoptr(GPtrArray) all_policy = NULL;
g_return_val_if_fail (instruments != NULL, NULL);
all_policy = g_ptr_array_new_null_terminated (0, g_free, TRUE);
for (guint i = 0; i < instruments->len; i++)
{
SysprofInstrument *instrument = g_ptr_array_index (instruments, i);
g_auto(GStrv) policy = _sysprof_instrument_list_required_policy (instrument);
if (policy == NULL || policy[0] == NULL)
continue;
for (guint j = 0; policy[j]; j++)
{
gboolean found = FALSE;
for (guint k = 0; !found && k < all_policy->len; k++)
found = strcmp (policy[j], g_ptr_array_index (all_policy, k)) == 0;
if (!found)
g_ptr_array_add (all_policy, g_strdup (policy[j]));
}
}
if (all_policy->len == 0)
return NULL;
return (char **)g_ptr_array_free (g_steal_pointer (&all_policy), FALSE);
}
DexFuture *
_sysprof_instruments_acquire_policy (GPtrArray *instruments,
SysprofRecording *recording)
{
g_autoptr(GDBusConnection) connection = NULL;
g_autoptr(PolkitDetails) details = NULL;
g_autoptr(GError) error = NULL;
g_auto(GStrv) required_policy = NULL;
g_return_val_if_fail (instruments != NULL, NULL);
g_return_val_if_fail (SYSPROF_IS_RECORDING (recording), NULL);
/* Ensure we have access to the System D-Bus so that we can get
* access to sysprofd for system information.
*/
if (!(connection = dex_await_object (dex_bus_get (G_BUS_TYPE_SYSTEM), &error)))
return dex_future_new_for_error (g_steal_pointer (&error));
/* First ensure that all our required policy have been acquired on
* the bus so that we don't need to individually acquire them from
* each of the instruments.
*/
if ((required_policy = _sysprof_instruments_list_required_policy (instruments)))
{
#if HAVE_POLKIT
for (guint i = 0; required_policy[i]; i++)
{
if (!dex_await_boolean (_sysprof_polkit_authorize (connection,
required_policy[i],
details,
TRUE), &error))
return dex_future_new_for_error (g_steal_pointer (&error));
}
#endif
}
return dex_future_new_for_boolean (TRUE);
}
DexFuture *
_sysprof_instruments_prepare (GPtrArray *instruments,
SysprofRecording *recording)
{
g_autoptr(GPtrArray) futures = NULL;
g_return_val_if_fail (instruments != NULL, NULL);
g_return_val_if_fail (SYSPROF_IS_RECORDING (recording), NULL);
futures = g_ptr_array_new_with_free_func (dex_unref);
for (guint i = 0; i < instruments->len; i++)
{
SysprofInstrument *instrument = g_ptr_array_index (instruments, i);
g_ptr_array_add (futures, _sysprof_instrument_prepare (instrument, recording));
}
if (futures->len == 0)
return dex_future_new_for_boolean (TRUE);
return dex_future_allv ((DexFuture **)futures->pdata, futures->len);
}
DexFuture *
_sysprof_instruments_record (GPtrArray *instruments,
SysprofRecording *recording,
GCancellable *cancellable)
{
g_autoptr(GPtrArray) futures = NULL;
g_return_val_if_fail (instruments != NULL, NULL);
g_return_val_if_fail (SYSPROF_IS_RECORDING (recording), NULL);
g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
futures = g_ptr_array_new_with_free_func (dex_unref);
for (guint i = 0; i < instruments->len; i++)
{
SysprofInstrument *instrument = g_ptr_array_index (instruments, i);
g_ptr_array_add (futures, _sysprof_instrument_record (instrument, recording, cancellable));
}
if (futures->len == 0)
return dex_future_new_for_boolean (TRUE);
return dex_future_allv ((DexFuture **)futures->pdata, futures->len);
}
DexFuture *
_sysprof_instruments_augment (GPtrArray *instruments,
SysprofRecording *recording)
{
g_autoptr(GPtrArray) futures = NULL;
g_return_val_if_fail (instruments != NULL, NULL);
g_return_val_if_fail (SYSPROF_IS_RECORDING (recording), NULL);
futures = g_ptr_array_new_with_free_func (dex_unref);
for (guint i = 0; i < instruments->len; i++)
{
SysprofInstrument *instrument = g_ptr_array_index (instruments, i);
g_ptr_array_add (futures, _sysprof_instrument_augment (instrument, recording));
}
if (futures->len == 0)
return dex_future_new_for_boolean (TRUE);
return dex_future_allv ((DexFuture **)futures->pdata, futures->len);
}
DexFuture *
_sysprof_instruments_process_started (GPtrArray *instruments,
SysprofRecording *recording,
int pid)
{
g_autoptr(GPtrArray) futures = NULL;
g_return_val_if_fail (instruments != NULL, NULL);
g_return_val_if_fail (SYSPROF_IS_RECORDING (recording), NULL);
futures = g_ptr_array_new_with_free_func (dex_unref);
for (guint i = 0; i < instruments->len; i++)
{
SysprofInstrument *instrument = g_ptr_array_index (instruments, i);
g_ptr_array_add (futures, _sysprof_instrument_process_started (instrument, recording, pid));
}
if (futures->len == 0)
return dex_future_new_for_boolean (TRUE);
return dex_future_allv ((DexFuture **)futures->pdata, futures->len);
}

View File

@ -0,0 +1,42 @@
/* sysprof-instrument.h
*
* Copyright 2023 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
*/
#pragma once
#include <glib-object.h>
#include <sysprof-capture.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_INSTRUMENT (sysprof_instrument_get_type())
#define SYSPROF_IS_INSTRUMENT(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_INSTRUMENT)
#define SYSPROF_INSTRUMENT(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_INSTRUMENT, SysprofInstrument)
#define SYSPROF_INSTRUMENT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_INSTRUMENT, SysprofInstrumentClass)
typedef struct _SysprofInstrument SysprofInstrument;
typedef struct _SysprofInstrumentClass SysprofInstrumentClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_instrument_get_type (void) G_GNUC_CONST;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofInstrument, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,251 @@
/* sysprof-jitmap-symbolizer.c
*
* Copyright 2023 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 <stdlib.h>
#include "sysprof-document-jitmap.h"
#include "sysprof-document-private.h"
#include "sysprof-jitmap-symbolizer.h"
#include "sysprof-symbol-private.h"
#include "sysprof-symbolizer-private.h"
typedef struct _Jitmap
{
SysprofAddress address;
GRefString *name;
} Jitmap;
struct _SysprofJitmapSymbolizer
{
SysprofSymbolizer parent_instance;
GArray *jitmaps;
};
struct _SysprofJitmapSymbolizerClass
{
SysprofSymbolizerClass parent_class;
};
G_DEFINE_FINAL_TYPE (SysprofJitmapSymbolizer, sysprof_jitmap_symbolizer, SYSPROF_TYPE_SYMBOLIZER)
typedef struct
{
SysprofDocument *document;
GListModel *model;
} Prepare;
static void
prepare_free (gpointer data)
{
Prepare *prepare = data;
g_clear_object (&prepare->model);
g_clear_object (&prepare->document);
g_free (prepare);
}
static void
jitmap_clear (gpointer data)
{
Jitmap *j = data;
g_clear_pointer (&j->name, g_ref_string_release);
}
static int
compare_by_address (gconstpointer a,
gconstpointer b)
{
const Jitmap *jitmap_a = a;
const Jitmap *jitmap_b = b;
if (jitmap_a->address < jitmap_b->address)
return -1;
else if (jitmap_a->address > jitmap_b->address)
return 1;
else
return 0;
}
static void
sysprof_jitmap_symbolizer_prepare_worker (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
SysprofJitmapSymbolizer *self = source_object;
Prepare *prepare = task_data;
guint n_jitmaps;
g_assert (G_IS_TASK (task));
g_assert (SYSPROF_IS_JITMAP_SYMBOLIZER (self));
g_assert (prepare != NULL);
g_assert (SYSPROF_IS_DOCUMENT (prepare->document));
g_assert (G_IS_LIST_MODEL (prepare->model));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
n_jitmaps = g_list_model_get_n_items (prepare->model);
for (guint i = 0; i < n_jitmaps; i++)
{
g_autoptr(SysprofDocumentJitmap) jitmap = g_list_model_get_item (prepare->model, i);
guint size = sysprof_document_jitmap_get_size (jitmap);
for (guint j = 0; j < size; j++)
{
const char *name;
Jitmap map;
if (!(name = sysprof_document_jitmap_get_mapping (jitmap, j, &map.address)))
continue;
map.name = _sysprof_document_ref_string (prepare->document, name);
g_array_append_val (self->jitmaps, map);
}
}
g_array_sort (self->jitmaps, compare_by_address);
g_task_return_boolean (task, TRUE);
}
static void
sysprof_jitmap_symbolizer_prepare_async (SysprofSymbolizer *symbolizer,
SysprofDocument *document,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
SysprofJitmapSymbolizer *self = (SysprofJitmapSymbolizer *)symbolizer;
g_autoptr(GTask) task = NULL;
Prepare *prepare;
g_assert (SYSPROF_IS_JITMAP_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);
prepare->model = sysprof_document_list_jitmaps (document);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, sysprof_jitmap_symbolizer_prepare_async);
g_task_set_task_data (task, prepare, prepare_free);
g_task_run_in_thread (task, sysprof_jitmap_symbolizer_prepare_worker);
}
static gboolean
sysprof_jitmap_symbolizer_prepare_finish (SysprofSymbolizer *symbolizer,
GAsyncResult *result,
GError **error)
{
g_assert (SYSPROF_IS_JITMAP_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_jitmap_symbolizer_symbolize (SysprofSymbolizer *symbolizer,
SysprofStrings *strings,
const SysprofProcessInfo *process_info,
SysprofAddressContext context,
SysprofAddress address)
{
SysprofJitmapSymbolizer *self = (SysprofJitmapSymbolizer *)symbolizer;
const Jitmap key = { address, NULL };
guint guess = (address & 0xFFFF) - 1;
const Jitmap *match;
if (context != SYSPROF_ADDRESS_CONTEXT_NONE &&
context != SYSPROF_ADDRESS_CONTEXT_USER)
return NULL;
if ((address & 0xFFFFFFFF00000000) != 0xE000000000000000)
return NULL;
/* Jitmap addresses generally start at 1 and work their way up
* monotonically (after masking off the high 0xE...............
* bits). So we can try for a fast index lookup to skip any sort
* of searching in the well behaved case.
*/
if G_LIKELY (guess < self->jitmaps->len)
{
match = &g_array_index (self->jitmaps, Jitmap, guess);
if G_LIKELY (match->address == address)
goto create_symbol;
}
match = bsearch (&key,
self->jitmaps->data,
self->jitmaps->len,
sizeof (Jitmap),
compare_by_address);
if (match == NULL)
return NULL;
create_symbol:
return _sysprof_symbol_new (g_ref_string_acquire (match->name),
NULL,
NULL,
match->address,
match->address + 1,
SYSPROF_SYMBOL_KIND_USER);
}
static void
sysprof_jitmap_symbolizer_finalize (GObject *object)
{
SysprofJitmapSymbolizer *self = (SysprofJitmapSymbolizer *)object;
g_clear_pointer (&self->jitmaps, g_array_unref);
G_OBJECT_CLASS (sysprof_jitmap_symbolizer_parent_class)->finalize (object);
}
static void
sysprof_jitmap_symbolizer_class_init (SysprofJitmapSymbolizerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass);
object_class->finalize = sysprof_jitmap_symbolizer_finalize;
symbolizer_class->prepare_async = sysprof_jitmap_symbolizer_prepare_async;
symbolizer_class->prepare_finish = sysprof_jitmap_symbolizer_prepare_finish;
symbolizer_class->symbolize = sysprof_jitmap_symbolizer_symbolize;
}
static void
sysprof_jitmap_symbolizer_init (SysprofJitmapSymbolizer *self)
{
self->jitmaps = g_array_new (FALSE, FALSE, sizeof (Jitmap));
g_array_set_clear_func (self->jitmaps, jitmap_clear);
}
SysprofSymbolizer *
sysprof_jitmap_symbolizer_new (void)
{
return g_object_new (SYSPROF_TYPE_JITMAP_SYMBOLIZER, NULL);
}

View File

@ -0,0 +1,42 @@
/* sysprof-jitmap-symbolizer.h
*
* Copyright 2023 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
*/
#pragma once
#include "sysprof-symbolizer.h"
G_BEGIN_DECLS
#define SYSPROF_TYPE_JITMAP_SYMBOLIZER (sysprof_jitmap_symbolizer_get_type())
#define SYSPROF_IS_JITMAP_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_JITMAP_SYMBOLIZER)
#define SYSPROF_JITMAP_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_JITMAP_SYMBOLIZER, SysprofJitmapSymbolizer)
#define SYSPROF_JITMAP_SYMBOLIZER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_JITMAP_SYMBOLIZER, SysprofJitmapSymbolizerClass)
typedef struct _SysprofJitmapSymbolizer SysprofJitmapSymbolizer;
typedef struct _SysprofJitmapSymbolizerClass SysprofJitmapSymbolizerClass;
SYSPROF_AVAILABLE_IN_ALL
GType sysprof_jitmap_symbolizer_get_type (void) G_GNUC_CONST;
SYSPROF_AVAILABLE_IN_ALL
SysprofSymbolizer *sysprof_jitmap_symbolizer_new (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofJitmapSymbolizer, g_object_unref)
G_END_DECLS

View File

@ -0,0 +1,107 @@
/* sysprof-journald-source.c
*
* Copyright 2023 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 <systemd/sd-journal.h>
#include "sysprof-journald-source.h"
typedef struct _SysprofJournaldSource
{
GSource parent_instance;
sd_journal *journal;
SysprofJournaldSourceFunc func;
gpointer func_data;
GDestroyNotify func_data_destroy;
} SysprofJournaldSource;
static void
sysprof_journald_source_finalize (GSource *source)
{
SysprofJournaldSource *self = (SysprofJournaldSource *)source;
if (self->func_data_destroy)
self->func_data_destroy (self->func_data);
g_clear_pointer (&self->journal, sd_journal_close);
}
static gboolean
sysprof_journald_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
SysprofJournaldSource *self = (SysprofJournaldSource *)source;
if (sd_journal_process (self->journal) == SD_JOURNAL_APPEND)
{
if (sd_journal_next (self->journal) > 0)
return self->func (self->func_data, self->journal);
}
return G_SOURCE_CONTINUE;
}
static GSourceFuncs sysprof_journald_source_funcs = {
.dispatch = sysprof_journald_source_dispatch,
.finalize = sysprof_journald_source_finalize,
};
GSource *
sysprof_journald_source_new (SysprofJournaldSourceFunc func,
gpointer func_data,
GDestroyNotify func_data_destroy)
{
SysprofJournaldSource *self;
sd_journal *journal = NULL;
int fd;
if (sd_journal_open (&journal, 0) < 0)
return NULL;
if (sd_journal_seek_tail (journal) < 0)
goto failure;
if (sd_journal_previous (journal) < 0)
goto failure;
if (sd_journal_next (journal) < 0)
goto failure;
if (-1 == (fd = sd_journal_get_fd (journal)))
goto failure;
self = (SysprofJournaldSource *)g_source_new (&sysprof_journald_source_funcs,
sizeof (SysprofJournaldSource));
self->journal = journal;
self->func = func;
self->func_data = func_data;
self->func_data_destroy = func_data_destroy;
g_source_add_unix_fd ((GSource *)self, fd, sd_journal_get_events (journal));
return (GSource *)self;
failure:
g_clear_pointer (&journal, sd_journal_close);
return NULL;
}

View File

@ -0,0 +1,36 @@
/* sysprof-journald-source.h
*
* Copyright 2023 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
*/
#pragma once
#include <glib-object.h>
#include <systemd/sd-journal.h>
G_BEGIN_DECLS
typedef gboolean (*SysprofJournaldSourceFunc) (gpointer user_data,
sd_journal *journal);
GSource *sysprof_journald_source_new (SysprofJournaldSourceFunc func,
gpointer func_data,
GDestroyNotify func_data_destroy);
G_END_DECLS

Some files were not shown because too many files have changed in this diff Show More