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 "bus-error.h"
40 #include "unit-name.h"
45 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
48 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
50 #define svg(...) printf(__VA_ARGS__)
52 #define svg_bar(class, x1, x2, y) \
53 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
55 SCALE_X * (x1), SCALE_Y * (y), \
56 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
58 #define svg_text(b, x, y, format, ...) \
60 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
61 svg(format, ## __VA_ARGS__); \
65 static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
71 static char** arg_dot_from_patterns = NULL;
72 static char** arg_dot_to_patterns = NULL;
73 static usec_t arg_fuzz = 0;
74 static bool arg_no_pager = false;
80 usec_t kernel_done_time;
82 usec_t userspace_time;
84 usec_t generators_start_time;
85 usec_t generators_finish_time;
86 usec_t unitsload_start_time;
87 usec_t unitsload_finish_time;
92 const char *description;
93 const char *load_state;
94 const char *active_state;
95 const char *sub_state;
96 const char *following;
97 const char *unit_path;
100 const char *job_path;
112 static void pager_open_if_enabled(void) {
120 static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
121 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
122 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
125 r = sd_bus_get_property(bus,
126 "org.freedesktop.systemd1",
135 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
139 r = sd_bus_message_read(reply, "t", val);
141 log_error("Failed to parse reply.");
148 static int compare_unit_time(const void *a, const void *b) {
149 return compare(((struct unit_times *)b)->time,
150 ((struct unit_times *)a)->time);
153 static int compare_unit_start(const void *a, const void *b) {
154 return compare(((struct unit_times *)a)->ixt,
155 ((struct unit_times *)b)->ixt);
158 static int get_os_name(char **_n) {
162 r = parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &n, NULL);
173 static void free_unit_times(struct unit_times *t, unsigned n) {
174 struct unit_times *p;
176 for (p = t; p < t + n; p++)
182 static int bus_parse_unit_info(sd_bus_message *message, struct unit_info *u) {
188 r = sd_bus_message_read(message, "(ssssssouso)", &u->id,
199 log_error("Failed to parse message as unit_info.");
206 static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
207 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
208 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
209 int r, c = 0, n_units = 0;
210 struct unit_times *unit_times = NULL;
213 r = sd_bus_call_method(bus,
214 "org.freedesktop.systemd1",
215 "/org/freedesktop/systemd1",
216 "org.freedesktop.systemd1.Manager",
222 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
226 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
230 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
231 struct unit_times *t;
237 struct unit_times *w;
239 n_units = MAX(2*c, 16);
240 w = realloc(unit_times, sizeof(struct unit_times) * n_units);
252 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
254 if (bus_get_uint64_property(bus, u.unit_path,
255 "org.freedesktop.systemd1.Unit",
256 "InactiveExitTimestampMonotonic",
258 bus_get_uint64_property(bus, u.unit_path,
259 "org.freedesktop.systemd1.Unit",
260 "ActiveEnterTimestampMonotonic",
262 bus_get_uint64_property(bus, u.unit_path,
263 "org.freedesktop.systemd1.Unit",
264 "ActiveExitTimestampMonotonic",
266 bus_get_uint64_property(bus, u.unit_path,
267 "org.freedesktop.systemd1.Unit",
268 "InactiveEnterTimestampMonotonic",
274 if (t->aet >= t->ixt)
275 t->time = t->aet - t->ixt;
276 else if (t->iet >= t->ixt)
277 t->time = t->iet - t->ixt;
284 t->name = strdup(u.id);
285 if (t->name == NULL) {
296 free_unit_times(unit_times, (unsigned) c);
300 static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
301 static struct boot_times times;
302 static bool cached = false;
307 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
309 if (bus_get_uint64_property(bus,
310 "/org/freedesktop/systemd1",
311 "org.freedesktop.systemd1.Manager",
312 "FirmwareTimestampMonotonic",
313 ×.firmware_time) < 0 ||
314 bus_get_uint64_property(bus,
315 "/org/freedesktop/systemd1",
316 "org.freedesktop.systemd1.Manager",
317 "LoaderTimestampMonotonic",
318 ×.loader_time) < 0 ||
319 bus_get_uint64_property(bus,
320 "/org/freedesktop/systemd1",
321 "org.freedesktop.systemd1.Manager",
323 ×.kernel_time) < 0 ||
324 bus_get_uint64_property(bus,
325 "/org/freedesktop/systemd1",
326 "org.freedesktop.systemd1.Manager",
327 "InitRDTimestampMonotonic",
328 ×.initrd_time) < 0 ||
329 bus_get_uint64_property(bus,
330 "/org/freedesktop/systemd1",
331 "org.freedesktop.systemd1.Manager",
332 "UserspaceTimestampMonotonic",
333 ×.userspace_time) < 0 ||
334 bus_get_uint64_property(bus,
335 "/org/freedesktop/systemd1",
336 "org.freedesktop.systemd1.Manager",
337 "FinishTimestampMonotonic",
338 ×.finish_time) < 0 ||
339 bus_get_uint64_property(bus,
340 "/org/freedesktop/systemd1",
341 "org.freedesktop.systemd1.Manager",
342 "GeneratorsStartTimestampMonotonic",
343 ×.generators_start_time) < 0 ||
344 bus_get_uint64_property(bus,
345 "/org/freedesktop/systemd1",
346 "org.freedesktop.systemd1.Manager",
347 "GeneratorsFinishTimestampMonotonic",
348 ×.generators_finish_time) < 0 ||
349 bus_get_uint64_property(bus,
350 "/org/freedesktop/systemd1",
351 "org.freedesktop.systemd1.Manager",
352 "UnitsLoadStartTimestampMonotonic",
353 ×.unitsload_start_time) < 0 ||
354 bus_get_uint64_property(bus,
355 "/org/freedesktop/systemd1",
356 "org.freedesktop.systemd1.Manager",
357 "UnitsLoadFinishTimestampMonotonic",
358 ×.unitsload_finish_time) < 0)
361 if (times.finish_time <= 0) {
362 log_error("Bootup is not yet finished. Please try again later.");
366 if (times.initrd_time)
367 times.kernel_done_time = times.initrd_time;
369 times.kernel_done_time = times.userspace_time;
378 static int pretty_boot_time(sd_bus *bus, char **_buf) {
379 char ts[FORMAT_TIMESPAN_MAX];
380 struct boot_times *t;
381 static char buf[4096];
386 r = acquire_boot_times(bus, &t);
393 size = strpcpyf(&ptr, size, "Startup finished in ");
394 if (t->firmware_time)
395 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
397 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
399 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
400 if (t->initrd_time > 0)
401 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
403 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
404 if (t->kernel_time > 0)
405 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
407 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
417 static void svg_graph_box(double height, double begin, double end) {
420 /* outside box, fill */
421 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
422 SCALE_X * (end - begin), SCALE_Y * height);
424 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
425 /* lines for each second */
426 if (i % 5000000 == 0)
427 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
428 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
429 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
430 else if (i % 1000000 == 0)
431 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
432 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
433 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
435 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
436 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
440 static int analyze_plot(sd_bus *bus) {
441 struct unit_times *times;
442 struct boot_times *boot;
446 _cleanup_free_ char *pretty_times = NULL, *osname = NULL;
447 struct unit_times *u;
449 n = acquire_boot_times(bus, &boot);
453 n = pretty_boot_time(bus, &pretty_times);
457 get_os_name(&osname);
458 assert_se(uname(&name) >= 0);
460 n = acquire_time_data(bus, ×);
464 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
466 width = SCALE_X * (boot->firmware_time + boot->finish_time);
470 if (boot->firmware_time > boot->loader_time)
472 if (boot->loader_time) {
477 if (boot->initrd_time)
479 if (boot->kernel_time)
482 for (u = times; u < times + n; u++) {
485 if (u->ixt < boot->userspace_time ||
486 u->ixt > boot->finish_time) {
491 len = ((boot->firmware_time + u->ixt) * SCALE_X)
492 + (10.0 * strlen(u->name));
496 if (u->iet > u->ixt && u->iet <= boot->finish_time
497 && u->aet == 0 && u->axt == 0)
498 u->aet = u->axt = u->iet;
499 if (u->aet < u->ixt || u->aet > boot->finish_time)
500 u->aet = boot->finish_time;
501 if (u->axt < u->aet || u->aet > boot->finish_time)
502 u->axt = boot->finish_time;
503 if (u->iet < u->axt || u->iet > boot->finish_time)
504 u->iet = boot->finish_time;
508 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
509 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
510 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
512 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
513 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
514 80.0 + width, 150.0 + (m * SCALE_Y) +
515 5 * SCALE_Y /* legend */);
517 /* write some basic info as a comment, including some help */
518 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
519 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
520 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
521 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
522 "<!-- point your browser to this file. -->\n\n"
523 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
526 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
527 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
528 " rect.background { fill: rgb(255,255,255); }\n"
529 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
530 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
531 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
532 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
533 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
534 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
535 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
536 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
537 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
538 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
539 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
540 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
542 " line.sec5 { stroke-width: 2; }\n"
543 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
544 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
545 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
546 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
547 " text.sec { font-size: 10px; }\n"
548 " ]]>\n </style>\n</defs>\n\n");
550 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
551 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
552 svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
553 isempty(osname) ? "Linux" : osname,
554 name.nodename, name.release, name.version, name.machine);
556 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
557 svg_graph_box(m, -boot->firmware_time, boot->finish_time);
559 if (boot->firmware_time) {
560 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
561 svg_text(true, -(double) boot->firmware_time, y, "firmware");
564 if (boot->loader_time) {
565 svg_bar("loader", -(double) boot->loader_time, 0, y);
566 svg_text(true, -(double) boot->loader_time, y, "loader");
569 if (boot->kernel_time) {
570 svg_bar("kernel", 0, boot->kernel_done_time, y);
571 svg_text(true, 0, y, "kernel");
574 if (boot->initrd_time) {
575 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
576 svg_text(true, boot->initrd_time, y, "initrd");
579 svg_bar("active", boot->userspace_time, boot->finish_time, y);
580 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
581 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
582 svg_text("left", boot->userspace_time, y, "systemd");
585 for (u = times; u < times + n; u++) {
586 char ts[FORMAT_TIMESPAN_MAX];
592 svg_bar("activating", u->ixt, u->aet, y);
593 svg_bar("active", u->aet, u->axt, y);
594 svg_bar("deactivating", u->axt, u->iet, y);
596 b = u->ixt * SCALE_X > width * 2 / 3;
598 svg_text(b, u->ixt, y, "%s (%s)",
599 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
601 svg_text(b, u->ixt, y, "%s", u->name);
607 svg_bar("activating", 0, 300000, y);
608 svg_text("right", 400000, y, "Activating");
610 svg_bar("active", 0, 300000, y);
611 svg_text("right", 400000, y, "Active");
613 svg_bar("deactivating", 0, 300000, y);
614 svg_text("right", 400000, y, "Deactivating");
616 svg_bar("generators", 0, 300000, y);
617 svg_text("right", 400000, y, "Generators");
619 svg_bar("unitsload", 0, 300000, y);
620 svg_text("right", 400000, y, "Loading unit files");
627 free_unit_times(times, (unsigned) n);
632 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
633 bool last, struct unit_times *times, struct boot_times *boot) {
635 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
637 for (i = level; i != 0; i--)
638 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));
640 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
644 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
645 format_timespan(ts, sizeof(ts), times->ixt - boot->userspace_time, USEC_PER_MSEC),
646 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
647 else if (times->aet > boot->userspace_time)
648 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->aet - boot->userspace_time, USEC_PER_MSEC));
651 } else printf("%s", name);
657 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
658 _cleanup_free_ char *path;
659 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
660 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
670 path = unit_dbus_path_from_name(name);
676 r = sd_bus_get_property(bus,
677 "org.freedesktop.systemd1",
679 "org.freedesktop.systemd1.Unit",
685 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
689 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s");
693 while ((r = sd_bus_message_read(reply, "s", &s)) > 0) {
694 r = strv_extend(&ret, s);
708 static Hashmap *unit_times_hashmap;
710 static int list_dependencies_compare(const void *_a, const void *_b) {
711 const char **a = (const char**) _a, **b = (const char**) _b;
712 usec_t usa = 0, usb = 0;
713 struct unit_times *times;
715 times = hashmap_get(unit_times_hashmap, *a);
718 times = hashmap_get(unit_times_hashmap, *b);
725 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
726 unsigned int branches) {
727 _cleanup_strv_free_ char **deps = NULL;
730 usec_t service_longest = 0;
732 struct unit_times *times;
733 struct boot_times *boot;
735 if(strv_extend(units, name))
738 r = list_dependencies_get_dependencies(bus, name, &deps);
742 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
744 r = acquire_boot_times(bus, &boot);
748 STRV_FOREACH(c, deps) {
749 times = hashmap_get(unit_times_hashmap, *c);
752 && times->aet <= boot->finish_time
753 && (times->aet >= service_longest
754 || service_longest == 0)) {
755 service_longest = times->aet;
760 if (service_longest == 0 )
763 STRV_FOREACH(c, deps) {
764 times = hashmap_get(unit_times_hashmap, *c);
765 if (times && times->aet
766 && times->aet <= boot->finish_time
767 && (service_longest - times->aet) <= arg_fuzz) {
775 STRV_FOREACH(c, deps) {
776 times = hashmap_get(unit_times_hashmap, *c);
779 || times->aet > boot->finish_time
780 || service_longest - times->aet > arg_fuzz)
785 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
789 if (strv_contains(*units, *c)) {
790 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
797 r = list_dependencies_one(bus, *c, level + 1, units,
798 (branches << 1) | (to_print ? 1 : 0));
808 static int list_dependencies(sd_bus *bus, const char *name) {
809 _cleanup_strv_free_ char **units = NULL;
810 char ts[FORMAT_TIMESPAN_MAX];
811 struct unit_times *times;
813 const char *path, *id;
814 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
815 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
816 struct boot_times *boot;
820 path = unit_dbus_path_from_name(name);
824 r = sd_bus_get_property(bus,
825 "org.freedesktop.systemd1",
827 "org.freedesktop.systemd1.Unit",
833 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
837 r = sd_bus_message_read(reply, "s", &id);
839 log_error("Failed to parse reply.");
843 times = hashmap_get(unit_times_hashmap, id);
845 r = acquire_boot_times(bus, &boot);
851 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
852 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
853 else if (times->aet > boot->userspace_time)
854 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->aet - boot->userspace_time, USEC_PER_MSEC));
859 return list_dependencies_one(bus, name, 0, &units, 0);
862 static int analyze_critical_chain(sd_bus *bus, char *names[]) {
863 struct unit_times *times;
868 n = acquire_time_data(bus, ×);
872 h = hashmap_new(string_hash_func, string_compare_func);
876 for (i = 0; i < (unsigned)n; i++) {
877 r = hashmap_put(h, times[i].name, ×[i]);
881 unit_times_hashmap = h;
883 pager_open_if_enabled();
885 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
886 "The time the unit takes to start is printed after the \"+\" character.\n");
888 if (!strv_isempty(names)) {
890 STRV_FOREACH(name, names)
891 list_dependencies(bus, *name);
893 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
896 free_unit_times(times, (unsigned) n);
900 static int analyze_blame(sd_bus *bus) {
901 struct unit_times *times;
905 n = acquire_time_data(bus, ×);
909 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
911 pager_open_if_enabled();
913 for (i = 0; i < (unsigned) n; i++) {
914 char ts[FORMAT_TIMESPAN_MAX];
916 if (times[i].time > 0)
917 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
920 free_unit_times(times, (unsigned) n);
924 static int analyze_time(sd_bus *bus) {
925 _cleanup_free_ char *buf = NULL;
928 r = pretty_boot_time(bus, &buf);
936 static int graph_one_property(sd_bus *bus, const struct unit_info *u, const char* prop, const char *color, char* patterns[]) {
937 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
938 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
939 _cleanup_strv_free_ char **units = NULL;
948 r = sd_bus_get_property(bus,
949 "org.freedesktop.systemd1",
951 "org.freedesktop.systemd1.Unit",
957 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
961 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s");
965 while ((r = sd_bus_message_read(reply, "s", &s)) > 0) {
966 r = strv_extend(&units, s);
973 STRV_FOREACH(unit, units) {
977 if (!strv_isempty(arg_dot_from_patterns)) {
980 STRV_FOREACH(p, arg_dot_from_patterns)
981 if (fnmatch(*p, u->id, 0) == 0) {
990 if (!strv_isempty(arg_dot_to_patterns)) {
993 STRV_FOREACH(p, arg_dot_to_patterns)
994 if (fnmatch(*p, *unit, 0) == 0) {
1003 if (!strv_isempty(patterns)) {
1004 match_found = false;
1006 STRV_FOREACH(p, patterns)
1007 if (fnmatch(*p, u->id, 0) == 0 || fnmatch(*p, *unit, 0) == 0) {
1015 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1021 static int graph_one(sd_bus *bus, const struct unit_info *u, char *patterns[]) {
1027 if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
1028 r = graph_one_property(bus, u, "After", "green", patterns);
1033 if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
1034 r = graph_one_property(bus, u, "Requires", "black", patterns);
1037 r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns);
1040 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns);
1043 r = graph_one_property(bus, u, "Wants", "grey66", patterns);
1046 r = graph_one_property(bus, u, "Conflicts", "red", patterns);
1049 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns);
1057 static int dot(sd_bus *bus, char* patterns[]) {
1058 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1059 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1063 r = sd_bus_call_method(bus,
1064 "org.freedesktop.systemd1",
1065 "/org/freedesktop/systemd1",
1066 "org.freedesktop.systemd1.Manager",
1072 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
1076 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1080 printf("digraph systemd {\n");
1082 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1083 r = graph_one(bus, &u, patterns);
1090 log_info(" Color legend: black = Requires\n"
1091 " dark blue = Requisite\n"
1092 " dark grey = Wants\n"
1093 " red = Conflicts\n"
1094 " green = After\n");
1097 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1098 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1102 static int dump(sd_bus *bus, char **args) {
1103 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1104 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1105 const char *text = NULL;
1108 if (!strv_isempty(args)) {
1109 log_error("Too many arguments.");
1113 pager_open_if_enabled();
1115 r = sd_bus_call_method(bus,
1116 "org.freedesktop.systemd1",
1117 "/org/freedesktop/systemd1",
1118 "org.freedesktop.systemd1.Manager",
1124 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
1128 r = sd_bus_message_read(reply, "s", &text);
1130 log_error("Failed to parse reply");
1134 fputs(text, stdout);
1138 static int set_log_level(sd_bus *bus, char **args) {
1139 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1146 if (strv_length(args) != 1) {
1147 log_error("This command expects one argument only.");
1153 r = sd_bus_set_property(bus,
1154 "org.freedesktop.systemd1",
1155 "/org/freedesktop/systemd1",
1156 "org.freedesktop.systemd1.Manager",
1162 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1169 static void analyze_help(void) {
1171 pager_open_if_enabled();
1173 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1174 "Process systemd profiling information\n\n"
1175 " -h --help Show this help\n"
1176 " --version Show package version\n"
1177 " --system Connect to system manager\n"
1178 " --user Connect to user service manager\n"
1179 " --order When generating a dependency graph, show only order\n"
1180 " --require When generating a dependency graph, show only requirement\n"
1181 " --from-pattern=GLOB, --to-pattern=GLOB\n"
1182 " When generating a dependency graph, filter only origins\n"
1183 " or destinations, respectively\n"
1184 " --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
1185 " services, which finished TIMESPAN earlier, than the\n"
1186 " latest in the branch. The unit of TIMESPAN is seconds\n"
1187 " unless specified with a different unit, i.e. 50ms\n"
1188 " --no-pager Do not pipe output into a pager\n\n"
1190 " time Print time spent in the kernel before reaching userspace\n"
1191 " blame Print list of running units ordered by time to init\n"
1192 " critical-chain Print a tree of the time critical chain of units\n"
1193 " plot Output SVG graphic showing service initialization\n"
1194 " dot Output dependency graph in dot(1) format\n"
1195 " set-log-level LEVEL Set logging threshold for systemd\n"
1196 " dump Output state serialization of service manager\n",
1197 program_invocation_short_name);
1199 /* When updating this list, including descriptions, apply
1200 * changes to shell-completion/bash/systemd and
1201 * shell-completion/systemd-zsh-completion.zsh too. */
1204 static int parse_argv(int argc, char *argv[]) {
1208 ARG_VERSION = 0x100,
1213 ARG_DOT_FROM_PATTERN,
1219 static const struct option options[] = {
1220 { "help", no_argument, NULL, 'h' },
1221 { "version", no_argument, NULL, ARG_VERSION },
1222 { "order", no_argument, NULL, ARG_ORDER },
1223 { "require", no_argument, NULL, ARG_REQUIRE },
1224 { "user", no_argument, NULL, ARG_USER },
1225 { "system", no_argument, NULL, ARG_SYSTEM },
1226 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1227 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1228 { "fuzz", required_argument, NULL, ARG_FUZZ },
1229 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1230 { NULL, 0, NULL, 0 }
1237 switch (getopt_long(argc, argv, "h", options, NULL)) {
1244 puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES);
1248 arg_scope = UNIT_FILE_USER;
1252 arg_scope = UNIT_FILE_SYSTEM;
1256 arg_dot = DEP_ORDER;
1260 arg_dot = DEP_REQUIRE;
1263 case ARG_DOT_FROM_PATTERN:
1264 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1269 case ARG_DOT_TO_PATTERN:
1270 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1276 r = parse_sec(optarg, &arg_fuzz);
1282 arg_no_pager = true;
1292 assert_not_reached("Unhandled option");
1297 int main(int argc, char *argv[]) {
1299 _cleanup_bus_unref_ sd_bus *bus = NULL;
1301 setlocale(LC_ALL, "");
1302 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1303 log_parse_environment();
1306 r = parse_argv(argc, argv);
1310 if (arg_scope == UNIT_FILE_SYSTEM)
1311 r = sd_bus_open_system(&bus);
1313 r = sd_bus_open_user(&bus);
1316 log_error("Failed to connect to bus: %s", strerror(-r));
1320 if (!argv[optind] || streq(argv[optind], "time"))
1321 r = analyze_time(bus);
1322 else if (streq(argv[optind], "blame"))
1323 r = analyze_blame(bus);
1324 else if (streq(argv[optind], "critical-chain"))
1325 r = analyze_critical_chain(bus, argv+optind+1);
1326 else if (streq(argv[optind], "plot"))
1327 r = analyze_plot(bus);
1328 else if (streq(argv[optind], "dot"))
1329 r = dot(bus, argv+optind+1);
1330 else if (streq(argv[optind], "dump"))
1331 r = dump(bus, argv+optind+1);
1332 else if (streq(argv[optind], "set-log-level"))
1333 r = set_log_level(bus, argv+optind+1);
1335 log_error("Unknown operation '%s'.", argv[optind]);
1340 strv_free(arg_dot_from_patterns);
1341 strv_free(arg_dot_to_patterns);
1343 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;