diff options
author | Lennart Poettering <lennart@poettering.net> | 2015-03-01 16:24:19 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2015-03-02 12:15:25 +0100 |
commit | 5ad096b3f1331b175340129a8c9a5a9d711e5415 (patch) | |
tree | 41896760dde9b0ca2d45d04484c4a2308a600b0e /src/core | |
parent | 606303a93ea52a70ebba55bb3152820e630f2164 (diff) |
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.
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/cgroup.c | 98 | ||||
-rw-r--r-- | src/core/cgroup.h | 4 | ||||
-rw-r--r-- | src/core/dbus-unit.c | 42 | ||||
-rw-r--r-- | src/core/mount.c | 7 | ||||
-rw-r--r-- | src/core/mount.h | 2 | ||||
-rw-r--r-- | src/core/scope.c | 3 | ||||
-rw-r--r-- | src/core/service.c | 7 | ||||
-rw-r--r-- | src/core/service.h | 2 | ||||
-rw-r--r-- | src/core/slice.c | 3 | ||||
-rw-r--r-- | src/core/socket.c | 8 | ||||
-rw-r--r-- | src/core/socket.h | 2 | ||||
-rw-r--r-- | src/core/swap.c | 8 | ||||
-rw-r--r-- | src/core/swap.h | 2 | ||||
-rw-r--r-- | src/core/unit.c | 10 | ||||
-rw-r--r-- | src/core/unit.h | 3 |
15 files changed, 173 insertions, 28 deletions
diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 10fdcc9984..6b8abb4802 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 993aa9db7d..869ddae8c4 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 0ff9a01e11..af7dc26241 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 8e4a376944..5ee679da7c 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 76771ab7a9..072b0e0447 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 a0e4732533..1c3c6bb540 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 c7b35050bd..a89ff3f96c 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 fe5afef46b..23124e8eab 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 4d2eaf7ed6..0bebdbcbc6 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 7d052f2ef9..9606ac2b1c 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 5acf214e1c..fa3ebdafa0 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 de3a5d8b10..4dd6be8c9a 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 5de8c20c04..9136b9abab 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 7cd704351c..b639d686ad 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 b3775d4d89..ac5647a7f2 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; |