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;
94 const char *description;
95 const char *load_state;
96 const char *active_state;
97 const char *sub_state;
98 const char *following;
99 const char *unit_path;
101 const char *job_type;
102 const char *job_path;
114 static void pager_open_if_enabled(void) {
122 static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
123 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
132 r = sd_bus_get_property_trivial(
134 "org.freedesktop.systemd1",
142 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
149 static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
150 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
158 r = sd_bus_get_property_strv(
160 "org.freedesktop.systemd1",
162 "org.freedesktop.systemd1.Unit",
167 log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
174 static int compare_unit_time(const void *a, const void *b) {
175 return compare(((struct unit_times *)b)->time,
176 ((struct unit_times *)a)->time);
179 static int compare_unit_start(const void *a, const void *b) {
180 return compare(((struct unit_times *)a)->activating,
181 ((struct unit_times *)b)->activating);
184 static int get_os_name(char **_n) {
188 r = parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &n, NULL);
199 static void free_unit_times(struct unit_times *t, unsigned n) {
200 struct unit_times *p;
202 for (p = t; p < t + n; p++)
208 static int bus_parse_unit_info(sd_bus_message *message, struct unit_info *u) {
214 r = sd_bus_message_read(message, "(ssssssouso)", &u->id,
225 return bus_log_parse_error(r);
230 static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
231 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
232 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
233 int r, c = 0, n_units = 0;
234 struct unit_times *unit_times = NULL;
237 r = sd_bus_call_method(
239 "org.freedesktop.systemd1",
240 "/org/freedesktop/systemd1",
241 "org.freedesktop.systemd1.Manager",
246 log_error("Failed to list units: %s", bus_error_message(&error, -r));
250 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
252 bus_log_parse_error(r);
256 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
257 struct unit_times *t;
263 struct unit_times *w;
265 n_units = MAX(2*c, 16);
266 w = realloc(unit_times, sizeof(struct unit_times) * n_units);
278 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
280 if (bus_get_uint64_property(bus, u.unit_path,
281 "org.freedesktop.systemd1.Unit",
282 "InactiveExitTimestampMonotonic",
283 &t->activating) < 0 ||
284 bus_get_uint64_property(bus, u.unit_path,
285 "org.freedesktop.systemd1.Unit",
286 "ActiveEnterTimestampMonotonic",
287 &t->activated) < 0 ||
288 bus_get_uint64_property(bus, u.unit_path,
289 "org.freedesktop.systemd1.Unit",
290 "ActiveExitTimestampMonotonic",
291 &t->deactivating) < 0 ||
292 bus_get_uint64_property(bus, u.unit_path,
293 "org.freedesktop.systemd1.Unit",
294 "InactiveEnterTimestampMonotonic",
295 &t->deactivated) < 0) {
300 if (t->activated >= t->activating)
301 t->time = t->activated - t->activating;
302 else if (t->deactivated >= t->activating)
303 t->time = t->deactivated - t->activating;
307 if (t->activating == 0)
310 t->name = strdup(u.id);
311 if (t->name == NULL) {
322 free_unit_times(unit_times, (unsigned) c);
326 static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
327 static struct boot_times times;
328 static bool cached = false;
333 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
335 if (bus_get_uint64_property(bus,
336 "/org/freedesktop/systemd1",
337 "org.freedesktop.systemd1.Manager",
338 "FirmwareTimestampMonotonic",
339 ×.firmware_time) < 0 ||
340 bus_get_uint64_property(bus,
341 "/org/freedesktop/systemd1",
342 "org.freedesktop.systemd1.Manager",
343 "LoaderTimestampMonotonic",
344 ×.loader_time) < 0 ||
345 bus_get_uint64_property(bus,
346 "/org/freedesktop/systemd1",
347 "org.freedesktop.systemd1.Manager",
349 ×.kernel_time) < 0 ||
350 bus_get_uint64_property(bus,
351 "/org/freedesktop/systemd1",
352 "org.freedesktop.systemd1.Manager",
353 "InitRDTimestampMonotonic",
354 ×.initrd_time) < 0 ||
355 bus_get_uint64_property(bus,
356 "/org/freedesktop/systemd1",
357 "org.freedesktop.systemd1.Manager",
358 "UserspaceTimestampMonotonic",
359 ×.userspace_time) < 0 ||
360 bus_get_uint64_property(bus,
361 "/org/freedesktop/systemd1",
362 "org.freedesktop.systemd1.Manager",
363 "FinishTimestampMonotonic",
364 ×.finish_time) < 0 ||
365 bus_get_uint64_property(bus,
366 "/org/freedesktop/systemd1",
367 "org.freedesktop.systemd1.Manager",
368 "GeneratorsStartTimestampMonotonic",
369 ×.generators_start_time) < 0 ||
370 bus_get_uint64_property(bus,
371 "/org/freedesktop/systemd1",
372 "org.freedesktop.systemd1.Manager",
373 "GeneratorsFinishTimestampMonotonic",
374 ×.generators_finish_time) < 0 ||
375 bus_get_uint64_property(bus,
376 "/org/freedesktop/systemd1",
377 "org.freedesktop.systemd1.Manager",
378 "UnitsLoadStartTimestampMonotonic",
379 ×.unitsload_start_time) < 0 ||
380 bus_get_uint64_property(bus,
381 "/org/freedesktop/systemd1",
382 "org.freedesktop.systemd1.Manager",
383 "UnitsLoadFinishTimestampMonotonic",
384 ×.unitsload_finish_time) < 0)
387 if (times.finish_time <= 0) {
388 log_error("Bootup is not yet finished. Please try again later.");
392 if (times.initrd_time)
393 times.kernel_done_time = times.initrd_time;
395 times.kernel_done_time = times.userspace_time;
404 static int pretty_boot_time(sd_bus *bus, char **_buf) {
405 char ts[FORMAT_TIMESPAN_MAX];
406 struct boot_times *t;
407 static char buf[4096];
412 r = acquire_boot_times(bus, &t);
419 size = strpcpyf(&ptr, size, "Startup finished in ");
420 if (t->firmware_time)
421 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
423 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
425 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
426 if (t->initrd_time > 0)
427 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
429 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
430 if (t->kernel_time > 0)
431 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
433 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
443 static void svg_graph_box(double height, double begin, double end) {
446 /* outside box, fill */
447 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
448 SCALE_X * (end - begin), SCALE_Y * height);
450 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
451 /* lines for each second */
452 if (i % 5000000 == 0)
453 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
454 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
455 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
456 else if (i % 1000000 == 0)
457 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
458 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
459 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
461 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
462 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
466 static int analyze_plot(sd_bus *bus) {
467 struct unit_times *times;
468 struct boot_times *boot;
472 _cleanup_free_ char *pretty_times = NULL, *osname = NULL;
473 struct unit_times *u;
475 n = acquire_boot_times(bus, &boot);
479 n = pretty_boot_time(bus, &pretty_times);
483 get_os_name(&osname);
484 assert_se(uname(&name) >= 0);
486 n = acquire_time_data(bus, ×);
490 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
492 width = SCALE_X * (boot->firmware_time + boot->finish_time);
496 if (boot->firmware_time > boot->loader_time)
498 if (boot->loader_time) {
503 if (boot->initrd_time)
505 if (boot->kernel_time)
508 for (u = times; u < times + n; u++) {
509 double text_start, text_width;
511 if (u->activating < boot->userspace_time ||
512 u->activating > boot->finish_time) {
518 /* If the text cannot fit on the left side then
519 * increase the svg width so it fits on the right.
520 * TODO: calculate the text width more accurately */
521 text_width = 8.0 * strlen(u->name);
522 text_start = (boot->firmware_time + u->activating) * SCALE_X;
523 if (text_width > text_start && text_width + text_start > width)
524 width = text_width + text_start;
526 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
527 && u->activated == 0 && u->deactivating == 0)
528 u->activated = u->deactivating = u->deactivated;
529 if (u->activated < u->activating || u->activated > boot->finish_time)
530 u->activated = boot->finish_time;
531 if (u->deactivating < u->activated || u->activated > boot->finish_time)
532 u->deactivating = boot->finish_time;
533 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
534 u->deactivated = boot->finish_time;
538 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
539 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
540 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
542 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
543 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
544 80.0 + width, 150.0 + (m * SCALE_Y) +
545 5 * SCALE_Y /* legend */);
547 /* write some basic info as a comment, including some help */
548 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
549 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
550 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
551 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
552 "<!-- point your browser to this file. -->\n\n"
553 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
556 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
557 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
558 " rect.background { fill: rgb(255,255,255); }\n"
559 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
560 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
561 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
562 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
563 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
564 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
565 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
566 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
567 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
568 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
569 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
570 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
572 " line.sec5 { stroke-width: 2; }\n"
573 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
574 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
575 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
576 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
577 " text.sec { font-size: 10px; }\n"
578 " ]]>\n </style>\n</defs>\n\n");
580 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
581 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
582 svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
583 isempty(osname) ? "Linux" : osname,
584 name.nodename, name.release, name.version, name.machine);
586 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
587 svg_graph_box(m, -boot->firmware_time, boot->finish_time);
589 if (boot->firmware_time) {
590 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
591 svg_text(true, -(double) boot->firmware_time, y, "firmware");
594 if (boot->loader_time) {
595 svg_bar("loader", -(double) boot->loader_time, 0, y);
596 svg_text(true, -(double) boot->loader_time, y, "loader");
599 if (boot->kernel_time) {
600 svg_bar("kernel", 0, boot->kernel_done_time, y);
601 svg_text(true, 0, y, "kernel");
604 if (boot->initrd_time) {
605 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
606 svg_text(true, boot->initrd_time, y, "initrd");
609 svg_bar("active", boot->userspace_time, boot->finish_time, y);
610 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
611 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
612 svg_text(true, boot->userspace_time, y, "systemd");
615 for (u = times; u < times + n; u++) {
616 char ts[FORMAT_TIMESPAN_MAX];
622 svg_bar("activating", u->activating, u->activated, y);
623 svg_bar("active", u->activated, u->deactivating, y);
624 svg_bar("deactivating", u->deactivating, u->deactivated, y);
626 /* place the text on the left if we have passed the half of the svg width */
627 b = u->activating * SCALE_X < width / 2;
629 svg_text(b, u->activating, y, "%s (%s)",
630 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
632 svg_text(b, u->activating, y, "%s", u->name);
638 svg_bar("activating", 0, 300000, y);
639 svg_text(true, 400000, y, "Activating");
641 svg_bar("active", 0, 300000, y);
642 svg_text(true, 400000, y, "Active");
644 svg_bar("deactivating", 0, 300000, y);
645 svg_text(true, 400000, y, "Deactivating");
647 svg_bar("generators", 0, 300000, y);
648 svg_text(true, 400000, y, "Generators");
650 svg_bar("unitsload", 0, 300000, y);
651 svg_text(true, 400000, y, "Loading unit files");
658 free_unit_times(times, (unsigned) n);
663 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
664 bool last, struct unit_times *times, struct boot_times *boot) {
666 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
668 for (i = level; i != 0; i--)
669 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));
671 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
675 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
676 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
677 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
678 else if (times->activated > boot->userspace_time)
679 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
689 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
690 _cleanup_free_ char *path = NULL;
696 path = unit_dbus_path_from_name(name);
700 return bus_get_unit_property_strv(bus, path, "After", deps);
703 static Hashmap *unit_times_hashmap;
705 static int list_dependencies_compare(const void *_a, const void *_b) {
706 const char **a = (const char**) _a, **b = (const char**) _b;
707 usec_t usa = 0, usb = 0;
708 struct unit_times *times;
710 times = hashmap_get(unit_times_hashmap, *a);
712 usa = times->activated;
713 times = hashmap_get(unit_times_hashmap, *b);
715 usb = times->activated;
720 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
721 unsigned int branches) {
722 _cleanup_strv_free_ char **deps = NULL;
725 usec_t service_longest = 0;
727 struct unit_times *times;
728 struct boot_times *boot;
730 if (strv_extend(units, name))
733 r = list_dependencies_get_dependencies(bus, name, &deps);
737 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
739 r = acquire_boot_times(bus, &boot);
743 STRV_FOREACH(c, deps) {
744 times = hashmap_get(unit_times_hashmap, *c);
747 && times->activated <= boot->finish_time
748 && (times->activated >= service_longest
749 || service_longest == 0)) {
750 service_longest = times->activated;
755 if (service_longest == 0 )
758 STRV_FOREACH(c, deps) {
759 times = hashmap_get(unit_times_hashmap, *c);
760 if (times && times->activated
761 && times->activated <= boot->finish_time
762 && (service_longest - times->activated) <= arg_fuzz) {
770 STRV_FOREACH(c, deps) {
771 times = hashmap_get(unit_times_hashmap, *c);
774 || times->activated > boot->finish_time
775 || service_longest - times->activated > arg_fuzz)
780 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
784 if (strv_contains(*units, *c)) {
785 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
792 r = list_dependencies_one(bus, *c, level + 1, units,
793 (branches << 1) | (to_print ? 1 : 0));
803 static int list_dependencies(sd_bus *bus, const char *name) {
804 _cleanup_strv_free_ char **units = NULL;
805 char ts[FORMAT_TIMESPAN_MAX];
806 struct unit_times *times;
808 const char *path, *id;
809 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
810 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
811 struct boot_times *boot;
815 path = unit_dbus_path_from_name(name);
819 r = sd_bus_get_property(
821 "org.freedesktop.systemd1",
823 "org.freedesktop.systemd1.Unit",
829 log_error("Failed to get ID: %s", bus_error_message(&error, -r));
833 r = sd_bus_message_read(reply, "s", &id);
835 return bus_log_parse_error(r);
837 times = hashmap_get(unit_times_hashmap, id);
839 r = acquire_boot_times(bus, &boot);
845 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
846 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
847 else if (times->activated > boot->userspace_time)
848 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
853 return list_dependencies_one(bus, name, 0, &units, 0);
856 static int analyze_critical_chain(sd_bus *bus, char *names[]) {
857 struct unit_times *times;
862 n = acquire_time_data(bus, ×);
866 h = hashmap_new(string_hash_func, string_compare_func);
870 for (i = 0; i < (unsigned)n; i++) {
871 r = hashmap_put(h, times[i].name, ×[i]);
875 unit_times_hashmap = h;
877 pager_open_if_enabled();
879 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
880 "The time the unit takes to start is printed after the \"+\" character.\n");
882 if (!strv_isempty(names)) {
884 STRV_FOREACH(name, names)
885 list_dependencies(bus, *name);
887 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
890 free_unit_times(times, (unsigned) n);
894 static int analyze_blame(sd_bus *bus) {
895 struct unit_times *times;
899 n = acquire_time_data(bus, ×);
903 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
905 pager_open_if_enabled();
907 for (i = 0; i < (unsigned) n; i++) {
908 char ts[FORMAT_TIMESPAN_MAX];
910 if (times[i].time > 0)
911 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
914 free_unit_times(times, (unsigned) n);
918 static int analyze_time(sd_bus *bus) {
919 _cleanup_free_ char *buf = NULL;
922 r = pretty_boot_time(bus, &buf);
930 static int graph_one_property(sd_bus *bus, const struct unit_info *u, const char* prop, const char *color, char* patterns[]) {
931 _cleanup_strv_free_ char **units = NULL;
939 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
943 STRV_FOREACH(unit, units) {
947 if (!strv_isempty(arg_dot_from_patterns)) {
950 STRV_FOREACH(p, arg_dot_from_patterns)
951 if (fnmatch(*p, u->id, 0) == 0) {
960 if (!strv_isempty(arg_dot_to_patterns)) {
963 STRV_FOREACH(p, arg_dot_to_patterns)
964 if (fnmatch(*p, *unit, 0) == 0) {
973 if (!strv_isempty(patterns)) {
976 STRV_FOREACH(p, patterns)
977 if (fnmatch(*p, u->id, 0) == 0 || fnmatch(*p, *unit, 0) == 0) {
985 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
991 static int graph_one(sd_bus *bus, const struct unit_info *u, char *patterns[]) {
997 if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
998 r = graph_one_property(bus, u, "After", "green", patterns);
1003 if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
1004 r = graph_one_property(bus, u, "Requires", "black", patterns);
1007 r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns);
1010 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns);
1013 r = graph_one_property(bus, u, "Wants", "grey66", patterns);
1016 r = graph_one_property(bus, u, "Conflicts", "red", patterns);
1019 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns);
1027 static int dot(sd_bus *bus, char* patterns[]) {
1028 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1029 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1033 r = sd_bus_call_method(
1035 "org.freedesktop.systemd1",
1036 "/org/freedesktop/systemd1",
1037 "org.freedesktop.systemd1.Manager",
1043 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1047 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1049 return bus_log_parse_error(r);
1051 printf("digraph systemd {\n");
1053 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1054 r = graph_one(bus, &u, patterns);
1061 log_info(" Color legend: black = Requires\n"
1062 " dark blue = Requisite\n"
1063 " dark grey = Wants\n"
1064 " red = Conflicts\n"
1065 " green = After\n");
1068 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1069 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1074 static int dump(sd_bus *bus, char **args) {
1075 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1076 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1077 const char *text = NULL;
1080 if (!strv_isempty(args)) {
1081 log_error("Too many arguments.");
1085 pager_open_if_enabled();
1087 r = sd_bus_call_method(
1089 "org.freedesktop.systemd1",
1090 "/org/freedesktop/systemd1",
1091 "org.freedesktop.systemd1.Manager",
1097 log_error("Failed issue method call: %s", bus_error_message(&error, -r));
1101 r = sd_bus_message_read(reply, "s", &text);
1103 return bus_log_parse_error(r);
1105 fputs(text, stdout);
1109 static int set_log_level(sd_bus *bus, char **args) {
1110 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1116 if (strv_length(args) != 1) {
1117 log_error("This command expects one argument only.");
1121 r = sd_bus_set_property(
1123 "org.freedesktop.systemd1",
1124 "/org/freedesktop/systemd1",
1125 "org.freedesktop.systemd1.Manager",
1131 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1138 static int help(void) {
1140 pager_open_if_enabled();
1142 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1143 "Process systemd profiling information.\n\n"
1144 " -h --help Show this help\n"
1145 " --version Show package version\n"
1146 " --no-pager Do not pipe output into a pager\n"
1147 " --system Connect to system manager\n"
1148 " --user Connect to user manager\n"
1149 " -H --host=[USER@]HOST Operate on remote host\n"
1150 " -M --machine=CONTAINER Operate on local container\n"
1151 " --order When generating a dependency graph, show only order\n"
1152 " --require When generating a dependency graph, show only requirement\n"
1153 " --from-pattern=GLOB, --to-pattern=GLOB\n"
1154 " When generating a dependency graph, filter only origins\n"
1155 " or destinations, respectively\n"
1156 " --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
1157 " services, which finished TIMESPAN earlier, than the\n"
1158 " latest in the branch. The unit of TIMESPAN is seconds\n"
1159 " unless specified with a different unit, i.e. 50ms\n\n"
1161 " time Print time spent in the kernel before reaching userspace\n"
1162 " blame Print list of running units ordered by time to init\n"
1163 " critical-chain Print a tree of the time critical chain of units\n"
1164 " plot Output SVG graphic showing service initialization\n"
1165 " dot Output dependency graph in dot(1) format\n"
1166 " set-log-level LEVEL Set logging threshold for systemd\n"
1167 " dump Output state serialization of service manager\n",
1168 program_invocation_short_name);
1170 /* When updating this list, including descriptions, apply
1171 * changes to shell-completion/bash/systemd and
1172 * shell-completion/systemd-zsh-completion.zsh too. */
1177 static int parse_argv(int argc, char *argv[]) {
1179 ARG_VERSION = 0x100,
1184 ARG_DOT_FROM_PATTERN,
1190 static const struct option options[] = {
1191 { "help", no_argument, NULL, 'h' },
1192 { "version", no_argument, NULL, ARG_VERSION },
1193 { "order", no_argument, NULL, ARG_ORDER },
1194 { "require", no_argument, NULL, ARG_REQUIRE },
1195 { "user", no_argument, NULL, ARG_USER },
1196 { "system", no_argument, NULL, ARG_SYSTEM },
1197 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1198 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1199 { "fuzz", required_argument, NULL, ARG_FUZZ },
1200 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1209 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
1217 puts(PACKAGE_STRING);
1218 puts(SYSTEMD_FEATURES);
1230 arg_dot = DEP_ORDER;
1234 arg_dot = DEP_REQUIRE;
1237 case ARG_DOT_FROM_PATTERN:
1238 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1243 case ARG_DOT_TO_PATTERN:
1244 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1250 r = parse_sec(optarg, &arg_fuzz);
1256 arg_no_pager = true;
1260 arg_transport = BUS_TRANSPORT_REMOTE;
1265 arg_transport = BUS_TRANSPORT_CONTAINER;
1273 assert_not_reached("Unhandled option");
1280 int main(int argc, char *argv[]) {
1281 _cleanup_bus_unref_ sd_bus *bus = NULL;
1284 setlocale(LC_ALL, "");
1285 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1286 log_parse_environment();
1289 r = parse_argv(argc, argv);
1293 r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
1295 log_error("Failed to create bus connection: %s", strerror(-r));
1299 if (!argv[optind] || streq(argv[optind], "time"))
1300 r = analyze_time(bus);
1301 else if (streq(argv[optind], "blame"))
1302 r = analyze_blame(bus);
1303 else if (streq(argv[optind], "critical-chain"))
1304 r = analyze_critical_chain(bus, argv+optind+1);
1305 else if (streq(argv[optind], "plot"))
1306 r = analyze_plot(bus);
1307 else if (streq(argv[optind], "dot"))
1308 r = dot(bus, argv+optind+1);
1309 else if (streq(argv[optind], "dump"))
1310 r = dump(bus, argv+optind+1);
1311 else if (streq(argv[optind], "set-log-level"))
1312 r = set_log_level(bus, argv+optind+1);
1314 log_error("Unknown operation '%s'.", argv[optind]);
1319 strv_free(arg_dot_from_patterns);
1320 strv_free(arg_dot_to_patterns);
1322 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;