diff --git a/src/libsysprof-profile/meson.build b/src/libsysprof-profile/meson.build index 33537699..5f17ff0c 100644 --- a/src/libsysprof-profile/meson.build +++ b/src/libsysprof-profile/meson.build @@ -8,6 +8,7 @@ libsysprof_profile_public_sources = [ 'sysprof-memory-usage.c', 'sysprof-malloc-tracing.c', 'sysprof-network-usage.c', + 'sysprof-power-profile.c', 'sysprof-profiler.c', 'sysprof-proxied-instrument.c', 'sysprof-recording.c', @@ -37,6 +38,7 @@ libsysprof_profile_public_headers = [ 'sysprof-memory-usage.h', 'sysprof-malloc-tracing.h', 'sysprof-network-usage.h', + 'sysprof-power-profile.h', 'sysprof-profiler.h', 'sysprof-proxied-instrument.h', 'sysprof-recording.h', diff --git a/src/libsysprof-profile/sysprof-power-profile.c b/src/libsysprof-profile/sysprof-power-profile.c new file mode 100644 index 00000000..26c08a20 --- /dev/null +++ b/src/libsysprof-profile/sysprof-power-profile.c @@ -0,0 +1,304 @@ +/* sysprof-power-profile.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 + +#include "sysprof-power-profile.h" +#include "sysprof-instrument-private.h" +#include "sysprof-recording-private.h" + +struct _SysprofPowerProfile +{ + SysprofInstrument parent_instance; + SysprofRecording *recording; + char *restore_id; + char *id; +}; + +struct _SysprofPowerProfileClass +{ + SysprofInstrumentClass parent_class; +}; + +G_DEFINE_FINAL_TYPE (SysprofPowerProfile, sysprof_power_profile, SYSPROF_TYPE_INSTRUMENT) + +enum { + PROP_0, + PROP_ID, + N_PROPS +}; + +static GParamSpec *properties[N_PROPS]; + +static void +restore_power_profile (char *power_profile) +{ + g_debug ("Restoring performance profile to %s\n", power_profile); + + if (power_profile != NULL) + { + g_autofree char *hold = power_profile; + g_autoptr(GDBusConnection) bus = NULL; + g_autoptr(GVariant) ret = NULL; + g_autoptr(GError) error = NULL; + + if (!(bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL))) + return; + + ret = g_dbus_connection_call_sync (bus, + "net.hadess.PowerProfiles", + "/net/hadess/PowerProfiles", + "org.freedesktop.DBus.Properties", + "Set", + g_variant_new ("(ssv)", + "net.hadess.PowerProfiles", + "ActiveProfile", + g_variant_new_string (power_profile)), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, &error); + + if (error != NULL) + g_warning ("Failed to restore performance profile: %s", error->message); + } +} + +static DexFuture * +sysprof_power_profile_release_cb (DexFuture *future, + gpointer user_data) +{ + SysprofPowerProfile *self = user_data; + + g_assert (DEX_IS_FUTURE (future)); + g_assert (SYSPROF_IS_POWER_PROFILE (self)); + + g_clear_pointer (&self->restore_id, restore_power_profile); + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +sysprof_power_profile_record (SysprofInstrument *instrument, + SysprofRecording *recording, + GCancellable *cancellable) +{ + g_assert (SYSPROF_IS_POWER_PROFILE (instrument)); + g_assert (SYSPROF_IS_RECORDING (recording)); + g_assert (G_IS_CANCELLABLE (cancellable)); + + return dex_future_finally (dex_cancellable_new_from_cancellable (cancellable), + sysprof_power_profile_release_cb, + g_object_ref (instrument), + g_object_unref); +} + +static char * +get_string_prop (GVariant *v) +{ + g_autoptr(GVariant) child1 = g_variant_get_child_value (v, 0); + g_autoptr(GVariant) child2 = g_variant_get_child_value (child1, 0); + + return g_variant_dup_string (child2, NULL); +} + +static DexFuture * +sysprof_power_profile_prepare_fiber (gpointer user_data) +{ + SysprofPowerProfile *self = user_data; + g_autoptr(GDBusConnection) bus = NULL; + g_autoptr(GVariant) ret = NULL; + g_autoptr(GError) error = NULL; + + g_assert (SYSPROF_IS_POWER_PROFILE (self)); + g_assert (SYSPROF_IS_RECORDING (self->recording)); + + if (self->id == NULL) + goto failure; + + if (!(bus = dex_await_object (dex_bus_get (G_BUS_TYPE_SYSTEM), &error))) + goto failure; + + if (!(ret = dex_await_variant (dex_dbus_connection_call (bus, + "net.hadess.PowerProfiles", + "/net/hadess/PowerProfiles", + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new ("(ss)", + "net.hadess.PowerProfiles", + "ActiveProfile"), + G_VARIANT_TYPE ("(v)"), + G_DBUS_CALL_FLAGS_NONE, + -1), &error))) + goto failure; + + /* Save the value to be restored after recording */ + g_clear_pointer (&self->restore_id, g_free); + self->restore_id = get_string_prop (ret); + g_clear_pointer (&ret, g_variant_unref); + + /* We don't use "HoldPorfile" here because it is not reliable */ + if (g_strcmp0 (self->id, self->restore_id) != 0) + { + if (!(ret = dex_await_variant (dex_dbus_connection_call (bus, + "net.hadess.PowerProfiles", + "/net/hadess/PowerProfiles", + "org.freedesktop.DBus.Properties", + "Set", + g_variant_new ("(ssv)", + "net.hadess.PowerProfiles", + "ActiveProfile", + g_variant_new_string (self->id)), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1), &error))) + goto failure; + + _sysprof_recording_diagnostic (self->recording, + "Power Profile", + "Power profile temporarily set to %s from %s", + self->id, self->restore_id); + } + else + { + g_clear_pointer (&self->restore_id, g_free); + } + +failure: + if (error != NULL) + _sysprof_recording_diagnostic (self->recording, + "Power Profile", + "Failed to set power profile to ā€œ%sā€: %s", + self->id, error->message); + + g_clear_object (&self->recording); + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +sysprof_power_profile_prepare (SysprofInstrument *instrument, + SysprofRecording *recording) +{ + SysprofPowerProfile *self = (SysprofPowerProfile *)instrument; + + g_assert (SYSPROF_IS_POWER_PROFILE (self)); + g_assert (SYSPROF_IS_RECORDING (recording)); + + g_set_object (&self->recording, recording); + + return dex_scheduler_spawn (NULL, 0, + sysprof_power_profile_prepare_fiber, + g_object_ref (self), + g_object_unref); +} + +static void +sysprof_power_profile_dispose (GObject *object) +{ + SysprofPowerProfile *self = (SysprofPowerProfile *)object; + + g_clear_pointer (&self->id, g_free); + g_clear_pointer (&self->restore_id, g_free); + + G_OBJECT_CLASS (sysprof_power_profile_parent_class)->dispose (object); +} + +static void +sysprof_power_profile_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofPowerProfile *self = SYSPROF_POWER_PROFILE (object); + + switch (prop_id) + { + case PROP_ID: + g_value_set_string (value, sysprof_power_profile_get_id (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_power_profile_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofPowerProfile *self = SYSPROF_POWER_PROFILE (object); + + switch (prop_id) + { + case PROP_ID: + self->id = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_power_profile_class_init (SysprofPowerProfileClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); + + object_class->dispose = sysprof_power_profile_dispose; + object_class->get_property = sysprof_power_profile_get_property; + object_class->set_property = sysprof_power_profile_set_property; + + instrument_class->prepare = sysprof_power_profile_prepare; + instrument_class->record = sysprof_power_profile_record; + + properties[PROP_ID] = + g_param_spec_string ("id", 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_power_profile_init (SysprofPowerProfile *self) +{ +} + +SysprofInstrument * +sysprof_power_profile_new (const char *id) +{ + return g_object_new (SYSPROF_TYPE_POWER_PROFILE, + "id", id, + NULL); +} + +const char * +sysprof_power_profile_get_id (SysprofPowerProfile *self) +{ + g_return_val_if_fail (SYSPROF_IS_POWER_PROFILE (self), NULL); + + return self->id; +} diff --git a/src/libsysprof-profile/sysprof-power-profile.h b/src/libsysprof-profile/sysprof-power-profile.h new file mode 100644 index 00000000..5b676ffd --- /dev/null +++ b/src/libsysprof-profile/sysprof-power-profile.h @@ -0,0 +1,44 @@ +/* sysprof-power-profile.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-instrument.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_POWER_PROFILE (sysprof_power_profile_get_type()) +#define SYSPROF_IS_POWER_PROFILE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_POWER_PROFILE) +#define SYSPROF_POWER_PROFILE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_POWER_PROFILE, SysprofPowerProfile) +#define SYSPROF_POWER_PROFILE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_POWER_PROFILE, SysprofPowerProfileClass) + +typedef struct _SysprofPowerProfile SysprofPowerProfile; +typedef struct _SysprofPowerProfileClass SysprofPowerProfileClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_power_profile_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofInstrument *sysprof_power_profile_new (const char *id); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_power_profile_get_id (SysprofPowerProfile *self); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofPowerProfile, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-profile.h b/src/libsysprof-profile/sysprof-profile.h index d3e9afee..6640985a 100644 --- a/src/libsysprof-profile/sysprof-profile.h +++ b/src/libsysprof-profile/sysprof-profile.h @@ -34,6 +34,7 @@ G_BEGIN_DECLS # include "sysprof-malloc-tracing.h" # include "sysprof-memory-usage.h" # include "sysprof-network-usage.h" +# include "sysprof-power-profile.h" # include "sysprof-profiler.h" # include "sysprof-proxied-instrument.h" # include "sysprof-recording.h" diff --git a/src/libsysprof-profile/tests/test-profiler.c b/src/libsysprof-profile/tests/test-profiler.c index 271d8b27..1aa3264d 100644 --- a/src/libsysprof-profile/tests/test-profiler.c +++ b/src/libsysprof-profile/tests/test-profiler.c @@ -31,11 +31,13 @@ static SysprofRecording *active_recording; static gboolean memprof; static gboolean tracer; static gboolean gnome_shell; +static char *power_profile; static const GOptionEntry entries[] = { { "capture", 'c', 0, G_OPTION_ARG_FILENAME, &capture_file, "The file to capture into", "CAPTURE" }, { "memprof", 'm', 0, G_OPTION_ARG_NONE, &memprof, "Do memory allocation tracking on subprocess" }, { "tracer", 't', 0, G_OPTION_ARG_NONE, &tracer, "Enable tracing with __cyg_profile_enter" }, { "gnome-shell", 's', 0, G_OPTION_ARG_NONE, &gnome_shell, "Request GNOME Shell to provide profiler data" }, + { "power-profile", 'p', 0, G_OPTION_ARG_STRING, &power_profile, "Use POWER_PROFILE for duration of recording", "power-saver|balanced|performance" }, { 0 } }; @@ -177,6 +179,9 @@ main (int argc, sysprof_profiler_add_instrument (profiler, sysprof_memory_usage_new ()); sysprof_profiler_add_instrument (profiler, sysprof_network_usage_new ()); + if (power_profile) + sysprof_profiler_add_instrument (profiler, sysprof_power_profile_new (power_profile)); + if (gnome_shell) sysprof_profiler_add_instrument (profiler, sysprof_proxied_instrument_new (G_BUS_TYPE_SESSION,