From dcbeb0f87a52068f0fdd7a557f87368a3f81917f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 24 Apr 2023 17:21:59 -0700 Subject: [PATCH] libsysprof: add SysprofCaptureModel This adds a GListModel that we can use to load capture files. The goal here is to map the entire capture into memory so we can avoid reading lots of buffers. That also allows for the model items to live as long as the model is alive (or underlying file map, really). The next goal is to stack features on top of this such as implementing the callgraph as a filter of the model, or generic filters between the callgraph model and the actual data source model. --- src/libsysprof/meson.build | 4 + .../sysprof-capture-frame-object-private.h | 29 +++ src/libsysprof/sysprof-capture-frame-object.c | 72 ++++++++ src/libsysprof/sysprof-capture-frame-object.h | 37 ++++ src/libsysprof/sysprof-capture-model.c | 165 ++++++++++++++++++ src/libsysprof/sysprof-capture-model.h | 38 ++++ src/libsysprof/sysprof.h | 2 + src/tests/meson.build | 5 + src/tests/test-capture-model.c | 56 ++++++ 9 files changed, 408 insertions(+) create mode 100644 src/libsysprof/sysprof-capture-frame-object-private.h create mode 100644 src/libsysprof/sysprof-capture-frame-object.c create mode 100644 src/libsysprof/sysprof-capture-frame-object.h create mode 100644 src/libsysprof/sysprof-capture-model.c create mode 100644 src/libsysprof/sysprof-capture-model.h create mode 100644 src/tests/test-capture-model.c diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build index a730ef11..42c5f0af 100644 --- a/src/libsysprof/meson.build +++ b/src/libsysprof/meson.build @@ -3,7 +3,9 @@ libsysprof_c_args = [ '-DSYSPROF_COMPILATION' ] libsysprof_public_sources = [ 'sysprof-battery-source.c', 'sysprof-callgraph-profile.c', + 'sysprof-capture-frame-object.c', 'sysprof-capture-gobject.c', + 'sysprof-capture-model.c', 'sysprof-capture-symbol-resolver.c', 'sysprof-control-source.c', 'sysprof-diskstat-source.c', @@ -36,7 +38,9 @@ libsysprof_public_headers = [ 'sysprof-battery-source.h', 'sysprof-callgraph-profile.h', 'sysprof-capture-autocleanups.h', + 'sysprof-capture-frame-object.h', 'sysprof-capture-gobject.h', + 'sysprof-capture-model.h', 'sysprof-capture-symbol-resolver.h', 'sysprof-control-source.h', 'sysprof-diskstat-source.h', diff --git a/src/libsysprof/sysprof-capture-frame-object-private.h b/src/libsysprof/sysprof-capture-frame-object-private.h new file mode 100644 index 00000000..34d6ce2e --- /dev/null +++ b/src/libsysprof/sysprof-capture-frame-object-private.h @@ -0,0 +1,29 @@ +/* sysprof-capture-frame-object-private.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "sysprof-capture-frame-object.h" + +G_BEGIN_DECLS + +SysprofCaptureFrameObject *sysprof_capture_frame_object_new (GMappedFile *mapped, + gconstpointer data, + gboolean is_native); + +G_END_DECLS diff --git a/src/libsysprof/sysprof-capture-frame-object.c b/src/libsysprof/sysprof-capture-frame-object.c new file mode 100644 index 00000000..1cf6e983 --- /dev/null +++ b/src/libsysprof/sysprof-capture-frame-object.c @@ -0,0 +1,72 @@ +/* sysprof-capture-frame-object.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-capture-frame-object-private.h" + +struct _SysprofCaptureFrameObject +{ + GObject parent_instance; + GMappedFile *mapped_file; + const SysprofCaptureFrame *frame; + guint is_native : 1; +}; + +G_DEFINE_FINAL_TYPE (SysprofCaptureFrameObject, sysprof_capture_frame_object, G_TYPE_OBJECT) + +static void +sysprof_capture_frame_object_finalize (GObject *object) +{ + SysprofCaptureFrameObject *self = (SysprofCaptureFrameObject *)object; + + g_clear_pointer (&self->mapped_file, g_mapped_file_unref); + self->frame = NULL; + + G_OBJECT_CLASS (sysprof_capture_frame_object_parent_class)->finalize (object); +} + +static void +sysprof_capture_frame_object_class_init (SysprofCaptureFrameObjectClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_capture_frame_object_finalize; +} + +static void +sysprof_capture_frame_object_init (SysprofCaptureFrameObject *self) +{ +} + +SysprofCaptureFrameObject * +sysprof_capture_frame_object_new (GMappedFile *mapped_file, + gconstpointer data, + gboolean is_native) +{ + SysprofCaptureFrameObject *self; + + self = g_object_new (SYSPROF_TYPE_CAPTURE_FRAME_OBJECT, NULL); + self->mapped_file = g_mapped_file_ref (mapped_file); + self->frame = data; + self->is_native = !!is_native; + + return self; +} diff --git a/src/libsysprof/sysprof-capture-frame-object.h b/src/libsysprof/sysprof-capture-frame-object.h new file mode 100644 index 00000000..ee64063e --- /dev/null +++ b/src/libsysprof/sysprof-capture-frame-object.h @@ -0,0 +1,37 @@ +/* sysprof-capture-frame-object.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_CAPTURE_FRAME_OBJECT (sysprof_capture_frame_object_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofCaptureFrameObject, sysprof_capture_frame_object, SYSPROF, CAPTURE_FRAME_OBJECT, GObject) + +SYSPROF_AVAILABLE_IN_ALL +SysprofCaptureFrame *sysprof_capture_frame_object_get_frame (SysprofCaptureFrameObject *self); + +G_END_DECLS diff --git a/src/libsysprof/sysprof-capture-model.c b/src/libsysprof/sysprof-capture-model.c new file mode 100644 index 00000000..b9ae28f0 --- /dev/null +++ b/src/libsysprof/sysprof-capture-model.c @@ -0,0 +1,165 @@ +/* sysprof-capture-model.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-capture-frame-object-private.h" +#include "sysprof-capture-model.h" + +struct _SysprofCaptureModel +{ + GObject parent_instance; + GPtrArray *frames; + GMappedFile *mapped_file; + SysprofCaptureFileHeader header; + guint is_native : 1; +}; + +static GType +sysprof_capture_model_get_item_type (GListModel *model) +{ + return SYSPROF_TYPE_CAPTURE_FRAME_OBJECT; +} + +static guint +sysprof_capture_model_get_n_items (GListModel *model) +{ + return SYSPROF_CAPTURE_MODEL (model)->frames->len; +} + +static gpointer +sysprof_capture_model_get_item (GListModel *model, + guint position) +{ + SysprofCaptureModel *self = SYSPROF_CAPTURE_MODEL (model); + + if (position >= self->frames->len) + return NULL; + + return sysprof_capture_frame_object_new (self->mapped_file, + g_ptr_array_index (self->frames, position), + self->is_native); +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_item_type = sysprof_capture_model_get_item_type; + iface->get_n_items = sysprof_capture_model_get_n_items; + iface->get_item = sysprof_capture_model_get_item; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (SysprofCaptureModel, sysprof_capture_model, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) + +static void +sysprof_capture_model_finalize (GObject *object) +{ + SysprofCaptureModel *self = (SysprofCaptureModel *)object; + + g_clear_pointer (&self->mapped_file, g_mapped_file_unref); + g_clear_pointer (&self->frames, g_ptr_array_unref); + + G_OBJECT_CLASS (sysprof_capture_model_parent_class)->finalize (object); +} +static void +sysprof_capture_model_class_init (SysprofCaptureModelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_capture_model_finalize; +} + +static void +sysprof_capture_model_init (SysprofCaptureModel *self) +{ + self->frames = g_ptr_array_new (); +} + +static gboolean +sysprof_capture_model_load (SysprofCaptureModel *self, + int capture_fd, + GError **error) +{ + const guint8 *data; + goffset pos; + gsize len; + + g_assert (SYSPROF_IS_CAPTURE_MODEL (self)); + g_assert (capture_fd > -1); + + if (!(self->mapped_file = g_mapped_file_new_from_fd (capture_fd, FALSE, error))) + return FALSE; + + data = (const guint8 *)g_mapped_file_get_contents (self->mapped_file); + len = g_mapped_file_get_length (self->mapped_file); + + if (len < sizeof self->header) + return FALSE; + + /* Keep a copy of our header */ + memcpy (&self->header, data, sizeof self->header); +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + self->is_native = !!self->header.little_endian; +#else + self->is_native = !self->header.little_endian; +#endif + + if (!self->is_native) + { + self->header.time = GUINT64_SWAP_LE_BE (self->header.time); + self->header.end_time = GUINT64_SWAP_LE_BE (self->header.end_time); + } + + pos = sizeof self->header; + while (pos < (len - sizeof(guint16))) + { + guint16 frame_len; + + memcpy (&frame_len, &data[pos], sizeof frame_len); + if (!self->is_native) + frame_len = GUINT16_SWAP_LE_BE (self->is_native); + + if (frame_len < sizeof (SysprofCaptureFrame)) + break; + + g_ptr_array_add (self->frames, (gpointer)&data[pos]); + + pos += frame_len; + } + + return TRUE; +} + +SysprofCaptureModel * +sysprof_capture_model_new_from_fd (int capture_fd, + GError **error) +{ + g_autoptr(SysprofCaptureModel) self = NULL; + + g_return_val_if_fail (capture_fd > -1, NULL); + + self = g_object_new (SYSPROF_TYPE_CAPTURE_MODEL, NULL); + + if (!sysprof_capture_model_load (self, capture_fd, error)) + return NULL; + + return g_steal_pointer (&self); +} diff --git a/src/libsysprof/sysprof-capture-model.h b/src/libsysprof/sysprof-capture-model.h new file mode 100644 index 00000000..71c2e227 --- /dev/null +++ b/src/libsysprof/sysprof-capture-model.h @@ -0,0 +1,38 @@ +/* sysprof-capture-model.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_CAPTURE_MODEL (sysprof_capture_model_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofCaptureModel, sysprof_capture_model, SYSPROF, CAPTURE_MODEL, GObject) + +SYSPROF_AVAILABLE_IN_ALL +SysprofCaptureModel *sysprof_capture_model_new_from_fd (int capture_fd, + GError **error); + +G_END_DECLS diff --git a/src/libsysprof/sysprof.h b/src/libsysprof/sysprof.h index 5abe64b6..ac96871b 100644 --- a/src/libsysprof/sysprof.h +++ b/src/libsysprof/sysprof.h @@ -28,7 +28,9 @@ G_BEGIN_DECLS # include "sysprof-battery-source.h" # include "sysprof-callgraph-profile.h" # include "sysprof-capture-autocleanups.h" +# include "sysprof-capture-frame-object.h" # include "sysprof-capture-gobject.h" +# include "sysprof-capture-model.h" # include "sysprof-capture-symbol-resolver.h" # include "sysprof-control-source.h" # include "sysprof-diskstat-source.h" diff --git a/src/tests/meson.build b/src/tests/meson.build index 611ff260..2dbc717b 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -56,6 +56,11 @@ if get_option('libsysprof') dependencies: test_deps, ) + test_capture_model = executable('test-capture-model', 'test-capture-model.c', + c_args: test_cflags, + dependencies: test_deps, + ) + test_mountinfo = executable('test-mountinfo', 'test-mountinfo.c', c_args: test_cflags, dependencies: test_deps, diff --git a/src/tests/test-capture-model.c b/src/tests/test-capture-model.c new file mode 100644 index 00000000..aca52f0b --- /dev/null +++ b/src/tests/test-capture-model.c @@ -0,0 +1,56 @@ +#include +#include + +#include + +int +main (int argc, + char *argv[]) +{ + SysprofCaptureModel *model; + const char *filename; + GError *error = NULL; + guint n_items; + int fd; + + sysprof_clock_init (); + + if (argc < 2) + { + g_printerr ("usage: %s FILENAME\n", argv[0]); + return 1; + } + + filename = argv[1]; + fd = open (filename, O_RDONLY|O_CLOEXEC); + + if (fd == -1) + { + g_printerr ("Failed to open %s: %s\n", + filename, g_strerror (errno)); + return 1; + } + + if (!(model = sysprof_capture_model_new_from_fd (fd, &error))) + { + g_printerr ("Failed to load %s: %s\n", + filename, error->message); + return 1; + } + + n_items = g_list_model_get_n_items (G_LIST_MODEL (model)); + + g_print ("%u frames\n", n_items); + + for (guint i = 0; i < n_items; i++) + { + SysprofCaptureFrameObject *obj = g_list_model_get_item (G_LIST_MODEL (model), i); + + g_clear_object (&obj); + } + + close (fd); + g_clear_object (&model); + + return 0; +}