From: Michael Vogt Date: Tue, 26 May 2026 13:52:40 +0000 (+0200) Subject: core: add kernel/userspace/finish boot timestamps to io.systemd.Manager metrics X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0b0db27050595251b40b4e7cf56593a275eaf3c2;p=thirdparty%2Fsystemd.git core: add kernel/userspace/finish boot timestamps to io.systemd.Manager metrics This commit adds the boot timeline (MANAGER_TIMESTAMP_KERNEL/USERSPACE/FINISH) as metrics. The kernel CLOCK_MONOTONIC value is 0 by definition, so only its .Realtime is reported. For userspace and finish report both .Realtime and .Monotonic. The naming follows D-Bus. --- diff --git a/src/core/varlink-metrics.c b/src/core/varlink-metrics.c index 9992264c788..0e6bfa96189 100644 --- a/src/core/varlink-metrics.c +++ b/src/core/varlink-metrics.c @@ -7,6 +7,7 @@ #include "manager.h" #include "metrics.h" #include "service.h" +#include "string-util.h" #include "unit-def.h" #include "unit.h" #include "varlink-metrics.h" @@ -97,6 +98,59 @@ static int version_build_json(const MetricFamily *mf, sd_varlink *vl, void *user /* fields= */ NULL); } +static int boot_timestamp_build_json( + const MetricFamily *mf, + sd_varlink *vl, + const dual_timestamp *t, + bool with_monotonic) { + + int r; + + assert(mf && mf->name); + assert(vl); + assert(t); + + if (timestamp_is_set(t->realtime)) { + r = metric_build_send_unsigned( + mf, /* the .Realtime metric family entry */ + vl, + /* object= */ NULL, + t->realtime, + /* fields= */ NULL); + if (r < 0) + return r; + } + + if (with_monotonic && timestamp_is_set(t->monotonic)) { + assert(endswith(mf[1].name, ".Monotonic")); + r = metric_build_send_unsigned( + mf + 1, /* the .Monotonic sibling is the next entry */ + vl, + /* object= */ NULL, + t->monotonic, + /* fields= */ NULL); + if (r < 0) + return r; + } + + return 0; +} + +static int kernel_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { + Manager *manager = ASSERT_PTR(userdata); + return boot_timestamp_build_json(mf, vl, &manager->timestamps[MANAGER_TIMESTAMP_KERNEL], /* with_monotonic= */ false); +} + +static int userspace_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { + Manager *manager = ASSERT_PTR(userdata); + return boot_timestamp_build_json(mf, vl, &manager->timestamps[MANAGER_TIMESTAMP_USERSPACE], /* with_monotonic= */ true); +} + +static int finish_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { + Manager *manager = ASSERT_PTR(userdata); + return boot_timestamp_build_json(mf, vl, &manager->timestamps[MANAGER_TIMESTAMP_FINISH], /* with_monotonic= */ true); +} + static int state_change_timestamp_build_json(const MetricFamily *mf, sd_varlink *vl, void *userdata) { Manager *manager = ASSERT_PTR(userdata); Unit *unit; @@ -403,6 +457,19 @@ static const MetricFamily metric_family_table[] = { .type = METRIC_FAMILY_TYPE_GAUGE, .generate = active_timestamp_build_json, }, + { + .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "FinishTimestamp.Realtime", + .description = "CLOCK_REALTIME microseconds at which userspace finished booting", + .type = METRIC_FAMILY_TYPE_GAUGE, + .generate = finish_timestamp_build_json, + }, + { + .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "FinishTimestamp.Monotonic", + .description = "CLOCK_MONOTONIC microseconds at which userspace finished booting", + .type = METRIC_FAMILY_TYPE_GAUGE, + .generate = NULL, + }, + /* Keep those ↑ in sync with finish_timestamp_build_json(). */ { .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "InactiveExitTimestamp", .description = "Per unit metric: timestamp when the unit last exited the inactive state in microseconds; 0 indicates the transition has not occurred", @@ -415,6 +482,12 @@ static const MetricFamily metric_family_table[] = { .type = METRIC_FAMILY_TYPE_GAUGE, .generate = jobs_queued_build_json, }, + { + .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "KernelTimestamp.Realtime", + .description = "CLOCK_REALTIME microseconds at which the kernel started (CLOCK_MONOTONIC == 0)", + .type = METRIC_FAMILY_TYPE_GAUGE, + .generate = kernel_timestamp_build_json, + }, { .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "NRestarts", .description = "Per unit metric: number of restarts", @@ -481,6 +554,19 @@ static const MetricFamily metric_family_table[] = { .type = METRIC_FAMILY_TYPE_GAUGE, .generate = units_total_build_json, }, + { + .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "UserspaceTimestamp.Realtime", + .description = "CLOCK_REALTIME microseconds at which userspace was reached", + .type = METRIC_FAMILY_TYPE_GAUGE, + .generate = userspace_timestamp_build_json, + }, + { + .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "UserspaceTimestamp.Monotonic", + .description = "CLOCK_MONOTONIC microseconds at which userspace was reached", + .type = METRIC_FAMILY_TYPE_GAUGE, + .generate = NULL, + }, + /* Keep those ↑ in sync with userspace_timestamp_build_json(). */ { .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "Version", .description = "Version of systemd", diff --git a/test/units/TEST-74-AUX-UTILS.report.sh b/test/units/TEST-74-AUX-UTILS.report.sh index e2d94df75b8..46f61b3b266 100755 --- a/test/units/TEST-74-AUX-UTILS.report.sh +++ b/test/units/TEST-74-AUX-UTILS.report.sh @@ -85,6 +85,19 @@ metrics_version="$(varlinkctl call --more /run/systemd/report/io.systemd.Manager [ -n "$metrics_version" ] systemctl --version | grep -F "($metrics_version)" >/dev/null +# Boot timeline timestamps. The kernel CLOCK_MONOTONIC is 0 by definition, so only its realtime is +# reported; userspace reports both realtime and monotonic. We don't check FinishTimestamp here: +# the test runs as a service, so manager_check_finished() typically hasn't fired yet and the +# timestamp is unset (the metric is then suppressed). Likewise KernelTimestamp is cleared when +# systemd runs inside a container (see main.c), so the metric is suppressed there too. +manager_metrics="$(varlinkctl call --more /run/systemd/report/io.systemd.Manager io.systemd.Metrics.List {})" +metric_value() { echo "$manager_metrics" | jq --seq -r "select(.name == \"io.systemd.Manager.$1\") | .value | tostring"; } +if ! systemd-detect-virt --quiet --container; then + [ "$(metric_value KernelTimestamp.Realtime)" -gt 0 ] +fi +[ "$(metric_value UserspaceTimestamp.Realtime)" -gt 0 ] +[ "$(metric_value UserspaceTimestamp.Monotonic)" -gt 0 ] + # test io.systemd.Basic.MachineInfo.* metrics, sourced from /etc/machine-info if [ -e /etc/machine-info ]; then MACHINE_INFO_BACKUP="$(mktemp)"