<div dir="ltr"><div style>Hi</div>-Feel free to change --type to something else<div style>-Feel free to change cpuacct to cpuacct.usage</div><div style>-Can be extended to collect other cgroup information like memory usage</div>
<div style>-Would be nice to have "systemd-analyze plot --type cpuacct" to draw a pie diagram.</div><div style>Thanks</div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Sun, Mar 24, 2013 at 7:23 PM, Umut Tezduyar <span dir="ltr"><<a href="mailto:umut@tezduyar.com" target="_blank">umut@tezduyar.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">systemd-analyze blame --type cpuacct displays cpu time<br>
usage information of the cgroup. The information displayed<br>
is cpuacct.usage.<br>
<br>
ControlGroup=cpuacct:/foo/bar for a service would work.<br>
ControlGroupPersistent=yes for a oneshot service keeps<br>
cpuacct around so blame can retrieve it.<br>
DefaultControllers=cpuacct on system.conf can be set<br>
to have cpuacct same as systemd cgroup.<br>
---<br>
src/analyze/systemd-analyze.c | 131 +++++++++++++++++++++++++++++++++++++----<br>
1 files changed, 119 insertions(+), 12 deletions(-)<br>
<br>
diff --git a/src/analyze/systemd-analyze.c b/src/analyze/systemd-analyze.c<br>
index 3dcde30..062bf31 100644<br>
--- a/src/analyze/systemd-analyze.c<br>
+++ b/src/analyze/systemd-analyze.c<br>
@@ -61,6 +61,11 @@ static enum dot {<br>
DEP_REQUIRE<br>
} arg_dot = DEP_ALL;<br>
<br>
+static enum type {<br>
+ TYPE_TIME,<br>
+ TYPE_CPUACCT,<br>
+} arg_type = TYPE_TIME;<br>
+<br>
struct boot_times {<br>
usec_t firmware_time;<br>
usec_t loader_time;<br>
@@ -77,6 +82,7 @@ struct unit_stat {<br>
usec_t axt;<br>
usec_t aet;<br>
usec_t time;<br>
+ usec_t cpuacct;<br>
};<br>
<br>
static int bus_get_uint64_property(DBusConnection *bus, const char *path, const char *interface, const char *property, uint64_t *val) {<br>
@@ -146,6 +152,83 @@ static int aquire_time_data (DBusConnection *bus, struct unit_info *u, struct un<br>
return 0;<br>
}<br>
<br>
+static void aquire_cpuacct_data (char *s, struct unit_stat *t) {<br>
+ uint64_t cpuacct;<br>
+ char *p, *v = NULL;<br>
+ int r;<br>
+<br>
+ if (!startswith(s, "cpuacct:/"))<br>
+ return;<br>
+ if (cg_get_path("cpuacct", s+9, "cpuacct.usage", &p) >= 0 &&<br>
+ read_one_line_file(p, &v) >= 0 &&<br>
+ safe_atou64(v, &cpuacct) >= 0)<br>
+ t->cpuacct = cpuacct;<br>
+<br>
+ free(p);<br>
+ free(v);<br>
+}<br>
+<br>
+static int aquire_cgroup_data (DBusConnection *bus, struct unit_info *u, struct unit_stat *t) {<br>
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;<br>
+ DBusMessageIter iter, sub;<br>
+ const char *interface = "org.freedesktop.systemd1.Service";<br>
+ const char *property = "ControlGroups";<br>
+ int r;<br>
+<br>
+ t->cpuacct = 0;<br>
+<br>
+ /* Only interested in .service units */<br>
+ if (!endswith(u->unit_path, "service"))<br>
+ return 0;<br>
+<br>
+ r = bus_method_call_with_reply(<br>
+ bus,<br>
+ "org.freedesktop.systemd1",<br>
+ u->unit_path,<br>
+ "org.freedesktop.DBus.Properties",<br>
+ "Get",<br>
+ &reply,<br>
+ NULL,<br>
+ DBUS_TYPE_STRING, &interface,<br>
+ DBUS_TYPE_STRING, &property,<br>
+ DBUS_TYPE_INVALID);<br>
+ if (r < 0)<br>
+ return r;<br>
+<br>
+ if (!dbus_message_iter_init(reply, &iter) ||<br>
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {<br>
+ log_error("Failed to parse reply.");<br>
+ return -EIO;<br>
+ }<br>
+<br>
+ dbus_message_iter_recurse(&iter, &sub);<br>
+<br>
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY) {<br>
+ log_error("Failed to parse reply.");<br>
+ return -EIO;<br>
+ }<br>
+<br>
+ if (dbus_message_iter_get_element_type(&sub) == DBUS_TYPE_STRING) {<br>
+<br>
+ DBusMessageIter sub1;<br>
+<br>
+ dbus_message_iter_recurse(&sub, &sub1);<br>
+ while (dbus_message_iter_get_arg_type(&sub1) == DBUS_TYPE_STRING) {<br>
+ const char *s;<br>
+ dbus_message_iter_get_basic(&sub1, &s);<br>
+ aquire_cpuacct_data (s, t);<br>
+ dbus_message_iter_next(&sub1);<br>
+ }<br>
+ }<br>
+<br>
+ return 0;<br>
+}<br>
+<br>
+static int compare_unit_cpuacct(const void *a, const void *b) {<br>
+ return compare(((struct unit_stat *)b)->cpuacct,<br>
+ ((struct unit_stat *)a)->cpuacct);<br>
+}<br>
+<br>
static int compare_unit_time(const void *a, const void *b) {<br>
return compare(((struct unit_stat *)b)->time,<br>
((struct unit_stat *)a)->time);<br>
@@ -180,7 +263,7 @@ static void free_unit_stats(struct unit_stat *t, unsigned n) {<br>
free(t);<br>
}<br>
<br>
-static int acquire_stat_data(DBusConnection *bus, struct unit_stat **out) {<br>
+static int acquire_stat_data(DBusConnection *bus, enum type ty, struct unit_stat **out) {<br>
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;<br>
DBusMessageIter iter, sub;<br>
int r, c = 0, n_units = 0;<br>
@@ -240,9 +323,15 @@ static int acquire_stat_data(DBusConnection *bus, struct unit_stat **out) {<br>
<br>
assert_cc(sizeof(usec_t) == sizeof(uint64_t));<br>
<br>
- r = aquire_time_data (bus, &u, t);<br>
- if (r < 0)<br>
- goto fail;<br>
+ if (ty == TYPE_CPUACCT) {<br>
+ r = aquire_cgroup_data(bus, &u, t);<br>
+ if (r < 0)<br>
+ goto fail;<br>
+ } else {<br>
+ r = aquire_time_data(bus, &u, t);<br>
+ if (r < 0)<br>
+ goto fail;<br>
+ }<br>
<br>
t->name = strdup(<a href="http://u.id" target="_blank">u.id</a>);<br>
if (t->name == NULL) {<br>
@@ -400,7 +489,7 @@ static int analyze_plot(DBusConnection *bus) {<br>
get_os_name(&osname);<br>
assert_se(uname(&name) >= 0);<br>
<br>
- n = acquire_stat_data(bus, ×);<br>
+ n = acquire_stat_data(bus, TYPE_TIME, ×);<br>
if (n <= 0)<br>
return n;<br>
<br>
@@ -559,20 +648,31 @@ static int analyze_blame(DBusConnection *bus) {<br>
unsigned i;<br>
int n;<br>
<br>
- n = acquire_stat_data(bus, ×);<br>
+ if (arg_type == TYPE_CPUACCT)<br>
+ n = acquire_stat_data(bus, TYPE_CPUACCT, ×);<br>
+ else<br>
+ n = acquire_stat_data(bus, TYPE_TIME, ×);<br>
+<br>
if (n <= 0)<br>
return n;<br>
<br>
- qsort(times, n, sizeof(struct unit_stat), compare_unit_time);<br>
+ if (arg_type == TYPE_CPUACCT)<br>
+ qsort(times, n, sizeof(struct unit_stat), compare_unit_cpuacct);<br>
+ else<br>
+ qsort(times, n, sizeof(struct unit_stat), compare_unit_time);<br>
<br>
for (i = 0; i < (unsigned) n; i++) {<br>
char ts[FORMAT_TIMESPAN_MAX];<br>
<br>
- if (times[i].ixt == 0)<br>
- continue;<br>
-<br>
- if (times[i].time > 0)<br>
- printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time), times[i].name);<br>
+ if (arg_type == TYPE_CPUACCT) {<br>
+ if (times[i].cpuacct > 0)<br>
+ printf("%llu %s\n", times[i].cpuacct, times[i].name);<br>
+ } else {<br>
+ if (times[i].ixt == 0)<br>
+ continue;<br>
+ if (times[i].time > 0)<br>
+ printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time), times[i].name);<br>
+ }<br>
}<br>
<br>
free_unit_stats(times, (unsigned) n);<br>
@@ -759,6 +859,7 @@ static void analyze_help(void)<br>
" --version Show package version\n"<br>
" --system Connect to system manager\n"<br>
" --user Connect to user service manager\n"<br>
+ " --type Type of data (time or cpuacct) to display when using blame\n"<br>
" --order When generating a dependency graph, show only order\n"<br>
" --require When generating a dependency graph, show only requirement\n\n"<br>
"Commands:\n"<br>
@@ -786,6 +887,7 @@ static int parse_argv(int argc, char *argv[])<br>
{ "require", no_argument, NULL, ARG_REQUIRE },<br>
{ "user", no_argument, NULL, ARG_USER },<br>
{ "system", no_argument, NULL, ARG_SYSTEM },<br>
+ { "type", required_argument, NULL, 't' },<br>
{ NULL, 0, NULL, 0 }<br>
};<br>
<br>
@@ -819,6 +921,11 @@ static int parse_argv(int argc, char *argv[])<br>
arg_dot = DEP_REQUIRE;<br>
break;<br>
<br>
+ case 't':<br>
+ if (strcmp(optarg, "cpuacct") == 0)<br>
+ arg_type = TYPE_CPUACCT;<br>
+ break;<br>
+<br>
case -1:<br>
return 1;<br>
<span class="HOEnZb"><font color="#888888"><br>
--<br>
1.7.2.5<br>
<br>
</font></span></blockquote></div><br></div>