From 21b735e798c580e7af8c33ace9f8565860b7f8df Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 27 Aug 2015 22:30:43 +0200 Subject: core: add unit_dbus_interface_from_type() to unit-name.h Let's add a way to get the type-specific D-Bus interface of a unit from either its type or name to src/basic/unit-name.[ch]. That way we can share it with the client side, where it is useful in tools like cgls or machinectl. Also ports over machinectl to make use of this. --- src/basic/unit-name.c | 38 +++++++++++++++++++++++++++++++++++++- src/basic/unit-name.h | 3 +++ src/core/automount.c | 1 - src/core/busname.c | 1 - src/core/dbus-unit.c | 2 +- src/core/dbus.c | 34 +++++++++++++++++++--------------- src/core/device.c | 1 - src/core/mount.c | 1 - src/core/path.c | 1 - src/core/scope.c | 1 - src/core/service.c | 1 - src/core/slice.c | 1 - src/core/snapshot.c | 1 - src/core/socket.c | 1 - src/core/swap.c | 1 - src/core/target.c | 1 - src/core/timer.c | 1 - src/core/unit.h | 3 --- src/machine/machinectl.c | 3 +-- 19 files changed, 61 insertions(+), 35 deletions(-) diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index fa530da456..8742ee757f 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -586,6 +586,42 @@ int unit_name_from_dbus_path(const char *path, char **name) { return 0; } +const char* unit_dbus_interface_from_type(UnitType t) { + + static const char *const table[_UNIT_TYPE_MAX] = { + [UNIT_SERVICE] = "org.freedesktop.systemd1.Service", + [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket", + [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName", + [UNIT_TARGET] = "org.freedesktop.systemd1.Target", + [UNIT_SNAPSHOT] = "org.freedesktop.systemd1.Snapshot", + [UNIT_DEVICE] = "org.freedesktop.systemd1.Device", + [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount", + [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount", + [UNIT_SWAP] = "org.freedesktop.systemd1.Swap", + [UNIT_TIMER] = "org.freedesktop.systemd1.Timer", + [UNIT_PATH] = "org.freedesktop.systemd1.Path", + [UNIT_SLICE] = "org.freedesktop.systemd1.Slice", + [UNIT_SCOPE] = "org.freedesktop.systemd1.Scope", + }; + + if (t < 0) + return NULL; + if (t >= _UNIT_TYPE_MAX) + return NULL; + + return table[t]; +} + +const char *unit_dbus_interface_from_name(const char *name) { + UnitType t; + + t = unit_name_to_type(name); + if (t < 0) + return NULL; + + return unit_dbus_interface_from_type(t); +} + static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) { const char *valid_chars; @@ -787,7 +823,7 @@ static const char* const unit_type_table[_UNIT_TYPE_MAX] = { [UNIT_TIMER] = "timer", [UNIT_PATH] = "path", [UNIT_SLICE] = "slice", - [UNIT_SCOPE] = "scope" + [UNIT_SCOPE] = "scope", }; DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType); diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h index b2043d0870..28b3a555f3 100644 --- a/src/basic/unit-name.h +++ b/src/basic/unit-name.h @@ -152,6 +152,9 @@ int unit_name_to_path(const char *name, char **ret); char *unit_dbus_path_from_name(const char *name); int unit_name_from_dbus_path(const char *path, char **name); +const char* unit_dbus_interface_from_type(UnitType t); +const char *unit_dbus_interface_from_name(const char *name); + typedef enum UnitNameMangle { UNIT_NAME_NOGLOB, UNIT_NAME_GLOB, diff --git a/src/core/automount.c b/src/core/automount.c index 4af381b4b6..b8171ddad7 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -1075,7 +1075,6 @@ const UnitVTable automount_vtable = { .reset_failed = automount_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Automount", .bus_vtable = bus_automount_vtable, .shutdown = automount_shutdown, diff --git a/src/core/busname.c b/src/core/busname.c index 9530a87311..d3c1282239 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -1058,7 +1058,6 @@ const UnitVTable busname_vtable = { .supported = busname_supported, - .bus_interface = "org.freedesktop.systemd1.BusName", .bus_vtable = bus_busname_vtable, .status_message_formats = { diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 0a9effda71..42bb653cc1 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -783,7 +783,7 @@ static int send_changed_signal(sd_bus *bus, void *userdata) { r = sd_bus_emit_properties_changed_strv( bus, p, - UNIT_VTABLE(u)->bus_interface, + unit_dbus_interface_from_type(u->type), NULL); if (r < 0) return r; diff --git a/src/core/dbus.c b/src/core/dbus.c index 44bf5cab28..d091aa5419 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -356,7 +356,7 @@ static int bus_unit_interface_find(sd_bus *bus, const char *path, const char *in if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; *found = u; @@ -378,7 +378,7 @@ static int bus_unit_cgroup_find(sd_bus *bus, const char *path, const char *inter if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; if (!unit_get_cgroup_context(u)) @@ -404,7 +404,7 @@ static int bus_cgroup_context_find(sd_bus *bus, const char *path, const char *in if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; c = unit_get_cgroup_context(u); @@ -431,7 +431,7 @@ static int bus_exec_context_find(sd_bus *bus, const char *path, const char *inte if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; c = unit_get_exec_context(u); @@ -458,7 +458,7 @@ static int bus_kill_context_find(sd_bus *bus, const char *path, const char *inte if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; c = unit_get_kill_context(u); @@ -555,30 +555,34 @@ static int bus_setup_api_vtables(Manager *m, sd_bus *bus) { return log_error_errno(r, "Failed to add job enumerator: %m"); for (t = 0; t < _UNIT_TYPE_MAX; t++) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m); + const char *interface; + + assert_se(interface = unit_dbus_interface_from_type(t)); + + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m); if (r < 0) - return log_error_errno(r, "Failed to register type specific vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register type specific vtable for %s: %m", interface); if (unit_vtable[t]->cgroup_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m); if (r < 0) - return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", interface); - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_cgroup_vtable, bus_cgroup_context_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_cgroup_vtable, bus_cgroup_context_find, m); if (r < 0) - return log_error_errno(r, "Failed to register control group vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register control group vtable for %s: %m", interface); } if (unit_vtable[t]->exec_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_exec_vtable, bus_exec_context_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_exec_vtable, bus_exec_context_find, m); if (r < 0) - return log_error_errno(r, "Failed to register execute vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register execute vtable for %s: %m", interface); } if (unit_vtable[t]->kill_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_kill_vtable, bus_kill_context_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_kill_vtable, bus_kill_context_find, m); if (r < 0) - return log_error_errno(r, "Failed to register kill vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register kill vtable for %s: %m", interface); } } diff --git a/src/core/device.c b/src/core/device.c index e7efcf0f0a..3f574b1832 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -849,7 +849,6 @@ const UnitVTable device_vtable = { .active_state = device_active_state, .sub_state_to_string = device_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Device", .bus_vtable = bus_device_vtable, .following = device_following, diff --git a/src/core/mount.c b/src/core/mount.c index c0d1cdfbd4..7e19e66a51 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1871,7 +1871,6 @@ const UnitVTable mount_vtable = { .reset_failed = mount_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Mount", .bus_vtable = bus_mount_vtable, .bus_set_property = bus_mount_set_property, .bus_commit_properties = bus_mount_commit_properties, diff --git a/src/core/path.c b/src/core/path.c index 20995d920c..e9111d0612 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -770,6 +770,5 @@ const UnitVTable path_vtable = { .reset_failed = path_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Path", .bus_vtable = bus_path_vtable }; diff --git a/src/core/scope.c b/src/core/scope.c index ab1769b46b..bf89936153 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -561,7 +561,6 @@ const UnitVTable scope_vtable = { .notify_cgroup_empty = scope_notify_cgroup_empty_event, - .bus_interface = "org.freedesktop.systemd1.Scope", .bus_vtable = bus_scope_vtable, .bus_set_property = bus_scope_set_property, .bus_commit_properties = bus_scope_commit_properties, diff --git a/src/core/service.c b/src/core/service.c index b790ec98be..097e7c710c 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -3214,7 +3214,6 @@ const UnitVTable service_vtable = { .bus_name_owner_change = service_bus_name_owner_change, - .bus_interface = "org.freedesktop.systemd1.Service", .bus_vtable = bus_service_vtable, .bus_set_property = bus_service_set_property, .bus_commit_properties = bus_service_commit_properties, diff --git a/src/core/slice.c b/src/core/slice.c index 064eb5d933..7442d23391 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -289,7 +289,6 @@ const UnitVTable slice_vtable = { .active_state = slice_active_state, .sub_state_to_string = slice_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Slice", .bus_vtable = bus_slice_vtable, .bus_set_property = bus_slice_set_property, .bus_commit_properties = bus_slice_commit_properties, diff --git a/src/core/snapshot.c b/src/core/snapshot.c index 9518e21f36..336ff20f84 100644 --- a/src/core/snapshot.c +++ b/src/core/snapshot.c @@ -302,6 +302,5 @@ const UnitVTable snapshot_vtable = { .active_state = snapshot_active_state, .sub_state_to_string = snapshot_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Snapshot", .bus_vtable = bus_snapshot_vtable }; diff --git a/src/core/socket.c b/src/core/socket.c index a387057473..08efc3754c 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2709,7 +2709,6 @@ const UnitVTable socket_vtable = { .reset_failed = socket_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Socket", .bus_vtable = bus_socket_vtable, .bus_set_property = bus_socket_set_property, .bus_commit_properties = bus_socket_commit_properties, diff --git a/src/core/swap.c b/src/core/swap.c index 0bc3827ff0..349fd6f7b9 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -1485,7 +1485,6 @@ const UnitVTable swap_vtable = { .reset_failed = swap_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Swap", .bus_vtable = bus_swap_vtable, .bus_set_property = bus_swap_set_property, .bus_commit_properties = bus_swap_commit_properties, diff --git a/src/core/target.c b/src/core/target.c index b492a7c4c7..f714cb31c2 100644 --- a/src/core/target.c +++ b/src/core/target.c @@ -221,7 +221,6 @@ const UnitVTable target_vtable = { .active_state = target_active_state, .sub_state_to_string = target_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Target", .bus_vtable = bus_target_vtable, .status_message_formats = { diff --git a/src/core/timer.c b/src/core/timer.c index 89758c6b19..eb6567bbfa 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -772,7 +772,6 @@ const UnitVTable timer_vtable = { .reset_failed = timer_reset_failed, .time_change = timer_time_change, - .bus_interface = "org.freedesktop.systemd1.Timer", .bus_vtable = bus_timer_vtable, .bus_set_property = bus_timer_set_property, diff --git a/src/core/unit.h b/src/core/unit.h index 9df5a7e6fb..f53b7f6da1 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -404,9 +404,6 @@ struct UnitVTable { * of this type will immediately fail. */ bool (*supported)(void); - /* The interface name */ - const char *bus_interface; - /* The bus vtable */ const sd_bus_vtable *bus_vtable; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 926035d185..8bd0ed756b 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -361,8 +361,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { bus, "org.freedesktop.systemd1", path, - endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : - endswith(unit, ".slice") ? "org.freedesktop.systemd1.Slice" : "org.freedesktop.systemd1.Service", + unit_dbus_interface_from_name(unit), "ControlGroup", &error, &reply, -- cgit v1.2.3-54-g00ecf From 45d7a8bb6c0e0caa4dd2a1cf1108b7ba2c0ebac4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 28 Aug 2015 02:04:33 +0200 Subject: cgtop: major modernizations In preparation of the unified cgroup support, let's clean up cgtop: a) rework time code to be based on "nsec_t" rather than "struct timespec" b) Introduce long option --order= for selecting ordering c) count number of processes only in the main hierarchy, don't bother with the controller hierarchies. We don't allow orthogonal hierarchies in systemd anymore, hence there's no point to check the other hierarchies. d) Deal with non-monotonic cpuacct values (see #749) e) When sorting groups, don't do prefix compare when ordering by number of tasks, since this is not accumulative for all children. f) Actually make --cpu without parameter work g) Don't output control characters when we get them as input. Fixes #749. --- man/systemd-cgtop.xml | 29 +++-- src/basic/time-util.c | 20 ++++ src/basic/time-util.h | 3 + src/cgtop/cgtop.c | 293 +++++++++++++++++++++++++++----------------------- 4 files changed, 203 insertions(+), 142 deletions(-) diff --git a/man/systemd-cgtop.xml b/man/systemd-cgtop.xml index d4b041a1f9..e4bc22f278 100644 --- a/man/systemd-cgtop.xml +++ b/man/systemd-cgtop.xml @@ -1,4 +1,4 @@ - + @@ -64,10 +64,10 @@ regular intervals (by default every 1s), similar in style to top1. - If systemd-cgtop is not connected to a tty, no - column headers are printed and the default is to only run one iteration. - The --iterations argument, if given, is still honored. - This mode is suitable for scripting. + If systemd-cgtop is not connected to a + tty, no column headers are printed and the default is to only run + one iteration. The --iterations= argument, if + given, is honored. This mode is suitable for scripting. Resource usage is only accounted for control groups in the relevant hierarchy, i.e. CPU usage is only accounted for control @@ -104,6 +104,7 @@ + Order by control group path name. @@ -111,25 +112,28 @@ + - Order by number of tasks in control group - (i.e. threads and processes). + Order by number of processes in control group. + Order by CPU load. + Order by memory usage. + Order by disk I/O load. @@ -140,7 +144,7 @@ Run in "batch" mode: do not accept input and run until the iteration limit set with - is exhausted or until killed. + is exhausted or until killed. This mode could be useful for sending output from systemd-cgtop to other programs or to a file. @@ -155,6 +159,15 @@ numbers. + + + + + Controls whether the CPU usage is shown as + percentage or time. By default the CPU usage is shown as + percentage. + + diff --git a/src/basic/time-util.c b/src/basic/time-util.c index e278196c90..b0e5883b87 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -36,6 +36,14 @@ usec_t now(clockid_t clock_id) { return timespec_load(&ts); } +nsec_t now_nsec(clockid_t clock_id) { + struct timespec ts; + + assert_se(clock_gettime(clock_id, &ts) == 0); + + return timespec_load_nsec(&ts); +} + dual_timestamp* dual_timestamp_get(dual_timestamp *ts) { assert(ts); @@ -129,6 +137,18 @@ usec_t timespec_load(const struct timespec *ts) { (usec_t) ts->tv_nsec / NSEC_PER_USEC; } +nsec_t timespec_load_nsec(const struct timespec *ts) { + assert(ts); + + if (ts->tv_sec == (time_t) -1 && + ts->tv_nsec == (long) -1) + return NSEC_INFINITY; + + return + (nsec_t) ts->tv_sec * NSEC_PER_SEC + + (nsec_t) ts->tv_nsec; +} + struct timespec *timespec_store(struct timespec *ts, usec_t u) { assert(ts); diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 2aba042217..ebafefa0ee 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -70,6 +70,7 @@ typedef struct dual_timestamp { #define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0ULL, 0ULL }) usec_t now(clockid_t clock); +nsec_t now_nsec(clockid_t clock); dual_timestamp* dual_timestamp_get(dual_timestamp *ts); dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); @@ -87,6 +88,8 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u); usec_t timeval_load(const struct timeval *tv) _pure_; struct timeval *timeval_store(struct timeval *tv, usec_t u); +nsec_t timespec_load_nsec(const struct timespec *ts) _pure_; + char *format_timestamp(char *buf, size_t l, usec_t t); char *format_timestamp_utc(char *buf, size_t l, usec_t t); char *format_timestamp_us(char *buf, size_t l, usec_t t); diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index f953c9e624..26b104f111 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -19,7 +19,6 @@ along with systemd; If not, see . ***/ -#define __STDC_FORMAT_MACROS #include #include #include @@ -48,20 +47,20 @@ typedef struct Group { unsigned n_tasks; unsigned cpu_iteration; - uint64_t cpu_usage; - struct timespec cpu_timestamp; + nsec_t cpu_usage; + nsec_t cpu_timestamp; double cpu_fraction; uint64_t memory; unsigned io_iteration; uint64_t io_input, io_output; - struct timespec io_timestamp; + nsec_t io_timestamp; uint64_t io_input_bps, io_output_bps; } Group; static unsigned arg_depth = 3; -static unsigned arg_iterations = (unsigned)-1; +static unsigned arg_iterations = (unsigned) -1; static bool arg_batch = false; static bool arg_raw = false; static usec_t arg_delay = 1*USEC_PER_SEC; @@ -111,9 +110,6 @@ static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, off_t static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) { Group *g; int r; - FILE *f = NULL; - pid_t pid; - unsigned n; assert(controller); assert(path); @@ -142,84 +138,81 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap r = hashmap_move_one(a, b, path); if (r < 0) return r; + g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false; } } - /* Regardless which controller, let's find the maximum number - * of processes in any of it */ + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + _cleanup_fclose_ FILE *f = NULL; + pid_t pid; - r = cg_enumerate_processes(controller, path, &f); - if (r < 0) - return r; + r = cg_enumerate_processes(controller, path, &f); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; - n = 0; - while (cg_read_pid(f, &pid) > 0) - n++; - fclose(f); + g->n_tasks = 0; + while (cg_read_pid(f, &pid) > 0) + g->n_tasks++; - if (n > 0) { - if (g->n_tasks_valid) - g->n_tasks = MAX(g->n_tasks, n); - else - g->n_tasks = n; + if (g->n_tasks > 0) + g->n_tasks_valid = true; - g->n_tasks_valid = true; - } - - if (streq(controller, "cpuacct")) { + } else if (streq(controller, "cpuacct")) { + _cleanup_free_ char *p = NULL, *v = NULL; uint64_t new_usage; - char *p, *v; - struct timespec ts; + nsec_t timestamp; r = cg_get_path(controller, path, "cpuacct.usage", &p); if (r < 0) return r; r = read_one_line_file(p, &v); - free(p); + if (r == -ENOENT) + return 0; if (r < 0) return r; r = safe_atou64(v, &new_usage); - free(v); if (r < 0) return r; - assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + timestamp = now_nsec(CLOCK_MONOTONIC); - if (g->cpu_iteration == iteration - 1) { - uint64_t x, y; + if (g->cpu_iteration == iteration - 1 && + (nsec_t) new_usage > g->cpu_usage) { - x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - - ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec); + nsec_t x, y; - y = new_usage - g->cpu_usage; + x = timestamp - g->cpu_timestamp; + if (x < 1) + x = 1; - if (y > 0) { - g->cpu_fraction = (double) y / (double) x; - g->cpu_valid = true; - } + y = (nsec_t) new_usage - g->cpu_usage; + g->cpu_fraction = (double) y / (double) x; + g->cpu_valid = true; } - g->cpu_usage = new_usage; - g->cpu_timestamp = ts; + g->cpu_usage = (nsec_t) new_usage; + g->cpu_timestamp = timestamp; g->cpu_iteration = iteration; } else if (streq(controller, "memory")) { - char *p, *v; + _cleanup_free_ char *p = NULL, *v = NULL; r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); if (r < 0) return r; r = read_one_line_file(p, &v); - free(p); + if (r == -ENOENT) + return 0; if (r < 0) return r; r = safe_atou64(v, &g->memory); - free(v); if (r < 0) return r; @@ -227,19 +220,21 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap g->memory_valid = true; } else if (streq(controller, "blkio")) { - char *p; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; uint64_t wr = 0, rd = 0; - struct timespec ts; + nsec_t timestamp; r = cg_get_path(controller, path, "blkio.io_service_bytes", &p); if (r < 0) return r; f = fopen(p, "re"); - free(p); - - if (!f) + if (!f) { + if (errno == ENOENT) + return 0; return -errno; + } for (;;) { char line[LINE_MAX], *l; @@ -269,20 +264,26 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap *q += k; } - fclose(f); - - assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + timestamp = now_nsec(CLOCK_MONOTONIC); if (g->io_iteration == iteration - 1) { uint64_t x, yr, yw; - x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - - ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec); + x = (uint64_t) (timestamp - g->io_timestamp); + if (x < 1) + x = 1; - yr = rd - g->io_input; - yw = wr - g->io_output; + if (rd > g->io_input) + yr = rd - g->io_input; + else + yr = 0; + + if (wr > g->io_output) + yw = wr - g->io_output; + else + yw = 0; - if (g->io_input > 0 || g->io_output > 0) { + if (yr > 0 || yw > 0) { g->io_input_bps = (yr * 1000000000ULL) / x; g->io_output_bps = (yw * 1000000000ULL) / x; g->io_valid = true; @@ -291,7 +292,7 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap g->io_input = rd; g->io_output = wr; - g->io_timestamp = ts; + g->io_timestamp = timestamp; g->io_iteration = iteration; } @@ -306,7 +307,7 @@ static int refresh_one( unsigned iteration, unsigned depth) { - DIR *d = NULL; + _cleanup_closedir_ DIR *d = NULL; int r; assert(controller); @@ -321,41 +322,29 @@ static int refresh_one( return r; r = cg_enumerate_subgroups(controller, path, &d); - if (r < 0) { - if (r == -ENOENT) - return 0; - + if (r == -ENOENT) + return 0; + if (r < 0) return r; - } for (;;) { - char *fn, *p; + _cleanup_free_ char *fn = NULL, *p = NULL; r = cg_read_subgroup(d, &fn); if (r <= 0) - goto finish; + return r; p = strjoin(path, "/", fn, NULL); - free(fn); - - if (!p) { - r = -ENOMEM; - goto finish; - } + if (!p) + return -ENOMEM; path_kill_slashes(p); r = refresh_one(controller, p, a, b, iteration, depth + 1); - free(p); - if (r < 0) - goto finish; + return r; } -finish: - if (d) - closedir(d); - return r; } @@ -364,35 +353,43 @@ static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) { assert(a); - r = refresh_one("name=systemd", "/", a, b, iteration, 0); + r = refresh_one(SYSTEMD_CGROUP_CONTROLLER, "/", a, b, iteration, 0); if (r < 0) - if (r != -ENOENT) - return r; + return r; r = refresh_one("cpuacct", "/", a, b, iteration, 0); if (r < 0) - if (r != -ENOENT) - return r; + return r; r = refresh_one("memory", "/", a, b, iteration, 0); if (r < 0) - if (r != -ENOENT) - return r; - + return r; r = refresh_one("blkio", "/", a, b, iteration, 0); if (r < 0) - if (r != -ENOENT) - return r; + return r; + return 0; } static int group_compare(const void*a, const void *b) { const Group *x = *(Group**)a, *y = *(Group**)b; - if (path_startswith(y->path, x->path)) - return -1; - if (path_startswith(x->path, y->path)) - return 1; + if (arg_order != ORDER_TASKS) { + /* Let's make sure that the parent is always before + * the child. Except when ordering by tasks, since + * that is actually not accumulative for all + * children. */ + + if (path_startswith(y->path, x->path)) + return -1; + if (path_startswith(x->path, y->path)) + return 1; + } + + switch (arg_order) { + + case ORDER_PATH: + break; - if (arg_order == ORDER_CPU) { + case ORDER_CPU: if (arg_cpu_type == CPU_PERCENT) { if (x->cpu_valid && y->cpu_valid) { if (x->cpu_fraction > y->cpu_fraction) @@ -409,10 +406,10 @@ static int group_compare(const void*a, const void *b) { else if (x->cpu_usage < y->cpu_usage) return 1; } - } - if (arg_order == ORDER_TASKS) { + break; + case ORDER_TASKS: if (x->n_tasks_valid && y->n_tasks_valid) { if (x->n_tasks > y->n_tasks) return -1; @@ -422,9 +419,10 @@ static int group_compare(const void*a, const void *b) { return -1; else if (y->n_tasks_valid) return 1; - } - if (arg_order == ORDER_MEMORY) { + break; + + case ORDER_MEMORY: if (x->memory_valid && y->memory_valid) { if (x->memory > y->memory) return -1; @@ -434,9 +432,10 @@ static int group_compare(const void*a, const void *b) { return -1; else if (y->memory_valid) return 1; - } - if (arg_order == ORDER_IO) { + break; + + case ORDER_IO: if (x->io_valid && y->io_valid) { if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps) return -1; @@ -448,7 +447,7 @@ static int group_compare(const void*a, const void *b) { return 1; } - return strcmp(x->path, y->path); + return path_compare(x->path, y->path); } #define ON ANSI_HIGHLIGHT_ON @@ -481,9 +480,10 @@ static int display(Hashmap *a) { for (j = 0; j < n; j++) { unsigned cputlen, pathtlen; - format_timespan(buffer, sizeof(buffer), (nsec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0); + format_timespan(buffer, sizeof(buffer), (usec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0); cputlen = strlen(buffer); maxtcpu = MAX(maxtcpu, cputlen); + pathtlen = strlen(array[j]->path); maxtpath = MAX(maxtpath, pathtlen); } @@ -503,7 +503,7 @@ static int display(Hashmap *a) { path_columns = 10; printf("%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s\n\n", - arg_order == ORDER_PATH ? ON : "", path_columns, "Path", + arg_order == ORDER_PATH ? ON : "", path_columns, "Control Group", arg_order == ORDER_PATH ? OFF : "", arg_order == ORDER_TASKS ? ON : "", "Tasks", arg_order == ORDER_TASKS ? OFF : "", @@ -519,7 +519,7 @@ static int display(Hashmap *a) { path_columns = maxtpath; for (j = 0; j < n; j++) { - char *p; + _cleanup_free_ char *p = NULL; if (on_tty() && j + 5 > rows) break; @@ -527,8 +527,7 @@ static int display(Hashmap *a) { g = array[j]; p = ellipsize(g->path, path_columns, 33); - printf("%-*s", path_columns, p ? p : g->path); - free(p); + printf("%-*s", path_columns, p ?: g->path); if (g->n_tasks_valid) printf(" %7u", g->n_tasks); @@ -541,7 +540,7 @@ static int display(Hashmap *a) { else fputs(" -", stdout); } else - printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (nsec_t) (g->cpu_usage / NSEC_PER_USEC), 0)); + printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (usec_t) (g->cpu_usage / NSEC_PER_USEC), 0)); printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->memory_valid, g->memory)); printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_input_bps)); @@ -557,14 +556,15 @@ static void help(void) { printf("%s [OPTIONS...]\n\n" "Show top control groups by their resource usage.\n\n" " -h --help Show this help\n" - " --version Print version and exit\n" - " -p Order by path\n" - " -t Order by number of tasks\n" - " -c Order by CPU load\n" - " -m Order by memory load\n" - " -i Order by IO load\n" + " --version Show package version\n" + " -p --order=path Order by path\n" + " -t --order=tasks Order by number of tasks\n" + " -c --order=cpu Order by CPU load (default)\n" + " -m --order=memory Order by memory load\n" + " -i --order=io Order by IO load\n" " -r --raw Provide raw (not human-readable) numbers\n" - " --cpu[=TYPE] Show CPU usage as time or percentage (default)\n" + " --cpu=percentage Show CPU usage as percentage (default)\n" + " --cpu=time Show CPU usage as time\n" " -d --delay=DELAY Delay between updates\n" " -n --iterations=N Run for N iterations before exiting\n" " -b --batch Run in batch mode, accepting no input\n" @@ -577,18 +577,20 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_DEPTH, - ARG_CPU_TYPE + ARG_CPU_TYPE, + ARG_ORDER, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "delay", required_argument, NULL, 'd' }, - { "iterations", required_argument, NULL, 'n' }, - { "batch", no_argument, NULL, 'b' }, - { "raw", no_argument, NULL, 'r' }, - { "depth", required_argument, NULL, ARG_DEPTH }, - { "cpu", optional_argument, NULL, ARG_CPU_TYPE}, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "delay", required_argument, NULL, 'd' }, + { "iterations", required_argument, NULL, 'n' }, + { "batch", no_argument, NULL, 'b' }, + { "raw", no_argument, NULL, 'r' }, + { "depth", required_argument, NULL, ARG_DEPTH }, + { "cpu", optional_argument, NULL, ARG_CPU_TYPE }, + { "order", required_argument, NULL, ARG_ORDER }, {} }; @@ -613,13 +615,17 @@ static int parse_argv(int argc, char *argv[]) { case ARG_CPU_TYPE: if (optarg) { - if (strcmp(optarg, "time") == 0) + if (streq(optarg, "time")) arg_cpu_type = CPU_TIME; - else if (strcmp(optarg, "percentage") == 0) + else if (streq(optarg, "percentage")) arg_cpu_type = CPU_PERCENT; - else + else { + log_error("Unknown argument to --cpu=: %s", optarg); return -EINVAL; - } + } + } else + arg_cpu_type = CPU_TIME; + break; case ARG_DEPTH: @@ -677,6 +683,23 @@ static int parse_argv(int argc, char *argv[]) { arg_order = ORDER_IO; break; + case ARG_ORDER: + if (streq(optarg, "path")) + arg_order = ORDER_PATH; + else if (streq(optarg, "tasks")) + arg_order = ORDER_TASKS; + else if (streq(optarg, "cpu")) + arg_order = ORDER_CPU; + else if (streq(optarg, "memory")) + arg_order = ORDER_MEMORY; + else if (streq(optarg, "io")) + arg_order = ORDER_IO; + else { + log_error("Invalid argument to --order=: %s", optarg); + return -EINVAL; + } + break; + case '?': return -EINVAL; @@ -715,7 +738,7 @@ int main(int argc, char *argv[]) { signal(SIGWINCH, columns_lines_cache_reset); - if (arg_iterations == (unsigned)-1) + if (arg_iterations == (unsigned) -1) arg_iterations = on_tty() ? 0 : 1; while (!quit) { @@ -753,11 +776,10 @@ int main(int argc, char *argv[]) { fputs("\n", stdout); fflush(stdout); - if (arg_batch) { + if (arg_batch) usleep(last_refresh + arg_delay - t); - } else { - r = read_one_char(stdin, &key, - last_refresh + arg_delay - t, NULL); + else { + r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL); if (r == -ETIMEDOUT) continue; if (r < 0) { @@ -843,7 +865,10 @@ int main(int argc, char *argv[]) { break; default: - fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key); + if (key < ' ') + fprintf(stdout, "\nUnknown key '\\x%x'. Ignoring.", key); + else + fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key); fflush(stdout); sleep(1); break; -- cgit v1.2.3-54-g00ecf From e049fa164f0062caab6e53113b3fd7102dc739ce Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 28 Aug 2015 02:23:01 +0200 Subject: cgls: modernize cgls in preparation for unified cgroup hierarchy work Fix --machine= handling, don't hardcode long-obsolete container cgroup paths Many other clean-ups. --- src/cgls/cgls.c | 210 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 111 insertions(+), 99 deletions(-) diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index b8d1d2ccaf..fae9398aa5 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -54,7 +54,7 @@ static void help(void) { " -a --all Show all groups, including empty\n" " -l --full Do not ellipsize output\n" " -k Include kernel threads in output\n" - " -M --machine Show container\n" + " -M --machine= Show container\n" , program_invocation_short_name); } @@ -123,146 +123,158 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -int main(int argc, char *argv[]) { - int r = 0, retval = EXIT_FAILURE; - int output_flags; - _cleanup_free_ char *root = NULL; +static int get_cgroup_root(char **ret) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL; + _cleanup_free_ char *unit = NULL, *path = NULL; + const char *m; + int r; + + if (!arg_machine) { + r = cg_get_root_path(ret); + if (r < 0) + return log_error_errno(r, "Failed to get root control group path: %m"); + + return 0; + } + + m = strjoina("/run/systemd/machines/", arg_machine); + r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); + if (r < 0) + return log_error_errno(r, "Failed to load machine data: %m"); + + path = unit_dbus_path_from_name(unit); + if (!path) + return log_oom(); + + r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + path, + unit_dbus_interface_from_name(unit), + "ControlGroup", + &error, + ret); + if (r < 0) + return log_error_errno(r, "Failed to query unit control group path: %m"); + + return 0; +} + +int main(int argc, char *argv[]) { + int r, output_flags; log_parse_environment(); log_open(); r = parse_argv(argc, argv); - if (r < 0) + if (r <= 0) goto finish; - else if (r == 0) { - retval = EXIT_SUCCESS; - goto finish; - } if (!arg_no_pager) { r = pager_open(false); - if (r > 0) { - if (arg_full == -1) - arg_full = true; - } + if (r > 0 && arg_full < 0) + arg_full = true; } output_flags = arg_all * OUTPUT_SHOW_ALL | (arg_full > 0) * OUTPUT_FULL_WIDTH; - r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus); - if (r < 0) { - log_error_errno(r, "Failed to create bus connection: %m"); - goto finish; - } - if (optind < argc) { + _cleanup_free_ char *root = NULL; int i; + r = get_cgroup_root(&root); + if (r < 0) + goto finish; + for (i = optind; i < argc; i++) { int q; - fprintf(stdout, "%s:\n", argv[i]); - fflush(stdout); - - if (arg_machine) - root = strjoin("machine/", arg_machine, "/", argv[i], NULL); - else - root = strdup(argv[i]); - if (!root) - return log_oom(); - - q = show_cgroup_by_path(root, NULL, 0, - arg_kernel_threads, output_flags); - if (q < 0) - r = q; - } + if (path_startswith(argv[i], "/sys/fs/cgroup")) { - } else { - _cleanup_free_ char *p; + printf("Directory %s:\n", argv[i]); + fflush(stdout); - p = get_current_dir_name(); - if (!p) { - log_error_errno(errno, "Cannot determine current working directory: %m"); - goto finish; - } + q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads, output_flags); + } else { + _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL; + const char *controller, *path; - if (path_startswith(p, "/sys/fs/cgroup") && !arg_machine) { - printf("Working Directory %s:\n", p); - r = show_cgroup_by_path(p, NULL, 0, - arg_kernel_threads, output_flags); - } else { - if (arg_machine) { - char *m; - const char *cgroup; - _cleanup_free_ char *unit = NULL; - _cleanup_free_ char *path = NULL; - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - - m = strjoina("/run/systemd/machines/", arg_machine); - r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); + r = cg_split_spec(argv[i], &c, &p); if (r < 0) { - log_error_errno(r, "Failed to get machine path: %m"); + log_error_errno(r, "Failed to split argument %s: %m", argv[i]); goto finish; } - path = unit_dbus_path_from_name(unit); - if (!path) { - log_oom(); - goto finish; - } + controller = c ?: SYSTEMD_CGROUP_CONTROLLER; + if (p) { + j = strjoin(root, "/", p, NULL); + if (!j) { + r = log_oom(); + goto finish; + } - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - path, - endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service", - "ControlGroup", - &error, - &reply, - "s"); + path_kill_slashes(j); + path = j; + } else + path = root; - if (r < 0) { - log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r)); - goto finish; - } + printf("Controller %s; control group %s:\n", controller, path); + fflush(stdout); - r = sd_bus_message_read(reply, "s", &cgroup); - if (r < 0) { - bus_log_parse_error(r); - goto finish; - } + q = show_cgroup(controller, path, NULL, 0, arg_kernel_threads, output_flags); + } - root = strdup(cgroup); - if (!root) { - log_oom(); - goto finish; - } + if (q < 0) + r = q; + } + + } else { + bool done = false; - } else - r = cg_get_root_path(&root); - if (r < 0) { - log_error_errno(r, "Failed to get %s path: %m", - arg_machine ? "machine" : "root"); + if (!arg_machine) { + _cleanup_free_ char *cwd = NULL; + + cwd = get_current_dir_name(); + if (!cwd) { + r = log_error_errno(errno, "Cannot determine current working directory: %m"); goto finish; } - r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, - arg_kernel_threads, output_flags); + if (path_startswith(cwd, "/sys/fs/cgroup")) { + printf("Working directory %s:\n", cwd); + fflush(stdout); + + r = show_cgroup_by_path(cwd, NULL, 0, arg_kernel_threads, output_flags); + done = true; + } + } + + if (!done) { + _cleanup_free_ char *root = NULL; + + r = get_cgroup_root(&root); + if (r < 0) + goto finish; + + printf("Control group %s:\n", root); + fflush(stdout); + + r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, arg_kernel_threads, output_flags); } } - if (r < 0) { - log_error_errno(r, "Failed to list cgroup tree %s: %m", root); - retval = EXIT_FAILURE; - } else - retval = EXIT_SUCCESS; + if (r < 0) + log_error_errno(r, "Failed to list cgroup tree: %m"); finish: pager_close(); - return retval; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } -- cgit v1.2.3-54-g00ecf