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.
This commit is contained in:
Michael Vogt
2026-06-20 10:37:47 +02:00
parent 3eb9864e23
commit 0b0db27050
2 changed files with 99 additions and 0 deletions
+86
View File
@@ -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",
+13
View File
@@ -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)"