diff --git a/src/libsysprof-analyze/meson.build b/src/libsysprof-analyze/meson.build index 67536961..c039dae0 100644 --- a/src/libsysprof-analyze/meson.build +++ b/src/libsysprof-analyze/meson.build @@ -3,6 +3,7 @@ libsysprof_analyze_public_sources = [ 'sysprof-callgraph.c', 'sysprof-callgraph-frame.c', 'sysprof-callgraph-symbol.c', + 'sysprof-cpu-info.c', 'sysprof-document.c', 'sysprof-document-allocation.c', 'sysprof-document-counter.c', @@ -37,10 +38,11 @@ libsysprof_analyze_public_sources = [ libsysprof_analyze_public_headers = [ 'sysprof-analyze.h', + 'sysprof-bundled-symbolizer.h', 'sysprof-callgraph.h', 'sysprof-callgraph-frame.h', 'sysprof-callgraph-symbol.h', - 'sysprof-bundled-symbolizer.h', + 'sysprof-cpu-info.h', 'sysprof-document.h', 'sysprof-document-allocation.h', 'sysprof-document-counter.h', diff --git a/src/libsysprof-analyze/sysprof-analyze.h b/src/libsysprof-analyze/sysprof-analyze.h index b830b281..ab4d9ca2 100644 --- a/src/libsysprof-analyze/sysprof-analyze.h +++ b/src/libsysprof-analyze/sysprof-analyze.h @@ -29,6 +29,7 @@ G_BEGIN_DECLS # include "sysprof-callgraph.h" # include "sysprof-callgraph-frame.h" # include "sysprof-callgraph-symbol.h" +# include "sysprof-cpu-info.h" # include "sysprof-document.h" # include "sysprof-document-allocation.h" # include "sysprof-document-counter.h" diff --git a/src/libsysprof-analyze/sysprof-cpu-info-private.h b/src/libsysprof-analyze/sysprof-cpu-info-private.h new file mode 100644 index 00000000..ef785717 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-cpu-info-private.h @@ -0,0 +1,30 @@ +/* sysprof-cpu-info-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 + */ + +#pragma once + +#include "sysprof-cpu-info.h" + +G_BEGIN_DECLS + +void _sysprof_cpu_info_set_model_name (SysprofCpuInfo *self, + const char *model_name); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-cpu-info.c b/src/libsysprof-analyze/sysprof-cpu-info.c new file mode 100644 index 00000000..b24b43b2 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-cpu-info.c @@ -0,0 +1,150 @@ +/* sysprof-cpu-info.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-cpu-info.h" + +struct _SysprofCpuInfo +{ + GObject parent_instance; + char *model_name; + guint id; +}; + +enum { + PROP_0, + PROP_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_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_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_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]); +} diff --git a/src/libsysprof-analyze/sysprof-cpu-info.h b/src/libsysprof-analyze/sysprof-cpu-info.h new file mode 100644 index 00000000..b61babe4 --- /dev/null +++ b/src/libsysprof-analyze/sysprof-cpu-info.h @@ -0,0 +1,39 @@ +/* sysprof-cpu-info.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_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 +const char *sysprof_cpu_info_get_model_name (SysprofCpuInfo *self); + +G_END_DECLS diff --git a/src/libsysprof-analyze/sysprof-document.c b/src/libsysprof-analyze/sysprof-document.c index a1b79d1e..5df46f5a 100644 --- a/src/libsysprof-analyze/sysprof-document.c +++ b/src/libsysprof-analyze/sysprof-document.c @@ -28,6 +28,7 @@ #include "sysprof-document-private.h" #include "sysprof-callgraph-private.h" +#include "sysprof-cpu-info-private.h" #include "sysprof-document-bitset-index-private.h" #include "sysprof-document-counter-private.h" #include "sysprof-document-ctrdef.h" @@ -65,6 +66,8 @@ struct _SysprofDocument GMappedFile *mapped_file; const guint8 *base; + GListStore *cpu_info; + GListStore *counters; GHashTable *counter_id_to_values; @@ -109,6 +112,7 @@ enum { PROP_0, PROP_ALLOCATIONS, PROP_COUNTERS, + PROP_CPU_INFO, PROP_FILES, PROP_LOGS, PROP_METADATA, @@ -337,6 +341,8 @@ sysprof_document_finalize (GObject *object) g_clear_object (&self->counters); g_clear_pointer (&self->counter_id_to_values, g_hash_table_unref); + g_clear_object (&self->cpu_info); + g_clear_object (&self->mount_namespace); g_clear_object (&self->symbols); @@ -363,6 +369,10 @@ sysprof_document_get_property (GObject *object, g_value_take_object (value, sysprof_document_list_counters (self)); break; + case PROP_CPU_INFO: + g_value_take_object (value, sysprof_document_list_cpu_info (self)); + break; + case PROP_FILES: g_value_take_object (value, sysprof_document_list_files (self)); break; @@ -414,6 +424,11 @@ sysprof_document_class_init (SysprofDocumentClass *klass) G_TYPE_LIST_MODEL, (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_CPU_INFO] = + g_param_spec_object ("cpu-info", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_FILES] = g_param_spec_object ("files", NULL, NULL, G_TYPE_LIST_MODEL, @@ -459,6 +474,8 @@ sysprof_document_init (SysprofDocument *self) self->frames = g_array_new (FALSE, FALSE, sizeof (SysprofDocumentFramePointer)); + self->cpu_info = g_list_store_new (SYSPROF_TYPE_CPU_INFO); + self->counters = g_list_store_new (SYSPROF_TYPE_DOCUMENT_COUNTER); self->counter_id_to_values = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_array_unref); @@ -980,6 +997,74 @@ sysprof_document_update_process_exit_times (SysprofDocument *self) } } +static void +sysprof_document_load_cpu (SysprofDocument *self) +{ + const gsize model_len = strlen ("Model\t\t: "); + g_autoptr(SysprofDocumentFile) file = NULL; + g_autoptr(GBytes) bytes = NULL; + g_autoptr(SysprofCpuInfo) cpu_info = NULL; + g_autofree char *model = NULL; + const char *str; + const char *line; + LineReader reader; + gsize line_len; + gsize len; + + g_assert (SYSPROF_IS_DOCUMENT (self)); + + if (!(file = sysprof_document_lookup_file (self, "/proc/cpuinfo")) || + !(bytes = sysprof_document_file_dup_bytes (file))) + return; + + str = (const char *)g_bytes_get_data (bytes, &len); + + line_reader_init (&reader, (char *)str, len); + while ((line = line_reader_next (&reader, &line_len))) + { + if (g_str_has_prefix (line, "processor\t: ")) + { + gint64 id = g_ascii_strtoll (line+strlen("processor\t: "), NULL, 10); + + if (cpu_info != NULL) + g_list_store_append (self->cpu_info, cpu_info); + + g_clear_object (&cpu_info); + + cpu_info = g_object_new (SYSPROF_TYPE_CPU_INFO, + "id", id, + NULL); + } + + if (g_str_has_prefix (line, "model name\t: ")) + { + const gsize model_name_len = strlen ("model name\t: "); + g_autofree char *model_name = g_strndup (line+model_name_len, line_len-model_name_len); + + if (cpu_info != NULL) + _sysprof_cpu_info_set_model_name (cpu_info, model_name); + } + + if (!model && g_str_has_prefix (line, "Model\t\t: ")) + model = g_strndup (line+model_len, line_len-model_len); + } + + if (cpu_info != NULL) + g_list_store_append (self->cpu_info, cpu_info); + + if (model != NULL) + { + guint n_items = g_list_model_get_n_items (G_LIST_MODEL (self->cpu_info)); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofCpuInfo) item = g_list_model_get_item (G_LIST_MODEL (self->cpu_info), i); + + _sysprof_cpu_info_set_model_name (item, model); + } + } +} + static void sysprof_document_load_worker (GTask *task, gpointer source_object, @@ -1223,6 +1308,8 @@ sysprof_document_load_worker (GTask *task, if (guessed_end_nsec > self->time_span.begin_nsec) self->time_span.end_nsec = guessed_end_nsec; + sysprof_document_load_cpu (self); + load_progress (load, .6, _("Discovering file system mounts")); sysprof_document_load_mounts (self); @@ -2186,3 +2273,19 @@ _sysprof_document_set_title (SysprofDocument *self, if (g_set_str (&self->title, title)) g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]); } + +/** + * sysprof_document_list_cpu_info: + * @self: a #SysprofDocument + * + * Gets the CPU that were discovered from the capture. + * + * Returns: (transfer full): a #GListModel of #SysprofCpuInfo + */ +GListModel * +sysprof_document_list_cpu_info (SysprofDocument *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT (self), NULL); + + return g_object_ref (G_LIST_MODEL (self->cpu_info)); +} diff --git a/src/libsysprof-analyze/sysprof-document.h b/src/libsysprof-analyze/sysprof-document.h index c800bcb9..baa53b09 100644 --- a/src/libsysprof-analyze/sysprof-document.h +++ b/src/libsysprof-analyze/sysprof-document.h @@ -47,6 +47,8 @@ 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); diff --git a/src/libsysprof-analyze/tests/meson.build b/src/libsysprof-analyze/tests/meson.build index 8f73e931..921b22b7 100644 --- a/src/libsysprof-analyze/tests/meson.build +++ b/src/libsysprof-analyze/tests/meson.build @@ -16,6 +16,7 @@ libsysprof_analyze_testsuite = { 'test-capture-model' : {'skip': true}, 'test-elf-loader' : {'skip': true}, 'test-list-counters' : {'skip': true}, + 'test-list-cpu' : {'skip': true}, 'test-list-files' : {'skip': true}, 'test-list-jitmap' : {'skip': true}, 'test-list-overlays' : {'skip': true}, diff --git a/src/libsysprof-analyze/tests/test-list-cpu.c b/src/libsysprof-analyze/tests/test-list-cpu.c new file mode 100644 index 00000000..23872b53 --- /dev/null +++ b/src/libsysprof-analyze/tests/test-list-cpu.c @@ -0,0 +1,76 @@ +/* test-list-counters.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 + +#include "sysprof-document-private.h" + +static const GOptionEntry entries[] = { + { 0 } +}; + +int +main (int argc, + char *argv[]) +{ + g_autoptr(GOptionContext) context = g_option_context_new ("- list cpu information from capture"); + g_autoptr(SysprofDocumentLoader) loader = NULL; + g_autoptr(SysprofDocument) document = NULL; + g_autoptr(GListModel) model = NULL; + g_autoptr(GError) error = NULL; + guint n_items; + + g_option_context_add_main_entries (context, entries, NULL); + + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s\n", error->message); + return 1; + } + + if (argc < 2) + { + g_printerr ("usage: %s CAPTURE_FILE\n", argv[0]); + return 1; + } + + loader = sysprof_document_loader_new (argv[1]); + sysprof_document_loader_set_symbolizer (loader, sysprof_no_symbolizer_get ()); + + if (!(document = sysprof_document_loader_load (loader, NULL, &error))) + { + g_printerr ("Failed to open capture: %s\n", error->message); + return 1; + } + + model = sysprof_document_list_cpu_info (document); + n_items = g_list_model_get_n_items (model); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(SysprofCpuInfo) cpu_info = g_list_model_get_item (model, i); + + g_print ("processor %u: %s\n", + sysprof_cpu_info_get_id (cpu_info), + sysprof_cpu_info_get_model_name (cpu_info)); + } + + return 0; +}