[systemd-devel] [PATCH 2/2] systemd-analyze: --type cpuacct option for blame

Umut Tezduyar umut at tezduyar.com
Sun Mar 24 11:23:53 PDT 2013


systemd-analyze blame --type cpuacct displays cpu time
usage information of the cgroup. The information displayed
is cpuacct.usage.

ControlGroup=cpuacct:/foo/bar for a service would work.
ControlGroupPersistent=yes for a oneshot service keeps
cpuacct around so blame can retrieve it.
DefaultControllers=cpuacct on system.conf can be set
to have cpuacct same as systemd cgroup.
---
 src/analyze/systemd-analyze.c |  131 +++++++++++++++++++++++++++++++++++++----
 1 files changed, 119 insertions(+), 12 deletions(-)

diff --git a/src/analyze/systemd-analyze.c b/src/analyze/systemd-analyze.c
index 3dcde30..062bf31 100644
--- a/src/analyze/systemd-analyze.c
+++ b/src/analyze/systemd-analyze.c
@@ -61,6 +61,11 @@ static enum dot {
         DEP_REQUIRE
 } arg_dot = DEP_ALL;
 
+static enum type {
+        TYPE_TIME,
+        TYPE_CPUACCT,
+} arg_type = TYPE_TIME;
+
 struct boot_times {
         usec_t firmware_time;
         usec_t loader_time;
@@ -77,6 +82,7 @@ struct unit_stat {
         usec_t axt;
         usec_t aet;
         usec_t time;
+        usec_t cpuacct;
 };
 
 static int bus_get_uint64_property(DBusConnection *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
@@ -146,6 +152,83 @@ static int aquire_time_data (DBusConnection *bus, struct unit_info *u, struct un
         return 0;
 }
 
+static void aquire_cpuacct_data (char *s, struct unit_stat *t) {
+        uint64_t cpuacct;
+        char *p, *v = NULL;
+        int r;
+
+        if (!startswith(s, "cpuacct:/"))
+                return;
+        if (cg_get_path("cpuacct", s+9, "cpuacct.usage", &p) >= 0 &&
+            read_one_line_file(p, &v) >= 0 &&
+            safe_atou64(v, &cpuacct) >= 0)
+                t->cpuacct = cpuacct;
+
+        free(p);
+        free(v);
+}
+
+static int aquire_cgroup_data (DBusConnection *bus, struct unit_info *u, struct unit_stat *t) {
+        _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+        DBusMessageIter iter, sub;
+        const char *interface = "org.freedesktop.systemd1.Service";
+        const char *property = "ControlGroups";
+        int r;
+
+        t->cpuacct = 0;
+
+        /* Only interested in .service units */
+        if (!endswith(u->unit_path, "service"))
+                return 0;
+
+        r = bus_method_call_with_reply(
+                        bus,
+                        "org.freedesktop.systemd1",
+                        u->unit_path,
+                        "org.freedesktop.DBus.Properties",
+                        "Get",
+                        &reply,
+                        NULL,
+                        DBUS_TYPE_STRING, &interface,
+                        DBUS_TYPE_STRING, &property,
+                        DBUS_TYPE_INVALID);
+        if (r < 0)
+                return r;
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
+                log_error("Failed to parse reply.");
+                return -EIO;
+        }
+
+        dbus_message_iter_recurse(&iter, &sub);
+
+        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY)  {
+                log_error("Failed to parse reply.");
+                return -EIO;
+        }
+
+        if (dbus_message_iter_get_element_type(&sub) == DBUS_TYPE_STRING) {
+
+                DBusMessageIter sub1;
+
+                dbus_message_iter_recurse(&sub, &sub1);
+                while (dbus_message_iter_get_arg_type(&sub1) == DBUS_TYPE_STRING) {
+                        const char *s;
+                        dbus_message_iter_get_basic(&sub1, &s);
+                        aquire_cpuacct_data (s, t);
+                        dbus_message_iter_next(&sub1);
+                }
+        }
+
+        return 0;
+}
+
+static int compare_unit_cpuacct(const void *a, const void *b) {
+        return compare(((struct unit_stat *)b)->cpuacct,
+                       ((struct unit_stat *)a)->cpuacct);
+}
+
 static int compare_unit_time(const void *a, const void *b) {
         return compare(((struct unit_stat *)b)->time,
                        ((struct unit_stat *)a)->time);
@@ -180,7 +263,7 @@ static void free_unit_stats(struct unit_stat *t, unsigned n) {
         free(t);
 }
 
-static int acquire_stat_data(DBusConnection *bus, struct unit_stat **out) {
+static int acquire_stat_data(DBusConnection *bus, enum type ty, struct unit_stat **out) {
         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
         DBusMessageIter iter, sub;
         int r, c = 0, n_units = 0;
@@ -240,9 +323,15 @@ static int acquire_stat_data(DBusConnection *bus, struct unit_stat **out) {
 
                 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
 
-                r = aquire_time_data (bus, &u, t);
-                if (r < 0)
-                        goto fail;
+                if (ty == TYPE_CPUACCT) {
+                        r = aquire_cgroup_data(bus, &u, t);
+                        if (r < 0)
+                                goto fail;
+                } else {
+                        r = aquire_time_data(bus, &u, t);
+                        if (r < 0)
+                                goto fail;
+                }
 
                 t->name = strdup(u.id);
                 if (t->name == NULL) {
@@ -400,7 +489,7 @@ static int analyze_plot(DBusConnection *bus) {
         get_os_name(&osname);
         assert_se(uname(&name) >= 0);
 
-        n = acquire_stat_data(bus, &times);
+        n = acquire_stat_data(bus, TYPE_TIME, &times);
         if (n <= 0)
                 return n;
 
@@ -559,20 +648,31 @@ static int analyze_blame(DBusConnection *bus) {
         unsigned i;
         int n;
 
-        n = acquire_stat_data(bus, &times);
+        if (arg_type == TYPE_CPUACCT)
+                n = acquire_stat_data(bus, TYPE_CPUACCT, &times);
+        else
+                n = acquire_stat_data(bus, TYPE_TIME, &times);
+
         if (n <= 0)
                 return n;
 
-        qsort(times, n, sizeof(struct unit_stat), compare_unit_time);
+        if (arg_type == TYPE_CPUACCT)
+                qsort(times, n, sizeof(struct unit_stat), compare_unit_cpuacct);
+        else
+                qsort(times, n, sizeof(struct unit_stat), compare_unit_time);
 
         for (i = 0; i < (unsigned) n; i++) {
                 char ts[FORMAT_TIMESPAN_MAX];
 
-                if (times[i].ixt == 0)
-                        continue;
-
-                if (times[i].time > 0)
-                        printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time), times[i].name);
+                if (arg_type == TYPE_CPUACCT) {
+                        if (times[i].cpuacct > 0)
+                                printf("%llu %s\n", times[i].cpuacct, times[i].name);
+                } else {
+                        if (times[i].ixt == 0)
+                                continue;
+                        if (times[i].time > 0)
+                                printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time), times[i].name);
+                }
         }
 
         free_unit_stats(times, (unsigned) n);
@@ -759,6 +859,7 @@ static void analyze_help(void)
                "     --version        Show package version\n"
                "     --system         Connect to system manager\n"
                "     --user           Connect to user service manager\n"
+               "     --type           Type of data (time or cpuacct) to display when using blame\n"
                "     --order          When generating a dependency graph, show only order\n"
                "     --require        When generating a dependency graph, show only requirement\n\n"
                "Commands:\n"
@@ -786,6 +887,7 @@ static int parse_argv(int argc, char *argv[])
                 { "require",   no_argument,       NULL, ARG_REQUIRE   },
                 { "user",      no_argument,       NULL, ARG_USER      },
                 { "system",    no_argument,       NULL, ARG_SYSTEM    },
+                { "type",      required_argument, NULL, 't'           },
                 { NULL,        0,                 NULL, 0             }
         };
 
@@ -819,6 +921,11 @@ static int parse_argv(int argc, char *argv[])
                         arg_dot = DEP_REQUIRE;
                         break;
 
+                case 't':
+                        if (strcmp(optarg, "cpuacct") == 0)
+                                arg_type = TYPE_CPUACCT;
+                        break;
+
                 case -1:
                         return 1;
 
-- 
1.7.2.5



More information about the systemd-devel mailing list