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__); \
70 static char** arg_dot_from_patterns = NULL;
71 static char** arg_dot_to_patterns = NULL;
72 static usec_t arg_fuzz = 0;
73 static bool arg_no_pager = false;
74 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
75 static char *arg_host = NULL;
76 static bool arg_user = false;
82 usec_t kernel_done_time;
84 usec_t userspace_time;
86 usec_t generators_start_time;
87 usec_t generators_finish_time;
88 usec_t unitsload_start_time;
89 usec_t unitsload_finish_time;
101 static void pager_open_if_enabled(void) {
109 static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
110 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
119 r = sd_bus_get_property_trivial(
121 "org.freedesktop.systemd1",
129 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
136 static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
137 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
145 r = sd_bus_get_property_strv(
147 "org.freedesktop.systemd1",
149 "org.freedesktop.systemd1.Unit",
154 log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
161 static int compare_unit_time(const void *a, const void *b) {
162 return compare(((struct unit_times *)b)->time,
163 ((struct unit_times *)a)->time);
166 static int compare_unit_start(const void *a, const void *b) {
167 return compare(((struct unit_times *)a)->activating,
168 ((struct unit_times *)b)->activating);
171 static int get_os_name(char **_n) {
175 r = parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &n, NULL);
186 static void free_unit_times(struct unit_times *t, unsigned n) {
187 struct unit_times *p;
189 for (p = t; p < t + n; p++)
195 static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
196 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
197 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
199 struct unit_times *unit_times = NULL;
203 r = sd_bus_call_method(
205 "org.freedesktop.systemd1",
206 "/org/freedesktop/systemd1",
207 "org.freedesktop.systemd1.Manager",
212 log_error("Failed to list units: %s", bus_error_message(&error, -r));
216 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
218 bus_log_parse_error(r);
222 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
223 struct unit_times *t;
225 if (!GREEDY_REALLOC(unit_times, size, c+1)) {
233 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
235 if (bus_get_uint64_property(bus, u.unit_path,
236 "org.freedesktop.systemd1.Unit",
237 "InactiveExitTimestampMonotonic",
238 &t->activating) < 0 ||
239 bus_get_uint64_property(bus, u.unit_path,
240 "org.freedesktop.systemd1.Unit",
241 "ActiveEnterTimestampMonotonic",
242 &t->activated) < 0 ||
243 bus_get_uint64_property(bus, u.unit_path,
244 "org.freedesktop.systemd1.Unit",
245 "ActiveExitTimestampMonotonic",
246 &t->deactivating) < 0 ||
247 bus_get_uint64_property(bus, u.unit_path,
248 "org.freedesktop.systemd1.Unit",
249 "InactiveEnterTimestampMonotonic",
250 &t->deactivated) < 0) {
255 if (t->activated >= t->activating)
256 t->time = t->activated - t->activating;
257 else if (t->deactivated >= t->activating)
258 t->time = t->deactivated - t->activating;
262 if (t->activating == 0)
265 t->name = strdup(u.id);
266 if (t->name == NULL) {
273 bus_log_parse_error(r);
281 free_unit_times(unit_times, (unsigned) c);
285 static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
286 static struct boot_times times;
287 static bool cached = false;
292 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
294 if (bus_get_uint64_property(bus,
295 "/org/freedesktop/systemd1",
296 "org.freedesktop.systemd1.Manager",
297 "FirmwareTimestampMonotonic",
298 ×.firmware_time) < 0 ||
299 bus_get_uint64_property(bus,
300 "/org/freedesktop/systemd1",
301 "org.freedesktop.systemd1.Manager",
302 "LoaderTimestampMonotonic",
303 ×.loader_time) < 0 ||
304 bus_get_uint64_property(bus,
305 "/org/freedesktop/systemd1",
306 "org.freedesktop.systemd1.Manager",
308 ×.kernel_time) < 0 ||
309 bus_get_uint64_property(bus,
310 "/org/freedesktop/systemd1",
311 "org.freedesktop.systemd1.Manager",
312 "InitRDTimestampMonotonic",
313 ×.initrd_time) < 0 ||
314 bus_get_uint64_property(bus,
315 "/org/freedesktop/systemd1",
316 "org.freedesktop.systemd1.Manager",
317 "UserspaceTimestampMonotonic",
318 ×.userspace_time) < 0 ||
319 bus_get_uint64_property(bus,
320 "/org/freedesktop/systemd1",
321 "org.freedesktop.systemd1.Manager",
322 "FinishTimestampMonotonic",
323 ×.finish_time) < 0 ||
324 bus_get_uint64_property(bus,
325 "/org/freedesktop/systemd1",
326 "org.freedesktop.systemd1.Manager",
327 "GeneratorsStartTimestampMonotonic",
328 ×.generators_start_time) < 0 ||
329 bus_get_uint64_property(bus,
330 "/org/freedesktop/systemd1",
331 "org.freedesktop.systemd1.Manager",
332 "GeneratorsFinishTimestampMonotonic",
333 ×.generators_finish_time) < 0 ||
334 bus_get_uint64_property(bus,
335 "/org/freedesktop/systemd1",
336 "org.freedesktop.systemd1.Manager",
337 "UnitsLoadStartTimestampMonotonic",
338 ×.unitsload_start_time) < 0 ||
339 bus_get_uint64_property(bus,
340 "/org/freedesktop/systemd1",
341 "org.freedesktop.systemd1.Manager",
342 "UnitsLoadFinishTimestampMonotonic",
343 ×.unitsload_finish_time) < 0)
346 if (times.finish_time <= 0) {
347 log_error("Bootup is not yet finished. Please try again later.");
351 if (times.initrd_time)
352 times.kernel_done_time = times.initrd_time;
354 times.kernel_done_time = times.userspace_time;
363 static int pretty_boot_time(sd_bus *bus, char **_buf) {
364 char ts[FORMAT_TIMESPAN_MAX];
365 struct boot_times *t;
366 static char buf[4096];
371 r = acquire_boot_times(bus, &t);
378 size = strpcpyf(&ptr, size, "Startup finished in ");
379 if (t->firmware_time)
380 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
382 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
384 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
385 if (t->initrd_time > 0)
386 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
388 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
389 if (t->kernel_time > 0)
390 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
392 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
402 static void svg_graph_box(double height, double begin, double end) {
405 /* outside box, fill */
406 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
407 SCALE_X * (end - begin), SCALE_Y * height);
409 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
410 /* lines for each second */
411 if (i % 5000000 == 0)
412 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
413 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
414 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
415 else if (i % 1000000 == 0)
416 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
417 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
418 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
420 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
421 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
425 static int analyze_plot(sd_bus *bus) {
426 struct unit_times *times;
427 struct boot_times *boot;
431 _cleanup_free_ char *pretty_times = NULL, *osname = NULL;
432 struct unit_times *u;
434 n = acquire_boot_times(bus, &boot);
438 n = pretty_boot_time(bus, &pretty_times);
442 get_os_name(&osname);
443 assert_se(uname(&name) >= 0);
445 n = acquire_time_data(bus, ×);
449 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
451 width = SCALE_X * (boot->firmware_time + boot->finish_time);
455 if (boot->firmware_time > boot->loader_time)
457 if (boot->loader_time) {
462 if (boot->initrd_time)
464 if (boot->kernel_time)
467 for (u = times; u < times + n; u++) {
468 double text_start, text_width;
470 if (u->activating < boot->userspace_time ||
471 u->activating > boot->finish_time) {
477 /* If the text cannot fit on the left side then
478 * increase the svg width so it fits on the right.
479 * TODO: calculate the text width more accurately */
480 text_width = 8.0 * strlen(u->name);
481 text_start = (boot->firmware_time + u->activating) * SCALE_X;
482 if (text_width > text_start && text_width + text_start > width)
483 width = text_width + text_start;
485 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
486 && u->activated == 0 && u->deactivating == 0)
487 u->activated = u->deactivating = u->deactivated;
488 if (u->activated < u->activating || u->activated > boot->finish_time)
489 u->activated = boot->finish_time;
490 if (u->deactivating < u->activated || u->activated > boot->finish_time)
491 u->deactivating = boot->finish_time;
492 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
493 u->deactivated = boot->finish_time;
497 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
498 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
499 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
501 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
502 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
503 80.0 + width, 150.0 + (m * SCALE_Y) +
504 5 * SCALE_Y /* legend */);
506 /* write some basic info as a comment, including some help */
507 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
508 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
509 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
510 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
511 "<!-- point your browser to this file. -->\n\n"
512 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
515 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
516 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
517 " rect.background { fill: rgb(255,255,255); }\n"
518 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
519 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
520 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
521 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
522 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
523 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
524 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
525 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
526 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
527 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
528 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
529 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
531 " line.sec5 { stroke-width: 2; }\n"
532 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
533 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
534 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
535 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
536 " text.sec { font-size: 10px; }\n"
537 " ]]>\n </style>\n</defs>\n\n");
539 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
540 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
541 svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
542 isempty(osname) ? "Linux" : osname,
543 name.nodename, name.release, name.version, name.machine);
545 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
546 svg_graph_box(m, -boot->firmware_time, boot->finish_time);
548 if (boot->firmware_time) {
549 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
550 svg_text(true, -(double) boot->firmware_time, y, "firmware");
553 if (boot->loader_time) {
554 svg_bar("loader", -(double) boot->loader_time, 0, y);
555 svg_text(true, -(double) boot->loader_time, y, "loader");
558 if (boot->kernel_time) {
559 svg_bar("kernel", 0, boot->kernel_done_time, y);
560 svg_text(true, 0, y, "kernel");
563 if (boot->initrd_time) {
564 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
565 svg_text(true, boot->initrd_time, y, "initrd");
568 svg_bar("active", boot->userspace_time, boot->finish_time, y);
569 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
570 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
571 svg_text(true, boot->userspace_time, y, "systemd");
574 for (u = times; u < times + n; u++) {
575 char ts[FORMAT_TIMESPAN_MAX];
581 svg_bar("activating", u->activating, u->activated, y);
582 svg_bar("active", u->activated, u->deactivating, y);
583 svg_bar("deactivating", u->deactivating, u->deactivated, y);
585 /* place the text on the left if we have passed the half of the svg width */
586 b = u->activating * SCALE_X < width / 2;
588 svg_text(b, u->activating, y, "%s (%s)",
589 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
591 svg_text(b, u->activating, y, "%s", u->name);
597 svg_bar("activating", 0, 300000, y);
598 svg_text(true, 400000, y, "Activating");
600 svg_bar("active", 0, 300000, y);
601 svg_text(true, 400000, y, "Active");
603 svg_bar("deactivating", 0, 300000, y);
604 svg_text(true, 400000, y, "Deactivating");
606 svg_bar("generators", 0, 300000, y);
607 svg_text(true, 400000, y, "Generators");
609 svg_bar("unitsload", 0, 300000, y);
610 svg_text(true, 400000, y, "Loading unit files");
617 free_unit_times(times, (unsigned) n);
622 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
623 bool last, struct unit_times *times, struct boot_times *boot) {
625 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
627 for (i = level; i != 0; i--)
628 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));
630 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
634 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
635 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
636 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
637 else if (times->activated > boot->userspace_time)
638 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
648 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
649 _cleanup_free_ char *path = NULL;
655 path = unit_dbus_path_from_name(name);
659 return bus_get_unit_property_strv(bus, path, "After", deps);
662 static Hashmap *unit_times_hashmap;
664 static int list_dependencies_compare(const void *_a, const void *_b) {
665 const char **a = (const char**) _a, **b = (const char**) _b;
666 usec_t usa = 0, usb = 0;
667 struct unit_times *times;
669 times = hashmap_get(unit_times_hashmap, *a);
671 usa = times->activated;
672 times = hashmap_get(unit_times_hashmap, *b);
674 usb = times->activated;
679 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
680 unsigned int branches) {
681 _cleanup_strv_free_ char **deps = NULL;
684 usec_t service_longest = 0;
686 struct unit_times *times;
687 struct boot_times *boot;
689 if (strv_extend(units, name))
692 r = list_dependencies_get_dependencies(bus, name, &deps);
696 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
698 r = acquire_boot_times(bus, &boot);
702 STRV_FOREACH(c, deps) {
703 times = hashmap_get(unit_times_hashmap, *c);
706 && times->activated <= boot->finish_time
707 && (times->activated >= service_longest
708 || service_longest == 0)) {
709 service_longest = times->activated;
714 if (service_longest == 0 )
717 STRV_FOREACH(c, deps) {
718 times = hashmap_get(unit_times_hashmap, *c);
719 if (times && times->activated
720 && times->activated <= boot->finish_time
721 && (service_longest - times->activated) <= arg_fuzz) {
729 STRV_FOREACH(c, deps) {
730 times = hashmap_get(unit_times_hashmap, *c);
733 || times->activated > boot->finish_time
734 || service_longest - times->activated > arg_fuzz)
739 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
743 if (strv_contains(*units, *c)) {
744 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
751 r = list_dependencies_one(bus, *c, level + 1, units,
752 (branches << 1) | (to_print ? 1 : 0));
762 static int list_dependencies(sd_bus *bus, const char *name) {
763 _cleanup_strv_free_ char **units = NULL;
764 char ts[FORMAT_TIMESPAN_MAX];
765 struct unit_times *times;
767 const char *path, *id;
768 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
769 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
770 struct boot_times *boot;
774 path = unit_dbus_path_from_name(name);
778 r = sd_bus_get_property(
780 "org.freedesktop.systemd1",
782 "org.freedesktop.systemd1.Unit",
788 log_error("Failed to get ID: %s", bus_error_message(&error, -r));
792 r = sd_bus_message_read(reply, "s", &id);
794 return bus_log_parse_error(r);
796 times = hashmap_get(unit_times_hashmap, id);
798 r = acquire_boot_times(bus, &boot);
804 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
805 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
806 else if (times->activated > boot->userspace_time)
807 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
812 return list_dependencies_one(bus, name, 0, &units, 0);
815 static int analyze_critical_chain(sd_bus *bus, char *names[]) {
816 struct unit_times *times;
821 n = acquire_time_data(bus, ×);
825 h = hashmap_new(string_hash_func, string_compare_func);
829 for (i = 0; i < (unsigned)n; i++) {
830 r = hashmap_put(h, times[i].name, ×[i]);
834 unit_times_hashmap = h;
836 pager_open_if_enabled();
838 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
839 "The time the unit takes to start is printed after the \"+\" character.\n");
841 if (!strv_isempty(names)) {
843 STRV_FOREACH(name, names)
844 list_dependencies(bus, *name);
846 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
849 free_unit_times(times, (unsigned) n);
853 static int analyze_blame(sd_bus *bus) {
854 struct unit_times *times;
858 n = acquire_time_data(bus, ×);
862 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
864 pager_open_if_enabled();
866 for (i = 0; i < (unsigned) n; i++) {
867 char ts[FORMAT_TIMESPAN_MAX];
869 if (times[i].time > 0)
870 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
873 free_unit_times(times, (unsigned) n);
877 static int analyze_time(sd_bus *bus) {
878 _cleanup_free_ char *buf = NULL;
881 r = pretty_boot_time(bus, &buf);
889 static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[]) {
890 _cleanup_strv_free_ char **units = NULL;
898 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
902 STRV_FOREACH(unit, units) {
906 if (!strv_isempty(arg_dot_from_patterns)) {
909 STRV_FOREACH(p, arg_dot_from_patterns)
910 if (fnmatch(*p, u->id, 0) == 0) {
919 if (!strv_isempty(arg_dot_to_patterns)) {
922 STRV_FOREACH(p, arg_dot_to_patterns)
923 if (fnmatch(*p, *unit, 0) == 0) {
932 if (!strv_isempty(patterns)) {
935 STRV_FOREACH(p, patterns)
936 if (fnmatch(*p, u->id, 0) == 0 || fnmatch(*p, *unit, 0) == 0) {
944 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
950 static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[]) {
956 if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
957 r = graph_one_property(bus, u, "After", "green", patterns);
962 if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
963 r = graph_one_property(bus, u, "Requires", "black", patterns);
966 r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns);
969 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns);
972 r = graph_one_property(bus, u, "Wants", "grey66", patterns);
975 r = graph_one_property(bus, u, "Conflicts", "red", patterns);
978 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns);
986 static int dot(sd_bus *bus, char* patterns[]) {
987 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
988 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
992 r = sd_bus_call_method(
994 "org.freedesktop.systemd1",
995 "/org/freedesktop/systemd1",
996 "org.freedesktop.systemd1.Manager",
1002 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1006 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1008 return bus_log_parse_error(r);
1010 printf("digraph systemd {\n");
1012 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1014 r = graph_one(bus, &u, patterns);
1019 return bus_log_parse_error(r);
1023 log_info(" Color legend: black = Requires\n"
1024 " dark blue = Requisite\n"
1025 " dark grey = Wants\n"
1026 " red = Conflicts\n"
1027 " green = After\n");
1030 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1031 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1036 static int dump(sd_bus *bus, char **args) {
1037 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1038 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1039 const char *text = NULL;
1042 if (!strv_isempty(args)) {
1043 log_error("Too many arguments.");
1047 pager_open_if_enabled();
1049 r = sd_bus_call_method(
1051 "org.freedesktop.systemd1",
1052 "/org/freedesktop/systemd1",
1053 "org.freedesktop.systemd1.Manager",
1059 log_error("Failed issue method call: %s", bus_error_message(&error, -r));
1063 r = sd_bus_message_read(reply, "s", &text);
1065 return bus_log_parse_error(r);
1067 fputs(text, stdout);
1071 static int set_log_level(sd_bus *bus, char **args) {
1072 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1078 if (strv_length(args) != 1) {
1079 log_error("This command expects one argument only.");
1083 r = sd_bus_set_property(
1085 "org.freedesktop.systemd1",
1086 "/org/freedesktop/systemd1",
1087 "org.freedesktop.systemd1.Manager",
1093 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1100 static int help(void) {
1102 pager_open_if_enabled();
1104 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1105 "Process systemd profiling information.\n\n"
1106 " -h --help Show this help\n"
1107 " --version Show package version\n"
1108 " --no-pager Do not pipe output into a pager\n"
1109 " --system Connect to system manager\n"
1110 " --user Connect to user manager\n"
1111 " -H --host=[USER@]HOST Operate on remote host\n"
1112 " -M --machine=CONTAINER Operate on local container\n"
1113 " --order When generating a dependency graph, show only order\n"
1114 " --require When generating a dependency graph, show only requirement\n"
1115 " --from-pattern=GLOB, --to-pattern=GLOB\n"
1116 " When generating a dependency graph, filter only origins\n"
1117 " or destinations, respectively\n"
1118 " --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
1119 " services, which finished TIMESPAN earlier, than the\n"
1120 " latest in the branch. The unit of TIMESPAN is seconds\n"
1121 " unless specified with a different unit, i.e. 50ms\n\n"
1123 " time Print time spent in the kernel before reaching userspace\n"
1124 " blame Print list of running units ordered by time to init\n"
1125 " critical-chain Print a tree of the time critical chain of units\n"
1126 " plot Output SVG graphic showing service initialization\n"
1127 " dot Output dependency graph in dot(1) format\n"
1128 " set-log-level LEVEL Set logging threshold for systemd\n"
1129 " dump Output state serialization of service manager\n",
1130 program_invocation_short_name);
1132 /* When updating this list, including descriptions, apply
1133 * changes to shell-completion/bash/systemd and
1134 * shell-completion/systemd-zsh-completion.zsh too. */
1139 static int parse_argv(int argc, char *argv[]) {
1141 ARG_VERSION = 0x100,
1146 ARG_DOT_FROM_PATTERN,
1152 static const struct option options[] = {
1153 { "help", no_argument, NULL, 'h' },
1154 { "version", no_argument, NULL, ARG_VERSION },
1155 { "order", no_argument, NULL, ARG_ORDER },
1156 { "require", no_argument, NULL, ARG_REQUIRE },
1157 { "user", no_argument, NULL, ARG_USER },
1158 { "system", no_argument, NULL, ARG_SYSTEM },
1159 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1160 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1161 { "fuzz", required_argument, NULL, ARG_FUZZ },
1162 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1163 { "host", required_argument, NULL, 'H' },
1164 { "machine", required_argument, NULL, 'M' },
1173 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
1181 puts(PACKAGE_STRING);
1182 puts(SYSTEMD_FEATURES);
1194 arg_dot = DEP_ORDER;
1198 arg_dot = DEP_REQUIRE;
1201 case ARG_DOT_FROM_PATTERN:
1202 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1207 case ARG_DOT_TO_PATTERN:
1208 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1214 r = parse_sec(optarg, &arg_fuzz);
1220 arg_no_pager = true;
1224 arg_transport = BUS_TRANSPORT_REMOTE;
1229 arg_transport = BUS_TRANSPORT_CONTAINER;
1237 assert_not_reached("Unhandled option");
1244 int main(int argc, char *argv[]) {
1245 _cleanup_bus_unref_ sd_bus *bus = NULL;
1248 setlocale(LC_ALL, "");
1249 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1250 log_parse_environment();
1253 r = parse_argv(argc, argv);
1257 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
1259 log_error("Failed to create bus connection: %s", strerror(-r));
1263 if (!argv[optind] || streq(argv[optind], "time"))
1264 r = analyze_time(bus);
1265 else if (streq(argv[optind], "blame"))
1266 r = analyze_blame(bus);
1267 else if (streq(argv[optind], "critical-chain"))
1268 r = analyze_critical_chain(bus, argv+optind+1);
1269 else if (streq(argv[optind], "plot"))
1270 r = analyze_plot(bus);
1271 else if (streq(argv[optind], "dot"))
1272 r = dot(bus, argv+optind+1);
1273 else if (streq(argv[optind], "dump"))
1274 r = dump(bus, argv+optind+1);
1275 else if (streq(argv[optind], "set-log-level"))
1276 r = set_log_level(bus, argv+optind+1);
1278 log_error("Unknown operation '%s'.", argv[optind]);
1283 strv_free(arg_dot_from_patterns);
1284 strv_free(arg_dot_to_patterns);
1286 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;