From 5ad096b3f1331b175340129a8c9a5a9d711e5415 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 1 Mar 2015 16:24:19 +0100 Subject: [PATCH] core: expose consumed CPU time per unit This adds support for showing the accumulated consumed CPU time per-unit in the "systemctl status" output. The property is also readable via the bus. --- src/core/cgroup.c | 98 +++++++++++++++++++++++++++++++++++++++++++---- src/core/cgroup.h | 4 ++ src/core/dbus-unit.c | 42 +++++++++++++------- src/core/mount.c | 7 +++- src/core/mount.h | 2 + src/core/scope.c | 3 ++ src/core/service.c | 7 +++- src/core/service.h | 2 + src/core/slice.c | 3 +- src/core/socket.c | 8 +++- src/core/socket.h | 2 + src/core/swap.c | 8 +++- src/core/swap.h | 2 + src/core/unit.c | 10 ++++- src/core/unit.h | 3 ++ src/systemctl/systemctl.c | 9 +++++ 16 files changed, 182 insertions(+), 28 deletions(-) diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 10fdcc9..6b8abb4 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -1029,16 +1029,100 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { assert(cgroup); u = manager_get_unit_by_cgroup(m, cgroup); - if (u) { - r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true); - if (r > 0) { - if (UNIT_VTABLE(u)->notify_cgroup_empty) - UNIT_VTABLE(u)->notify_cgroup_empty(u); + if (!u) + return 0; - unit_add_to_gc_queue(u); - } + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true); + if (r <= 0) + return r; + + if (UNIT_VTABLE(u)->notify_cgroup_empty) + UNIT_VTABLE(u)->notify_cgroup_empty(u); + + unit_add_to_gc_queue(u); + return 0; +} + +int unit_get_memory_current(Unit *u, uint64_t *ret) { + _cleanup_free_ char *v = NULL; + int r; + + assert(u); + assert(ret); + + if (!u->cgroup_path) + return -ENODATA; + + if ((u->cgroup_realized_mask & CGROUP_MEMORY) == 0) + return -ENODATA; + + r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + + return safe_atou64(v, ret); +} + +static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) { + _cleanup_free_ char *v = NULL; + uint64_t ns; + int r; + + assert(u); + assert(ret); + + if (!u->cgroup_path) + return -ENODATA; + + if ((u->cgroup_realized_mask & CGROUP_CPUACCT) == 0) + return -ENODATA; + + r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + + r = safe_atou64(v, &ns); + if (r < 0) + return r; + + *ret = ns; + return 0; +} + +int unit_get_cpu_usage(Unit *u, nsec_t *ret) { + nsec_t ns; + int r; + + r = unit_get_cpu_usage_raw(u, &ns); + if (r < 0) + return r; + + if (ns > u->cpuacct_usage_base) + ns -= u->cpuacct_usage_base; + else + ns = 0; + + *ret = ns; + return 0; +} + +int unit_reset_cpu_usage(Unit *u) { + nsec_t ns; + int r; + + assert(u); + + r = unit_get_cpu_usage_raw(u, &ns); + if (r < 0) { + u->cpuacct_usage_base = 0; + return r; } + u->cpuacct_usage_base = ns; return 0; } diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 993aa9d..869ddae 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -126,5 +126,9 @@ pid_t unit_search_main_pid(Unit *u); int manager_notify_cgroup_empty(Manager *m, const char *group); +int unit_get_memory_current(Unit *u, uint64_t *ret); +int unit_get_cpu_usage(Unit *u, nsec_t *ret); +int unit_reset_cpu_usage(Unit *u); + const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_; CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 0ff9a01..af7dc26 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -661,30 +661,43 @@ static int property_get_current_memory( void *userdata, sd_bus_error *error) { - Unit *u = userdata; uint64_t sz = (uint64_t) -1; + Unit *u = userdata; int r; assert(bus); assert(reply); assert(u); - if (u->cgroup_path && - (u->cgroup_realized_mask & CGROUP_MEMORY)) { - _cleanup_free_ char *v = NULL; + r = unit_get_memory_current(u, &sz); + if (r < 0 && r != -ENODATA) + log_unit_warning_errno(u->id, r, "Failed to get memory.usage_in_bytes attribute: %m"); - r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v); - if (r < 0 && r != -ENOENT) - log_unit_warning_errno(u->id, r, "Couldn't read memory.usage_in_bytes attribute: %m"); + return sd_bus_message_append(reply, "t", sz); +} - if (v) { - r = safe_atou64(v, &sz); - if (r < 0) - log_unit_warning_errno(u->id, r, "Failed to parse memory.usage_in_bytes attribute: %m"); - } - } +static int property_get_cpu_usage( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { - return sd_bus_message_append(reply, "t", sz); + nsec_t ns = (nsec_t) -1; + Unit *u = userdata; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = unit_get_cpu_usage(u, &ns); + if (r < 0 && r != -ENODATA) + log_unit_warning_errno(u->id, r, "Failed to get cpuacct.usage attribute: %m"); + + return sd_bus_message_append(reply, "t", ns); } const sd_bus_vtable bus_unit_cgroup_vtable[] = { @@ -692,6 +705,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0), SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Unit, cgroup_path), 0), SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0), + SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0), SD_BUS_VTABLE_END }; diff --git a/src/core/mount.c b/src/core/mount.c index 8e4a376..5ee679d 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -706,7 +706,11 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { assert(c); assert(_pid); - unit_realize_cgroup(UNIT(m)); + (void) unit_realize_cgroup(UNIT(m)); + if (m->reset_cpu_usage) { + (void) unit_reset_cpu_usage(UNIT(m)); + m->reset_cpu_usage = false; + } r = unit_setup_exec_runtime(UNIT(m)); if (r < 0) @@ -1030,6 +1034,7 @@ static int mount_start(Unit *u) { m->result = MOUNT_SUCCESS; m->reload_result = MOUNT_SUCCESS; + m->reset_cpu_usage = true; mount_enter_mounting(m); return 1; diff --git a/src/core/mount.h b/src/core/mount.h index 76771ab..072b0e0 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -86,6 +86,8 @@ struct Mount { bool just_mounted:1; bool just_changed:1; + bool reset_cpu_usage:1; + bool sloppy_options; MountResult result; diff --git a/src/core/scope.c b/src/core/scope.c index a0e4732..1c3c6bb 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -286,6 +286,9 @@ static int scope_start(Unit *u) { if (!u->transient && UNIT(s)->manager->n_reloading <= 0) return -ENOENT; + (void) unit_realize_cgroup(u); + (void) unit_reset_cpu_usage(u); + r = unit_attach_pids_to_cgroup(u); if (r < 0) return r; diff --git a/src/core/service.c b/src/core/service.c index c7b3505..a89ff3f 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1057,7 +1057,11 @@ static int service_spawn( assert(c); assert(_pid); - unit_realize_cgroup(UNIT(s)); + (void) unit_realize_cgroup(UNIT(s)); + if (s->reset_cpu_usage) { + (void) unit_reset_cpu_usage(UNIT(s)); + s->reset_cpu_usage = false; + } r = unit_setup_exec_runtime(UNIT(s)); if (r < 0) @@ -1828,6 +1832,7 @@ static int service_start(Unit *u) { s->main_pid_known = false; s->main_pid_alien = false; s->forbid_restart = false; + s->reset_cpu_usage = true; free(s->status_text); s->status_text = NULL; diff --git a/src/core/service.h b/src/core/service.h index fe5afef..23124e8 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -189,6 +189,8 @@ struct Service { bool forbid_restart:1; bool start_timeout_defined:1; + bool reset_cpu_usage:1; + char *bus_name; char *status_text; diff --git a/src/core/slice.c b/src/core/slice.c index 4d2eaf7..0bebdbc 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -181,7 +181,8 @@ static int slice_start(Unit *u) { assert(t); assert(t->state == SLICE_DEAD); - unit_realize_cgroup(u); + (void) unit_realize_cgroup(u); + (void) unit_reset_cpu_usage(u); slice_set_state(t, SLICE_ACTIVE); return 1; diff --git a/src/core/socket.c b/src/core/socket.c index 7d052f2..9606ac2 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1392,7 +1392,11 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { assert(c); assert(_pid); - unit_realize_cgroup(UNIT(s)); + (void) unit_realize_cgroup(UNIT(s)); + if (s->reset_cpu_usage) { + (void) unit_reset_cpu_usage(UNIT(s)); + s->reset_cpu_usage = false; + } r = unit_setup_exec_runtime(UNIT(s)); if (r < 0) @@ -1948,6 +1952,8 @@ static int socket_start(Unit *u) { assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED); s->result = SOCKET_SUCCESS; + s->reset_cpu_usage = true; + socket_enter_start_pre(s); return 1; diff --git a/src/core/socket.h b/src/core/socket.h index 5acf214..fa3ebda 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -166,6 +166,8 @@ struct Socket { bool selinux_context_from_net; char *user, *group; + + bool reset_cpu_usage:1; }; /* Called from the service code when collecting fds */ diff --git a/src/core/swap.c b/src/core/swap.c index de3a5d8..4dd6be8 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -604,7 +604,11 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { assert(c); assert(_pid); - unit_realize_cgroup(UNIT(s)); + (void) unit_realize_cgroup(UNIT(s)); + if (s->reset_cpu_usage) { + (void) unit_reset_cpu_usage(UNIT(s)); + s->reset_cpu_usage = false; + } r = unit_setup_exec_runtime(UNIT(s)); if (r < 0) @@ -830,6 +834,8 @@ static int swap_start(Unit *u) { return -EAGAIN; s->result = SWAP_SUCCESS; + s->reset_cpu_usage = true; + swap_enter_activating(s); return 1; } diff --git a/src/core/swap.h b/src/core/swap.h index 5de8c20..9136b9a 100644 --- a/src/core/swap.h +++ b/src/core/swap.h @@ -87,6 +87,8 @@ struct Swap { bool is_active:1; bool just_activated:1; + bool reset_cpu_usage:1; + SwapResult result; usec_t timeout_usec; diff --git a/src/core/unit.c b/src/core/unit.c index 7cd7043..b639d68 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -2602,6 +2602,7 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { unit_serialize_item(u, f, "assert-result", yes_no(u->assert_result)); unit_serialize_item(u, f, "transient", yes_no(u->transient)); + unit_serialize_item_format(u, f, "cpuacct-usage-base", "%" PRIu64, u->cpuacct_usage_base); if (u->cgroup_path) unit_serialize_item(u, f, "cgroup", u->cgroup_path); @@ -2776,6 +2777,12 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { u->transient = b; continue; + } else if (streq(l, "cpuacct-usage-base")) { + + r = safe_atou64(v, &u->cpuacct_usage_base); + if (r < 0) + log_debug("Failed to parse CPU usage %s", v); + } else if (streq(l, "cgroup")) { char *s; @@ -2787,8 +2794,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { void *p; p = hashmap_remove(u->manager->cgroup_unit, u->cgroup_path); - log_info("Removing cgroup_path %s from hashmap (%p)", - u->cgroup_path, p); + log_info("Removing cgroup_path %s from hashmap (%p)", u->cgroup_path, p); free(u->cgroup_path); } diff --git a/src/core/unit.h b/src/core/unit.h index b3775d4..ac5647a 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -176,6 +176,9 @@ struct Unit { UnitFileState unit_file_state; int unit_file_preset; + /* Where the cpuacct.usage cgroup counter was at the time the unit was started */ + nsec_t cpuacct_usage_base; + /* Counterparts in the cgroup filesystem */ char *cgroup_path; CGroupControllerMask cgroup_realized_mask; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 10213af..e915f6f3 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3195,6 +3195,7 @@ typedef struct UnitStatusInfo { /* CGroup */ uint64_t memory_current; uint64_t memory_limit; + uint64_t cpu_usage_nsec; LIST_HEAD(ExecStatusInfo, exec); } UnitStatusInfo; @@ -3465,6 +3466,11 @@ static void print_status_info( printf("\n"); } + if (i->cpu_usage_nsec != (uint64_t) -1) { + char buf[FORMAT_TIMESPAN_MAX]; + printf(" CPU: %s\n", format_timespan(buf, sizeof(buf), i->cpu_usage_nsec / NSEC_PER_USEC, USEC_PER_MSEC)); + } + if (i->control_group && (i->main_pid > 0 || i->control_pid > 0 || ((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group, false) == 0))) { @@ -3683,6 +3689,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo * i->memory_current = u; else if (streq(name, "MemoryLimit")) i->memory_limit = u; + else if (streq(name, "CPUUsageNSec")) + i->cpu_usage_nsec = u; break; } @@ -4156,6 +4164,7 @@ static int show_one( UnitStatusInfo info = { .memory_current = (uint64_t) -1, .memory_limit = (uint64_t) -1, + .cpu_usage_nsec = (uint64_t) -1, }; ExecStatusInfo *p; int r; -- 2.7.4