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.
This commit is contained in:
Christian Hergert
2023-04-24 17:21:59 -07:00
parent cc7e2820f3
commit dcbeb0f87a
9 changed files with 408 additions and 0 deletions

View File

@ -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',

View File

@ -0,0 +1,29 @@
/* sysprof-capture-frame-object-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
*/
#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

View File

@ -0,0 +1,72 @@
/* sysprof-capture-frame-object.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-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;
}

View File

@ -0,0 +1,37 @@
/* sysprof-capture-frame-object.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_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

View File

@ -0,0 +1,165 @@
/* sysprof-capture-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-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);
}

View File

@ -0,0 +1,38 @@
/* sysprof-capture-model.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_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

View File

@ -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"

View File

@ -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,

View File

@ -0,0 +1,56 @@
#include <errno.h>
#include <fcntl.h>
#include <sysprof.h>
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;
}