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"
44 #include "analyze-verify.h"
46 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
47 #define SCALE_Y (20.0)
49 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
51 #define svg(...) printf(__VA_ARGS__)
53 #define svg_bar(class, x1, x2, y) \
54 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
56 SCALE_X * (x1), SCALE_Y * (y), \
57 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
59 #define svg_text(b, x, y, format, ...) \
61 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
62 svg(format, ## __VA_ARGS__); \
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;
75 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
76 static char *arg_host = NULL;
77 static bool arg_user = false;
78 static bool arg_man = true;
84 usec_t kernel_done_time;
86 usec_t userspace_time;
88 usec_t security_start_time;
89 usec_t security_finish_time;
90 usec_t generators_start_time;
91 usec_t generators_finish_time;
92 usec_t unitsload_start_time;
93 usec_t unitsload_finish_time;
108 char *kernel_release;
109 char *kernel_version;
110 char *os_pretty_name;
111 char *virtualization;
115 static void pager_open_if_enabled(void) {
123 static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
124 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
133 r = sd_bus_get_property_trivial(
135 "org.freedesktop.systemd1",
143 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
150 static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
151 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
159 r = sd_bus_get_property_strv(
161 "org.freedesktop.systemd1",
163 "org.freedesktop.systemd1.Unit",
168 log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
175 static int compare_unit_time(const void *a, const void *b) {
176 return compare(((struct unit_times *)b)->time,
177 ((struct unit_times *)a)->time);
180 static int compare_unit_start(const void *a, const void *b) {
181 return compare(((struct unit_times *)a)->activating,
182 ((struct unit_times *)b)->activating);
185 static void free_unit_times(struct unit_times *t, unsigned n) {
186 struct unit_times *p;
188 for (p = t; p < t + n; p++)
194 static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
195 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
196 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
198 struct unit_times *unit_times = NULL;
202 r = sd_bus_call_method(
204 "org.freedesktop.systemd1",
205 "/org/freedesktop/systemd1",
206 "org.freedesktop.systemd1.Manager",
211 log_error("Failed to list units: %s", bus_error_message(&error, -r));
215 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
217 bus_log_parse_error(r);
221 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
222 struct unit_times *t;
224 if (!GREEDY_REALLOC(unit_times, size, c+1)) {
232 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
234 if (bus_get_uint64_property(bus, u.unit_path,
235 "org.freedesktop.systemd1.Unit",
236 "InactiveExitTimestampMonotonic",
237 &t->activating) < 0 ||
238 bus_get_uint64_property(bus, u.unit_path,
239 "org.freedesktop.systemd1.Unit",
240 "ActiveEnterTimestampMonotonic",
241 &t->activated) < 0 ||
242 bus_get_uint64_property(bus, u.unit_path,
243 "org.freedesktop.systemd1.Unit",
244 "ActiveExitTimestampMonotonic",
245 &t->deactivating) < 0 ||
246 bus_get_uint64_property(bus, u.unit_path,
247 "org.freedesktop.systemd1.Unit",
248 "InactiveEnterTimestampMonotonic",
249 &t->deactivated) < 0) {
254 if (t->activated >= t->activating)
255 t->time = t->activated - t->activating;
256 else if (t->deactivated >= t->activating)
257 t->time = t->deactivated - t->activating;
261 if (t->activating == 0)
264 t->name = strdup(u.id);
265 if (t->name == NULL) {
272 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 "SecurityStartTimestampMonotonic",
328 ×.security_start_time) < 0 ||
329 bus_get_uint64_property(bus,
330 "/org/freedesktop/systemd1",
331 "org.freedesktop.systemd1.Manager",
332 "SecurityFinishTimestampMonotonic",
333 ×.security_finish_time) < 0 ||
334 bus_get_uint64_property(bus,
335 "/org/freedesktop/systemd1",
336 "org.freedesktop.systemd1.Manager",
337 "GeneratorsStartTimestampMonotonic",
338 ×.generators_start_time) < 0 ||
339 bus_get_uint64_property(bus,
340 "/org/freedesktop/systemd1",
341 "org.freedesktop.systemd1.Manager",
342 "GeneratorsFinishTimestampMonotonic",
343 ×.generators_finish_time) < 0 ||
344 bus_get_uint64_property(bus,
345 "/org/freedesktop/systemd1",
346 "org.freedesktop.systemd1.Manager",
347 "UnitsLoadStartTimestampMonotonic",
348 ×.unitsload_start_time) < 0 ||
349 bus_get_uint64_property(bus,
350 "/org/freedesktop/systemd1",
351 "org.freedesktop.systemd1.Manager",
352 "UnitsLoadFinishTimestampMonotonic",
353 ×.unitsload_finish_time) < 0)
356 if (times.finish_time <= 0) {
357 log_error("Bootup is not yet finished. Please try again later.");
361 if (times.initrd_time)
362 times.kernel_done_time = times.initrd_time;
364 times.kernel_done_time = times.userspace_time;
373 static void free_host_info(struct host_info *hi) {
375 free(hi->kernel_name);
376 free(hi->kernel_release);
377 free(hi->kernel_version);
378 free(hi->os_pretty_name);
379 free(hi->virtualization);
380 free(hi->architecture);
384 static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
386 struct host_info *host;
388 static const struct bus_properties_map hostname_map[] = {
389 { "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
390 { "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
391 { "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
392 { "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
393 { "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
397 static const struct bus_properties_map manager_map[] = {
398 { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
399 { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
403 host = new0(struct host_info, 1);
407 r = bus_map_all_properties(bus,
408 "org.freedesktop.hostname1",
409 "/org/freedesktop/hostname1",
415 r = bus_map_all_properties(bus,
416 "org.freedesktop.systemd1",
417 "/org/freedesktop/systemd1",
426 free_host_info(host);
430 static int pretty_boot_time(sd_bus *bus, char **_buf) {
431 char ts[FORMAT_TIMESPAN_MAX];
432 struct boot_times *t;
433 static char buf[4096];
438 r = acquire_boot_times(bus, &t);
445 size = strpcpyf(&ptr, size, "Startup finished in ");
446 if (t->firmware_time)
447 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
449 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
451 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
452 if (t->initrd_time > 0)
453 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
455 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
456 if (t->kernel_time > 0)
457 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
459 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
469 static void svg_graph_box(double height, double begin, double end) {
472 /* outside box, fill */
473 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
474 SCALE_X * (end - begin), SCALE_Y * height);
476 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
477 /* lines for each second */
478 if (i % 5000000 == 0)
479 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
480 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
481 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
482 else if (i % 1000000 == 0)
483 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
484 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
485 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
487 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
488 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
492 static int analyze_plot(sd_bus *bus) {
493 struct unit_times *times;
494 struct boot_times *boot;
495 struct host_info *host = NULL;
498 _cleanup_free_ char *pretty_times = NULL;
499 struct unit_times *u;
501 n = acquire_boot_times(bus, &boot);
505 n = pretty_boot_time(bus, &pretty_times);
509 n = acquire_host_info(bus, &host);
513 n = acquire_time_data(bus, ×);
517 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
519 width = SCALE_X * (boot->firmware_time + boot->finish_time);
523 if (boot->firmware_time > boot->loader_time)
525 if (boot->loader_time) {
530 if (boot->initrd_time)
532 if (boot->kernel_time)
535 for (u = times; u < times + n; u++) {
536 double text_start, text_width;
538 if (u->activating < boot->userspace_time ||
539 u->activating > boot->finish_time) {
545 /* If the text cannot fit on the left side then
546 * increase the svg width so it fits on the right.
547 * TODO: calculate the text width more accurately */
548 text_width = 8.0 * strlen(u->name);
549 text_start = (boot->firmware_time + u->activating) * SCALE_X;
550 if (text_width > text_start && text_width + text_start > width)
551 width = text_width + text_start;
553 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
554 && u->activated == 0 && u->deactivating == 0)
555 u->activated = u->deactivating = u->deactivated;
556 if (u->activated < u->activating || u->activated > boot->finish_time)
557 u->activated = boot->finish_time;
558 if (u->deactivating < u->activated || u->activated > boot->finish_time)
559 u->deactivating = boot->finish_time;
560 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
561 u->deactivated = boot->finish_time;
565 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
566 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
567 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
569 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
570 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
571 80.0 + width, 150.0 + (m * SCALE_Y) +
572 5 * SCALE_Y /* legend */);
574 /* write some basic info as a comment, including some help */
575 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
576 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
577 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
578 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
579 "<!-- point your browser to this file. -->\n\n"
580 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
583 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
584 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
585 " rect.background { fill: rgb(255,255,255); }\n"
586 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
587 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
588 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
589 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
590 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
591 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
592 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
593 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
594 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
595 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
596 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
597 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
598 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
600 " line.sec5 { stroke-width: 2; }\n"
601 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
602 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
603 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
604 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
605 " text.sec { font-size: 10px; }\n"
606 " ]]>\n </style>\n</defs>\n\n");
608 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
609 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
610 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
611 isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
612 isempty(host->hostname) ? "" : host->hostname,
613 isempty(host->kernel_name) ? "" : host->kernel_name,
614 isempty(host->kernel_release) ? "" : host->kernel_release,
615 isempty(host->kernel_version) ? "" : host->kernel_version,
616 isempty(host->architecture) ? "" : host->architecture,
617 isempty(host->virtualization) ? "" : host->virtualization);
619 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
620 svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
622 if (boot->firmware_time) {
623 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
624 svg_text(true, -(double) boot->firmware_time, y, "firmware");
627 if (boot->loader_time) {
628 svg_bar("loader", -(double) boot->loader_time, 0, y);
629 svg_text(true, -(double) boot->loader_time, y, "loader");
632 if (boot->kernel_time) {
633 svg_bar("kernel", 0, boot->kernel_done_time, y);
634 svg_text(true, 0, y, "kernel");
637 if (boot->initrd_time) {
638 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
639 svg_text(true, boot->initrd_time, y, "initrd");
642 svg_bar("active", boot->userspace_time, boot->finish_time, y);
643 svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
644 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
645 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
646 svg_text(true, boot->userspace_time, y, "systemd");
649 for (u = times; u < times + n; u++) {
650 char ts[FORMAT_TIMESPAN_MAX];
656 svg_bar("activating", u->activating, u->activated, y);
657 svg_bar("active", u->activated, u->deactivating, y);
658 svg_bar("deactivating", u->deactivating, u->deactivated, y);
660 /* place the text on the left if we have passed the half of the svg width */
661 b = u->activating * SCALE_X < width / 2;
663 svg_text(b, u->activating, y, "%s (%s)",
664 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
666 svg_text(b, u->activating, y, "%s", u->name);
673 svg("<g transform=\"translate(20,100)\">\n");
675 svg_bar("activating", 0, 300000, y);
676 svg_text(true, 400000, y, "Activating");
678 svg_bar("active", 0, 300000, y);
679 svg_text(true, 400000, y, "Active");
681 svg_bar("deactivating", 0, 300000, y);
682 svg_text(true, 400000, y, "Deactivating");
684 svg_bar("security", 0, 300000, y);
685 svg_text(true, 400000, y, "Setting up security module");
687 svg_bar("generators", 0, 300000, y);
688 svg_text(true, 400000, y, "Generators");
690 svg_bar("unitsload", 0, 300000, y);
691 svg_text(true, 400000, y, "Loading unit files");
698 free_unit_times(times, (unsigned) n);
702 free_host_info(host);
706 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
707 bool last, struct unit_times *times, struct boot_times *boot) {
709 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
711 for (i = level; i != 0; i--)
712 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE));
714 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
718 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
719 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
720 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
721 else if (times->activated > boot->userspace_time)
722 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
732 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
733 _cleanup_free_ char *path = NULL;
739 path = unit_dbus_path_from_name(name);
743 return bus_get_unit_property_strv(bus, path, "After", deps);
746 static Hashmap *unit_times_hashmap;
748 static int list_dependencies_compare(const void *_a, const void *_b) {
749 const char **a = (const char**) _a, **b = (const char**) _b;
750 usec_t usa = 0, usb = 0;
751 struct unit_times *times;
753 times = hashmap_get(unit_times_hashmap, *a);
755 usa = times->activated;
756 times = hashmap_get(unit_times_hashmap, *b);
758 usb = times->activated;
763 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
764 unsigned int branches) {
765 _cleanup_strv_free_ char **deps = NULL;
768 usec_t service_longest = 0;
770 struct unit_times *times;
771 struct boot_times *boot;
773 if (strv_extend(units, name))
776 r = list_dependencies_get_dependencies(bus, name, &deps);
780 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
782 r = acquire_boot_times(bus, &boot);
786 STRV_FOREACH(c, deps) {
787 times = hashmap_get(unit_times_hashmap, *c);
790 && times->activated <= boot->finish_time
791 && (times->activated >= service_longest
792 || service_longest == 0)) {
793 service_longest = times->activated;
798 if (service_longest == 0 )
801 STRV_FOREACH(c, deps) {
802 times = hashmap_get(unit_times_hashmap, *c);
803 if (times && times->activated
804 && times->activated <= boot->finish_time
805 && (service_longest - times->activated) <= arg_fuzz) {
813 STRV_FOREACH(c, deps) {
814 times = hashmap_get(unit_times_hashmap, *c);
817 || times->activated > boot->finish_time
818 || service_longest - times->activated > arg_fuzz)
823 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
827 if (strv_contains(*units, *c)) {
828 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
835 r = list_dependencies_one(bus, *c, level + 1, units,
836 (branches << 1) | (to_print ? 1 : 0));
846 static int list_dependencies(sd_bus *bus, const char *name) {
847 _cleanup_strv_free_ char **units = NULL;
848 char ts[FORMAT_TIMESPAN_MAX];
849 struct unit_times *times;
851 const char *path, *id;
852 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
853 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
854 struct boot_times *boot;
858 path = unit_dbus_path_from_name(name);
862 r = sd_bus_get_property(
864 "org.freedesktop.systemd1",
866 "org.freedesktop.systemd1.Unit",
872 log_error("Failed to get ID: %s", bus_error_message(&error, -r));
876 r = sd_bus_message_read(reply, "s", &id);
878 return bus_log_parse_error(r);
880 times = hashmap_get(unit_times_hashmap, id);
882 r = acquire_boot_times(bus, &boot);
888 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
889 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
890 else if (times->activated > boot->userspace_time)
891 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
896 return list_dependencies_one(bus, name, 0, &units, 0);
899 static int analyze_critical_chain(sd_bus *bus, char *names[]) {
900 struct unit_times *times;
905 n = acquire_time_data(bus, ×);
909 h = hashmap_new(string_hash_func, string_compare_func);
913 for (i = 0; i < (unsigned)n; i++) {
914 r = hashmap_put(h, times[i].name, ×[i]);
918 unit_times_hashmap = h;
920 pager_open_if_enabled();
922 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
923 "The time the unit takes to start is printed after the \"+\" character.\n");
925 if (!strv_isempty(names)) {
927 STRV_FOREACH(name, names)
928 list_dependencies(bus, *name);
930 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
933 free_unit_times(times, (unsigned) n);
937 static int analyze_blame(sd_bus *bus) {
938 struct unit_times *times;
942 n = acquire_time_data(bus, ×);
946 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
948 pager_open_if_enabled();
950 for (i = 0; i < (unsigned) n; i++) {
951 char ts[FORMAT_TIMESPAN_MAX];
953 if (times[i].time > 0)
954 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
957 free_unit_times(times, (unsigned) n);
961 static int analyze_time(sd_bus *bus) {
962 _cleanup_free_ char *buf = NULL;
965 r = pretty_boot_time(bus, &buf);
973 static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[]) {
974 _cleanup_strv_free_ char **units = NULL;
982 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
986 STRV_FOREACH(unit, units) {
990 if (!strv_isempty(arg_dot_from_patterns)) {
993 STRV_FOREACH(p, arg_dot_from_patterns)
994 if (fnmatch(*p, u->id, 0) == 0) {
1003 if (!strv_isempty(arg_dot_to_patterns)) {
1004 match_found = false;
1006 STRV_FOREACH(p, arg_dot_to_patterns)
1007 if (fnmatch(*p, *unit, 0) == 0) {
1016 if (!strv_isempty(patterns)) {
1017 match_found = false;
1019 STRV_FOREACH(p, patterns)
1020 if (fnmatch(*p, u->id, 0) == 0 || fnmatch(*p, *unit, 0) == 0) {
1028 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1034 static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[]) {
1040 if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
1041 r = graph_one_property(bus, u, "After", "green", patterns);
1046 if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
1047 r = graph_one_property(bus, u, "Requires", "black", patterns);
1050 r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns);
1053 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns);
1056 r = graph_one_property(bus, u, "Wants", "grey66", patterns);
1059 r = graph_one_property(bus, u, "Conflicts", "red", patterns);
1062 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns);
1070 static int dot(sd_bus *bus, char* patterns[]) {
1071 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1072 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1076 r = sd_bus_call_method(
1078 "org.freedesktop.systemd1",
1079 "/org/freedesktop/systemd1",
1080 "org.freedesktop.systemd1.Manager",
1086 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1090 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1092 return bus_log_parse_error(r);
1094 printf("digraph systemd {\n");
1096 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1098 r = graph_one(bus, &u, patterns);
1103 return bus_log_parse_error(r);
1107 log_info(" Color legend: black = Requires\n"
1108 " dark blue = Requisite\n"
1109 " dark grey = Wants\n"
1110 " red = Conflicts\n"
1111 " green = After\n");
1114 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1115 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1120 static int dump(sd_bus *bus, char **args) {
1121 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1122 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1123 const char *text = NULL;
1126 if (!strv_isempty(args)) {
1127 log_error("Too many arguments.");
1131 pager_open_if_enabled();
1133 r = sd_bus_call_method(
1135 "org.freedesktop.systemd1",
1136 "/org/freedesktop/systemd1",
1137 "org.freedesktop.systemd1.Manager",
1143 log_error("Failed issue method call: %s", bus_error_message(&error, -r));
1147 r = sd_bus_message_read(reply, "s", &text);
1149 return bus_log_parse_error(r);
1151 fputs(text, stdout);
1155 static int set_log_level(sd_bus *bus, char **args) {
1156 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1162 if (strv_length(args) != 1) {
1163 log_error("This command expects one argument only.");
1167 r = sd_bus_set_property(
1169 "org.freedesktop.systemd1",
1170 "/org/freedesktop/systemd1",
1171 "org.freedesktop.systemd1.Manager",
1177 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1184 static void help(void) {
1186 pager_open_if_enabled();
1188 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1189 "Profile systemd, show unit dependencies, check unit files.\n\n"
1190 " -h --help Show this help\n"
1191 " --version Show package version\n"
1192 " --no-pager Do not pipe output into a pager\n"
1193 " --system Operate on system systemd instance\n"
1194 " --user Operate on user systemd instance\n"
1195 " -H --host=[USER@]HOST Operate on remote host\n"
1196 " -M --machine=CONTAINER Operate on local container\n"
1197 " --order When generating a dependency graph, show only order\n"
1198 " --require When generating a dependency graph, show only requirement\n"
1199 " --from-pattern=GLOB, --to-pattern=GLOB\n"
1200 " When generating a dependency graph, filter only origins\n"
1201 " or destinations, respectively\n"
1202 " --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
1203 " services, which finished TIMESPAN earlier, than the\n"
1204 " latest in the branch. The unit of TIMESPAN is seconds\n"
1205 " unless specified with a different unit, i.e. 50ms\n"
1206 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1208 " time Print time spent in the kernel before reaching userspace\n"
1209 " blame Print list of running units ordered by time to init\n"
1210 " critical-chain Print a tree of the time critical chain of units\n"
1211 " plot Output SVG graphic showing service initialization\n"
1212 " dot Output dependency graph in dot(1) format\n"
1213 " set-log-level LEVEL Set logging threshold for systemd\n"
1214 " dump Output state serialization of service manager\n"
1215 " verify FILE... Check unit files for correctness\n"
1216 , program_invocation_short_name);
1218 /* When updating this list, including descriptions, apply
1219 * changes to shell-completion/bash/systemd-analyze and
1220 * shell-completion/zsh/_systemd-analyze too. */
1223 static int parse_argv(int argc, char *argv[]) {
1225 ARG_VERSION = 0x100,
1230 ARG_DOT_FROM_PATTERN,
1237 static const struct option options[] = {
1238 { "help", no_argument, NULL, 'h' },
1239 { "version", no_argument, NULL, ARG_VERSION },
1240 { "order", no_argument, NULL, ARG_ORDER },
1241 { "require", no_argument, NULL, ARG_REQUIRE },
1242 { "user", no_argument, NULL, ARG_USER },
1243 { "system", no_argument, NULL, ARG_SYSTEM },
1244 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1245 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1246 { "fuzz", required_argument, NULL, ARG_FUZZ },
1247 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1248 { "man", optional_argument, NULL, ARG_MAN },
1249 { "host", required_argument, NULL, 'H' },
1250 { "machine", required_argument, NULL, 'M' },
1259 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
1267 puts(PACKAGE_STRING);
1268 puts(SYSTEMD_FEATURES);
1280 arg_dot = DEP_ORDER;
1284 arg_dot = DEP_REQUIRE;
1287 case ARG_DOT_FROM_PATTERN:
1288 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1293 case ARG_DOT_TO_PATTERN:
1294 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1300 r = parse_sec(optarg, &arg_fuzz);
1306 arg_no_pager = true;
1310 arg_transport = BUS_TRANSPORT_REMOTE;
1315 arg_transport = BUS_TRANSPORT_CONTAINER;
1321 r = parse_boolean(optarg);
1323 log_error("Failed to parse --man= argument.");
1337 assert_not_reached("Unhandled option code.");
1340 return 1; /* work to do */
1343 int main(int argc, char *argv[]) {
1346 setlocale(LC_ALL, "");
1347 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1348 log_parse_environment();
1351 r = parse_argv(argc, argv);
1355 if (streq_ptr(argv[optind], "verify"))
1356 r = verify_units(argv+optind+1,
1357 arg_user ? SYSTEMD_USER : SYSTEMD_SYSTEM,
1360 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1362 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1364 log_error("Failed to create bus connection: %s", strerror(-r));
1368 if (!argv[optind] || streq(argv[optind], "time"))
1369 r = analyze_time(bus);
1370 else if (streq(argv[optind], "blame"))
1371 r = analyze_blame(bus);
1372 else if (streq(argv[optind], "critical-chain"))
1373 r = analyze_critical_chain(bus, argv+optind+1);
1374 else if (streq(argv[optind], "plot"))
1375 r = analyze_plot(bus);
1376 else if (streq(argv[optind], "dot"))
1377 r = dot(bus, argv+optind+1);
1378 else if (streq(argv[optind], "dump"))
1379 r = dump(bus, argv+optind+1);
1380 else if (streq(argv[optind], "set-log-level"))
1381 r = set_log_level(bus, argv+optind+1);
1383 log_error("Unknown operation '%s'.", argv[optind]);
1389 strv_free(arg_dot_from_patterns);
1390 strv_free(arg_dot_to_patterns);
1392 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;