#include "manager.h"
#include "metrics.h"
#include "service.h"
+#include "string-util.h"
#include "unit-def.h"
#include "unit.h"
#include "varlink-metrics.h"
/* 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;
.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",
.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",
.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",
[ -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)"