1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010-2013 Lennart Poettering
7 Copyright 2013 Simon Peeters
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include <sys/utsname.h>
32 #include "dbus-common.h"
38 #include "unit-name.h"
43 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
46 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
48 #define svg(...) printf(__VA_ARGS__)
50 #define svg_bar(class, x1, x2, y) \
51 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
53 SCALE_X * (x1), SCALE_Y * (y), \
54 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
56 #define svg_text(b, x, y, format, ...) \
58 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
59 svg(format, ## __VA_ARGS__); \
63 static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
69 static char** arg_dot_from_patterns = NULL;
70 static char** arg_dot_to_patterns = NULL;
71 static usec_t arg_fuzz = 0;
72 static bool arg_no_pager = false;
78 usec_t kernel_done_time;
80 usec_t userspace_time;
82 usec_t generators_start_time;
83 usec_t generators_finish_time;
84 usec_t unitsload_start_time;
85 usec_t unitsload_finish_time;
97 static void pager_open_if_enabled(void) {
105 static int bus_get_uint64_property(DBusConnection *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
106 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
107 DBusMessageIter iter, sub;
110 r = bus_method_call_with_reply(
112 "org.freedesktop.systemd1",
114 "org.freedesktop.DBus.Properties",
118 DBUS_TYPE_STRING, &interface,
119 DBUS_TYPE_STRING, &property,
124 if (!dbus_message_iter_init(reply, &iter) ||
125 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
126 log_error("Failed to parse reply.");
130 dbus_message_iter_recurse(&iter, &sub);
132 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64) {
133 log_error("Failed to parse reply.");
137 dbus_message_iter_get_basic(&sub, val);
142 static int compare_unit_time(const void *a, const void *b) {
143 return compare(((struct unit_times *)b)->time,
144 ((struct unit_times *)a)->time);
147 static int compare_unit_start(const void *a, const void *b) {
148 return compare(((struct unit_times *)a)->ixt,
149 ((struct unit_times *)b)->ixt);
152 static int get_os_name(char **_n) {
156 r = parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &n, NULL);
167 static void free_unit_times(struct unit_times *t, unsigned n) {
168 struct unit_times *p;
170 for (p = t; p < t + n; p++)
176 static int acquire_time_data(DBusConnection *bus, struct unit_times **out) {
177 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
178 DBusMessageIter iter, sub;
179 int r, c = 0, n_units = 0;
180 struct unit_times *unit_times = NULL;
182 r = bus_method_call_with_reply(
184 "org.freedesktop.systemd1",
185 "/org/freedesktop/systemd1",
186 "org.freedesktop.systemd1.Manager",
194 if (!dbus_message_iter_init(reply, &iter) ||
195 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
196 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
197 log_error("Failed to parse reply.");
202 for (dbus_message_iter_recurse(&iter, &sub);
203 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
204 dbus_message_iter_next(&sub)) {
206 struct unit_times *t;
208 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
209 log_error("Failed to parse reply.");
215 struct unit_times *w;
217 n_units = MAX(2*c, 16);
218 w = realloc(unit_times, sizeof(struct unit_times) * n_units);
230 r = bus_parse_unit_info(&sub, &u);
234 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
236 if (bus_get_uint64_property(bus, u.unit_path,
237 "org.freedesktop.systemd1.Unit",
238 "InactiveExitTimestampMonotonic",
240 bus_get_uint64_property(bus, u.unit_path,
241 "org.freedesktop.systemd1.Unit",
242 "ActiveEnterTimestampMonotonic",
244 bus_get_uint64_property(bus, u.unit_path,
245 "org.freedesktop.systemd1.Unit",
246 "ActiveExitTimestampMonotonic",
248 bus_get_uint64_property(bus, u.unit_path,
249 "org.freedesktop.systemd1.Unit",
250 "InactiveEnterTimestampMonotonic",
256 if (t->aet >= t->ixt)
257 t->time = t->aet - t->ixt;
258 else if (t->iet >= t->ixt)
259 t->time = t->iet - t->ixt;
266 t->name = strdup(u.id);
267 if (t->name == NULL) {
278 free_unit_times(unit_times, (unsigned) c);
282 static int acquire_boot_times(DBusConnection *bus, struct boot_times **bt) {
283 static struct boot_times times;
284 static bool cached = false;
289 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
291 if (bus_get_uint64_property(bus,
292 "/org/freedesktop/systemd1",
293 "org.freedesktop.systemd1.Manager",
294 "FirmwareTimestampMonotonic",
295 ×.firmware_time) < 0 ||
296 bus_get_uint64_property(bus,
297 "/org/freedesktop/systemd1",
298 "org.freedesktop.systemd1.Manager",
299 "LoaderTimestampMonotonic",
300 ×.loader_time) < 0 ||
301 bus_get_uint64_property(bus,
302 "/org/freedesktop/systemd1",
303 "org.freedesktop.systemd1.Manager",
305 ×.kernel_time) < 0 ||
306 bus_get_uint64_property(bus,
307 "/org/freedesktop/systemd1",
308 "org.freedesktop.systemd1.Manager",
309 "InitRDTimestampMonotonic",
310 ×.initrd_time) < 0 ||
311 bus_get_uint64_property(bus,
312 "/org/freedesktop/systemd1",
313 "org.freedesktop.systemd1.Manager",
314 "UserspaceTimestampMonotonic",
315 ×.userspace_time) < 0 ||
316 bus_get_uint64_property(bus,
317 "/org/freedesktop/systemd1",
318 "org.freedesktop.systemd1.Manager",
319 "FinishTimestampMonotonic",
320 ×.finish_time) < 0 ||
321 bus_get_uint64_property(bus,
322 "/org/freedesktop/systemd1",
323 "org.freedesktop.systemd1.Manager",
324 "GeneratorsStartTimestampMonotonic",
325 ×.generators_start_time) < 0 ||
326 bus_get_uint64_property(bus,
327 "/org/freedesktop/systemd1",
328 "org.freedesktop.systemd1.Manager",
329 "GeneratorsFinishTimestampMonotonic",
330 ×.generators_finish_time) < 0 ||
331 bus_get_uint64_property(bus,
332 "/org/freedesktop/systemd1",
333 "org.freedesktop.systemd1.Manager",
334 "UnitsLoadStartTimestampMonotonic",
335 ×.unitsload_start_time) < 0 ||
336 bus_get_uint64_property(bus,
337 "/org/freedesktop/systemd1",
338 "org.freedesktop.systemd1.Manager",
339 "UnitsLoadFinishTimestampMonotonic",
340 ×.unitsload_finish_time) < 0)
343 if (times.finish_time <= 0) {
344 log_error("Bootup is not yet finished. Please try again later.");
348 if (times.initrd_time)
349 times.kernel_done_time = times.initrd_time;
351 times.kernel_done_time = times.userspace_time;
360 static int pretty_boot_time(DBusConnection *bus, char **_buf) {
361 char ts[FORMAT_TIMESPAN_MAX];
362 struct boot_times *t;
363 static char buf[4096];
368 r = acquire_boot_times(bus, &t);
375 size = strpcpyf(&ptr, size, "Startup finished in ");
376 if (t->firmware_time)
377 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
379 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
381 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
382 if (t->initrd_time > 0)
383 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
385 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
386 if (t->kernel_time > 0)
387 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
389 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
399 static void svg_graph_box(double height, double begin, double end) {
402 /* outside box, fill */
403 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
404 SCALE_X * (end - begin), SCALE_Y * height);
406 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
407 /* lines for each second */
408 if (i % 5000000 == 0)
409 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
410 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
411 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
412 else if (i % 1000000 == 0)
413 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
414 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
415 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
417 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
418 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
422 static int analyze_plot(DBusConnection *bus) {
423 struct unit_times *times;
424 struct boot_times *boot;
428 _cleanup_free_ char *pretty_times = NULL, *osname = NULL;
429 struct unit_times *u;
431 n = acquire_boot_times(bus, &boot);
435 n = pretty_boot_time(bus, &pretty_times);
439 get_os_name(&osname);
440 assert_se(uname(&name) >= 0);
442 n = acquire_time_data(bus, ×);
446 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
448 width = SCALE_X * (boot->firmware_time + boot->finish_time);
452 if (boot->firmware_time > boot->loader_time)
454 if (boot->loader_time) {
459 if (boot->initrd_time)
461 if (boot->kernel_time)
464 for (u = times; u < times + n; u++) {
467 if (u->ixt < boot->userspace_time ||
468 u->ixt > boot->finish_time) {
473 len = ((boot->firmware_time + u->ixt) * SCALE_X)
474 + (10.0 * strlen(u->name));
478 if (u->iet > u->ixt && u->iet <= boot->finish_time
479 && u->aet == 0 && u->axt == 0)
480 u->aet = u->axt = u->iet;
481 if (u->aet < u->ixt || u->aet > boot->finish_time)
482 u->aet = boot->finish_time;
483 if (u->axt < u->aet || u->aet > boot->finish_time)
484 u->axt = boot->finish_time;
485 if (u->iet < u->axt || u->iet > boot->finish_time)
486 u->iet = boot->finish_time;
490 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
491 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
492 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
494 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
495 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
496 80.0 + width, 150.0 + (m * SCALE_Y) +
497 5 * SCALE_Y /* legend */);
499 /* write some basic info as a comment, including some help */
500 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
501 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
502 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
503 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
504 "<!-- point your browser to this file. -->\n\n"
505 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
508 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
509 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
510 " rect.background { fill: rgb(255,255,255); }\n"
511 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
512 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
513 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
514 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
515 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
516 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
517 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
518 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
519 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
520 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
521 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
522 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
524 " line.sec5 { stroke-width: 2; }\n"
525 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
526 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
527 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
528 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
529 " text.sec { font-size: 10px; }\n"
530 " ]]>\n </style>\n</defs>\n\n");
532 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
533 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
534 svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
535 isempty(osname) ? "Linux" : osname,
536 name.nodename, name.release, name.version, name.machine);
538 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
539 svg_graph_box(m, -boot->firmware_time, boot->finish_time);
541 if (boot->firmware_time) {
542 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
543 svg_text(true, -(double) boot->firmware_time, y, "firmware");
546 if (boot->loader_time) {
547 svg_bar("loader", -(double) boot->loader_time, 0, y);
548 svg_text(true, -(double) boot->loader_time, y, "loader");
551 if (boot->kernel_time) {
552 svg_bar("kernel", 0, boot->kernel_done_time, y);
553 svg_text(true, 0, y, "kernel");
556 if (boot->initrd_time) {
557 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
558 svg_text(true, boot->initrd_time, y, "initrd");
561 svg_bar("active", boot->userspace_time, boot->finish_time, y);
562 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
563 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
564 svg_text("left", boot->userspace_time, y, "systemd");
567 for (u = times; u < times + n; u++) {
568 char ts[FORMAT_TIMESPAN_MAX];
574 svg_bar("activating", u->ixt, u->aet, y);
575 svg_bar("active", u->aet, u->axt, y);
576 svg_bar("deactivating", u->axt, u->iet, y);
578 b = u->ixt * SCALE_X > width * 2 / 3;
580 svg_text(b, u->ixt, y, "%s (%s)",
581 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
583 svg_text(b, u->ixt, y, "%s", u->name);
589 svg_bar("activating", 0, 300000, y);
590 svg_text("right", 400000, y, "Activating");
592 svg_bar("active", 0, 300000, y);
593 svg_text("right", 400000, y, "Active");
595 svg_bar("deactivating", 0, 300000, y);
596 svg_text("right", 400000, y, "Deactivating");
598 svg_bar("generators", 0, 300000, y);
599 svg_text("right", 400000, y, "Generators");
601 svg_bar("unitsload", 0, 300000, y);
602 svg_text("right", 400000, y, "Loading unit files");
609 free_unit_times(times, (unsigned) n);
614 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
615 bool last, struct unit_times *times, struct boot_times *boot) {
617 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
619 for (i = level; i != 0; i--)
620 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));
622 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
626 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
627 format_timespan(ts, sizeof(ts), times->ixt - boot->userspace_time, USEC_PER_MSEC),
628 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
629 else if (times->aet > boot->userspace_time)
630 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->aet - boot->userspace_time, USEC_PER_MSEC));
633 } else printf("%s", name);
639 static int list_dependencies_get_dependencies(DBusConnection *bus, const char *name, char ***deps) {
640 static const char dependencies[] =
643 _cleanup_free_ char *path;
644 const char *interface = "org.freedesktop.systemd1.Unit";
646 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
647 DBusMessageIter iter, sub, sub2, sub3;
656 path = unit_dbus_path_from_name(name);
662 r = bus_method_call_with_reply(
664 "org.freedesktop.systemd1",
666 "org.freedesktop.DBus.Properties",
670 DBUS_TYPE_STRING, &interface,
675 if (!dbus_message_iter_init(reply, &iter) ||
676 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
677 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
678 log_error("Failed to parse reply.");
683 dbus_message_iter_recurse(&iter, &sub);
685 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
688 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
689 dbus_message_iter_recurse(&sub, &sub2);
691 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) {
692 log_error("Failed to parse reply.");
697 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
698 log_error("Failed to parse reply.");
703 dbus_message_iter_recurse(&sub2, &sub3);
704 dbus_message_iter_next(&sub);
706 if (!nulstr_contains(dependencies, prop))
709 if (dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_ARRAY) {
710 if (dbus_message_iter_get_element_type(&sub3) == DBUS_TYPE_STRING) {
711 DBusMessageIter sub4;
712 dbus_message_iter_recurse(&sub3, &sub4);
714 while (dbus_message_iter_get_arg_type(&sub4) != DBUS_TYPE_INVALID) {
717 assert(dbus_message_iter_get_arg_type(&sub4) == DBUS_TYPE_STRING);
718 dbus_message_iter_get_basic(&sub4, &s);
720 r = strv_extend(&ret, s);
726 dbus_message_iter_next(&sub4);
739 static Hashmap *unit_times_hashmap;
741 static int list_dependencies_compare(const void *_a, const void *_b) {
742 const char **a = (const char**) _a, **b = (const char**) _b;
743 usec_t usa = 0, usb = 0;
744 struct unit_times *times;
746 times = hashmap_get(unit_times_hashmap, *a);
749 times = hashmap_get(unit_times_hashmap, *b);
756 static int list_dependencies_one(DBusConnection *bus, const char *name, unsigned int level, char ***units,
757 unsigned int branches) {
758 _cleanup_strv_free_ char **deps = NULL;
761 usec_t service_longest = 0;
763 struct unit_times *times;
764 struct boot_times *boot;
766 if(strv_extend(units, name))
769 r = list_dependencies_get_dependencies(bus, name, &deps);
773 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
775 r = acquire_boot_times(bus, &boot);
779 STRV_FOREACH(c, deps) {
780 times = hashmap_get(unit_times_hashmap, *c);
783 && times->aet <= boot->finish_time
784 && (times->aet >= service_longest
785 || service_longest == 0)) {
786 service_longest = times->aet;
791 if (service_longest == 0 )
794 STRV_FOREACH(c, deps) {
795 times = hashmap_get(unit_times_hashmap, *c);
796 if (times && times->aet
797 && times->aet <= boot->finish_time
798 && (service_longest - times->aet) <= arg_fuzz) {
806 STRV_FOREACH(c, deps) {
807 times = hashmap_get(unit_times_hashmap, *c);
810 || times->aet > boot->finish_time
811 || service_longest - times->aet > arg_fuzz)
816 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
820 if (strv_contains(*units, *c)) {
821 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
828 r = list_dependencies_one(bus, *c, level + 1, units,
829 (branches << 1) | (to_print ? 1 : 0));
839 static int list_dependencies(DBusConnection *bus, const char *name) {
840 _cleanup_strv_free_ char **units = NULL;
841 char ts[FORMAT_TIMESPAN_MAX];
842 struct unit_times *times;
846 *interface = "org.freedesktop.systemd1.Unit",
848 DBusMessageIter iter, sub;
849 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
850 struct boot_times *boot;
854 path = unit_dbus_path_from_name(name);
858 r = bus_method_call_with_reply (
860 "org.freedesktop.systemd1",
862 "org.freedesktop.DBus.Properties",
866 DBUS_TYPE_STRING, &interface,
867 DBUS_TYPE_STRING, &property,
872 if (!dbus_message_iter_init(reply, &iter) ||
873 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
874 log_error("Failed to parse reply.");
878 dbus_message_iter_recurse(&iter, &sub);
880 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
881 log_error("Failed to parse reply.");
885 dbus_message_iter_get_basic(&sub, &id);
887 times = hashmap_get(unit_times_hashmap, id);
889 r = acquire_boot_times(bus, &boot);
895 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
896 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
897 else if (times->aet > boot->userspace_time)
898 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->aet - boot->userspace_time, USEC_PER_MSEC));
903 return list_dependencies_one(bus, name, 0, &units, 0);
906 static int analyze_critical_chain(DBusConnection *bus, char *names[]) {
907 struct unit_times *times;
912 n = acquire_time_data(bus, ×);
916 h = hashmap_new(string_hash_func, string_compare_func);
920 for (i = 0; i < (unsigned)n; i++) {
921 r = hashmap_put(h, times[i].name, ×[i]);
925 unit_times_hashmap = h;
927 pager_open_if_enabled();
929 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
930 "The time the unit takes to start is printed after the \"+\" character.\n");
932 if (!strv_isempty(names)) {
934 STRV_FOREACH(name, names)
935 list_dependencies(bus, *name);
937 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
940 free_unit_times(times, (unsigned) n);
944 static int analyze_blame(DBusConnection *bus) {
945 struct unit_times *times;
949 n = acquire_time_data(bus, ×);
953 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
955 pager_open_if_enabled();
957 for (i = 0; i < (unsigned) n; i++) {
958 char ts[FORMAT_TIMESPAN_MAX];
960 if (times[i].time > 0)
961 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
964 free_unit_times(times, (unsigned) n);
968 static int analyze_time(DBusConnection *bus) {
969 _cleanup_free_ char *buf = NULL;
972 r = pretty_boot_time(bus, &buf);
980 static int graph_one_property(const char *name, const char *prop, DBusMessageIter *iter, char* patterns[]) {
982 static const char * const colors[] = {
983 "Requires", "[color=\"black\"]",
984 "RequiresOverridable", "[color=\"black\"]",
985 "Requisite", "[color=\"darkblue\"]",
986 "RequisiteOverridable", "[color=\"darkblue\"]",
987 "Wants", "[color=\"grey66\"]",
988 "Conflicts", "[color=\"red\"]",
989 "ConflictedBy", "[color=\"red\"]",
990 "After", "[color=\"green\"]"
993 const char *c = NULL;
1000 for (i = 0; i < ELEMENTSOF(colors); i += 2)
1001 if (streq(colors[i], prop)) {
1009 if (arg_dot != DEP_ALL)
1010 if ((arg_dot == DEP_ORDER) != streq(prop, "After"))
1013 if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY &&
1014 dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
1015 DBusMessageIter sub;
1017 dbus_message_iter_recurse(iter, &sub);
1019 for (dbus_message_iter_recurse(iter, &sub);
1020 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
1021 dbus_message_iter_next(&sub)) {
1026 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
1027 dbus_message_iter_get_basic(&sub, &s);
1029 if (!strv_isempty(arg_dot_from_patterns)) {
1030 match_found = false;
1032 STRV_FOREACH(p, arg_dot_from_patterns)
1033 if (fnmatch(*p, name, 0) == 0) {
1042 if (!strv_isempty(arg_dot_to_patterns)) {
1043 match_found = false;
1045 STRV_FOREACH(p, arg_dot_to_patterns)
1046 if (fnmatch(*p, s, 0) == 0) {
1055 if (!strv_isempty(patterns)) {
1056 match_found = false;
1058 STRV_FOREACH(p, patterns)
1059 if (fnmatch(*p, name, 0) == 0 || fnmatch(*p, s, 0) == 0) {
1067 printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
1074 static int graph_one(DBusConnection *bus, const struct unit_info *u, char *patterns[]) {
1075 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1076 const char *interface = "org.freedesktop.systemd1.Unit";
1078 DBusMessageIter iter, sub, sub2, sub3;
1083 r = bus_method_call_with_reply(
1085 "org.freedesktop.systemd1",
1087 "org.freedesktop.DBus.Properties",
1091 DBUS_TYPE_STRING, &interface,
1096 if (!dbus_message_iter_init(reply, &iter) ||
1097 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
1098 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
1099 log_error("Failed to parse reply.");
1103 for (dbus_message_iter_recurse(&iter, &sub);
1104 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
1105 dbus_message_iter_next(&sub)) {
1108 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
1109 dbus_message_iter_recurse(&sub, &sub2);
1111 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0 ||
1112 dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
1113 log_error("Failed to parse reply.");
1117 dbus_message_iter_recurse(&sub2, &sub3);
1118 r = graph_one_property(u->id, prop, &sub3, patterns);
1126 static int dot(DBusConnection *bus, char* patterns[]) {
1127 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1128 DBusMessageIter iter, sub;
1131 r = bus_method_call_with_reply(
1133 "org.freedesktop.systemd1",
1134 "/org/freedesktop/systemd1",
1135 "org.freedesktop.systemd1.Manager",
1143 if (!dbus_message_iter_init(reply, &iter) ||
1144 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
1145 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
1146 log_error("Failed to parse reply.");
1150 printf("digraph systemd {\n");
1152 for (dbus_message_iter_recurse(&iter, &sub);
1153 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
1154 dbus_message_iter_next(&sub)) {
1157 r = bus_parse_unit_info(&sub, &u);
1161 r = graph_one(bus, &u, patterns);
1168 log_info(" Color legend: black = Requires\n"
1169 " dark blue = Requisite\n"
1170 " dark grey = Wants\n"
1171 " red = Conflicts\n"
1172 " green = After\n");
1175 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1176 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1181 static int dump(DBusConnection *bus, char **args) {
1182 _cleanup_free_ DBusMessage *reply = NULL;
1187 dbus_error_init(&error);
1189 if (!strv_isempty(args)) {
1190 log_error("Too many arguments.");
1194 pager_open_if_enabled();
1196 r = bus_method_call_with_reply(
1198 "org.freedesktop.systemd1",
1199 "/org/freedesktop/systemd1",
1200 "org.freedesktop.systemd1.Manager",
1208 if (!dbus_message_get_args(reply, &error,
1209 DBUS_TYPE_STRING, &text,
1210 DBUS_TYPE_INVALID)) {
1211 log_error("Failed to parse reply: %s", bus_error_message(&error));
1212 dbus_error_free(&error);
1216 fputs(text, stdout);
1220 static int set_log_level(DBusConnection *bus, char **args) {
1221 _cleanup_dbus_error_free_ DBusError error;
1222 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
1223 DBusMessageIter iter, sub;
1224 const char* property = "LogLevel";
1225 const char* interface = "org.freedesktop.systemd1.Manager";
1231 if (strv_length(args) != 1) {
1232 log_error("This command expects one argument only.");
1237 dbus_error_init(&error);
1239 m = dbus_message_new_method_call("org.freedesktop.systemd1",
1240 "/org/freedesktop/systemd1",
1241 "org.freedesktop.DBus.Properties",
1246 dbus_message_iter_init_append(m, &iter);
1248 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
1249 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property) ||
1250 !dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, "s", &sub))
1253 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &value))
1256 if (!dbus_message_iter_close_container(&iter, &sub))
1259 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1261 log_error("Failed to issue method call: %s", bus_error_message(&error));
1268 static void analyze_help(void) {
1270 pager_open_if_enabled();
1272 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1273 "Process systemd profiling information\n\n"
1274 " -h --help Show this help\n"
1275 " --version Show package version\n"
1276 " --system Connect to system manager\n"
1277 " --user Connect to user service manager\n"
1278 " --order When generating a dependency graph, show only order\n"
1279 " --require When generating a dependency graph, show only requirement\n"
1280 " --from-pattern=GLOB, --to-pattern=GLOB\n"
1281 " When generating a dependency graph, filter only origins\n"
1282 " or destinations, respectively\n"
1283 " --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
1284 " services, which finished TIMESPAN earlier, than the\n"
1285 " latest in the branch. The unit of TIMESPAN is seconds\n"
1286 " unless specified with a different unit, i.e. 50ms\n"
1287 " --no-pager Do not pipe output into a pager\n\n"
1289 " time Print time spent in the kernel before reaching userspace\n"
1290 " blame Print list of running units ordered by time to init\n"
1291 " critical-chain Print a tree of the time critical chain of units\n"
1292 " plot Output SVG graphic showing service initialization\n"
1293 " dot Output dependency graph in dot(1) format\n"
1294 " set-log-level LEVEL Set logging threshold for systemd\n"
1295 " dump Output state serialization of service manager\n",
1296 program_invocation_short_name);
1298 /* When updating this list, including descriptions, apply
1299 * changes to shell-completion/bash/systemd and
1300 * shell-completion/systemd-zsh-completion.zsh too. */
1303 static int parse_argv(int argc, char *argv[]) {
1307 ARG_VERSION = 0x100,
1312 ARG_DOT_FROM_PATTERN,
1318 static const struct option options[] = {
1319 { "help", no_argument, NULL, 'h' },
1320 { "version", no_argument, NULL, ARG_VERSION },
1321 { "order", no_argument, NULL, ARG_ORDER },
1322 { "require", no_argument, NULL, ARG_REQUIRE },
1323 { "user", no_argument, NULL, ARG_USER },
1324 { "system", no_argument, NULL, ARG_SYSTEM },
1325 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1326 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1327 { "fuzz", required_argument, NULL, ARG_FUZZ },
1328 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1329 { NULL, 0, NULL, 0 }
1336 switch (getopt_long(argc, argv, "h", options, NULL)) {
1343 puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES);
1347 arg_scope = UNIT_FILE_USER;
1351 arg_scope = UNIT_FILE_SYSTEM;
1355 arg_dot = DEP_ORDER;
1359 arg_dot = DEP_REQUIRE;
1362 case ARG_DOT_FROM_PATTERN:
1363 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1368 case ARG_DOT_TO_PATTERN:
1369 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1375 r = parse_sec(optarg, &arg_fuzz);
1381 arg_no_pager = true;
1391 assert_not_reached("Unhandled option");
1396 int main(int argc, char *argv[]) {
1398 DBusConnection *bus = NULL;
1400 setlocale(LC_ALL, "");
1401 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1402 log_parse_environment();
1405 r = parse_argv(argc, argv);
1409 bus = dbus_bus_get(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, NULL);
1415 if (!argv[optind] || streq(argv[optind], "time"))
1416 r = analyze_time(bus);
1417 else if (streq(argv[optind], "blame"))
1418 r = analyze_blame(bus);
1419 else if (streq(argv[optind], "critical-chain"))
1420 r = analyze_critical_chain(bus, argv+optind+1);
1421 else if (streq(argv[optind], "plot"))
1422 r = analyze_plot(bus);
1423 else if (streq(argv[optind], "dot"))
1424 r = dot(bus, argv+optind+1);
1425 else if (streq(argv[optind], "dump"))
1426 r = dump(bus, argv+optind+1);
1427 else if (streq(argv[optind], "set-log-level"))
1428 r = set_log_level(bus, argv+optind+1);
1430 log_error("Unknown operation '%s'.", argv[optind]);
1432 dbus_connection_unref(bus);
1437 strv_free(arg_dot_from_patterns);
1438 strv_free(arg_dot_to_patterns);
1440 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;