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 */
46 #define SCALE_Y (20.0)
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 security_start_time;
87 usec_t security_finish_time;
88 usec_t generators_start_time;
89 usec_t generators_finish_time;
90 usec_t unitsload_start_time;
91 usec_t unitsload_finish_time;
106 char *kernel_release;
107 char *kernel_version;
108 char *os_pretty_name;
109 char *virtualization;
113 static void pager_open_if_enabled(void) {
121 static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
122 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
131 r = sd_bus_get_property_trivial(
133 "org.freedesktop.systemd1",
141 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
148 static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
149 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
157 r = sd_bus_get_property_strv(
159 "org.freedesktop.systemd1",
161 "org.freedesktop.systemd1.Unit",
166 log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
173 static int compare_unit_time(const void *a, const void *b) {
174 return compare(((struct unit_times *)b)->time,
175 ((struct unit_times *)a)->time);
178 static int compare_unit_start(const void *a, const void *b) {
179 return compare(((struct unit_times *)a)->activating,
180 ((struct unit_times *)b)->activating);
183 static void free_unit_times(struct unit_times *t, unsigned n) {
184 struct unit_times *p;
186 for (p = t; p < t + n; p++)
192 static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
193 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
194 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
196 struct unit_times *unit_times = NULL;
200 r = sd_bus_call_method(
202 "org.freedesktop.systemd1",
203 "/org/freedesktop/systemd1",
204 "org.freedesktop.systemd1.Manager",
209 log_error("Failed to list units: %s", bus_error_message(&error, -r));
213 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
215 bus_log_parse_error(r);
219 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
220 struct unit_times *t;
222 if (!GREEDY_REALLOC(unit_times, size, c+1)) {
230 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
232 if (bus_get_uint64_property(bus, u.unit_path,
233 "org.freedesktop.systemd1.Unit",
234 "InactiveExitTimestampMonotonic",
235 &t->activating) < 0 ||
236 bus_get_uint64_property(bus, u.unit_path,
237 "org.freedesktop.systemd1.Unit",
238 "ActiveEnterTimestampMonotonic",
239 &t->activated) < 0 ||
240 bus_get_uint64_property(bus, u.unit_path,
241 "org.freedesktop.systemd1.Unit",
242 "ActiveExitTimestampMonotonic",
243 &t->deactivating) < 0 ||
244 bus_get_uint64_property(bus, u.unit_path,
245 "org.freedesktop.systemd1.Unit",
246 "InactiveEnterTimestampMonotonic",
247 &t->deactivated) < 0) {
252 if (t->activated >= t->activating)
253 t->time = t->activated - t->activating;
254 else if (t->deactivated >= t->activating)
255 t->time = t->deactivated - t->activating;
259 if (t->activating == 0)
262 t->name = strdup(u.id);
263 if (t->name == NULL) {
270 bus_log_parse_error(r);
278 free_unit_times(unit_times, (unsigned) c);
282 static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
283 static struct boot_times times;
284 static bool cached = false;
289 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
291 if (bus_get_uint64_property(bus,
292 "/org/freedesktop/systemd1",
293 "org.freedesktop.systemd1.Manager",
294 "FirmwareTimestampMonotonic",
295 ×.firmware_time) < 0 ||
296 bus_get_uint64_property(bus,
297 "/org/freedesktop/systemd1",
298 "org.freedesktop.systemd1.Manager",
299 "LoaderTimestampMonotonic",
300 ×.loader_time) < 0 ||
301 bus_get_uint64_property(bus,
302 "/org/freedesktop/systemd1",
303 "org.freedesktop.systemd1.Manager",
305 ×.kernel_time) < 0 ||
306 bus_get_uint64_property(bus,
307 "/org/freedesktop/systemd1",
308 "org.freedesktop.systemd1.Manager",
309 "InitRDTimestampMonotonic",
310 ×.initrd_time) < 0 ||
311 bus_get_uint64_property(bus,
312 "/org/freedesktop/systemd1",
313 "org.freedesktop.systemd1.Manager",
314 "UserspaceTimestampMonotonic",
315 ×.userspace_time) < 0 ||
316 bus_get_uint64_property(bus,
317 "/org/freedesktop/systemd1",
318 "org.freedesktop.systemd1.Manager",
319 "FinishTimestampMonotonic",
320 ×.finish_time) < 0 ||
321 bus_get_uint64_property(bus,
322 "/org/freedesktop/systemd1",
323 "org.freedesktop.systemd1.Manager",
324 "SecurityStartTimestampMonotonic",
325 ×.security_start_time) < 0 ||
326 bus_get_uint64_property(bus,
327 "/org/freedesktop/systemd1",
328 "org.freedesktop.systemd1.Manager",
329 "SecurityFinishTimestampMonotonic",
330 ×.security_finish_time) < 0 ||
331 bus_get_uint64_property(bus,
332 "/org/freedesktop/systemd1",
333 "org.freedesktop.systemd1.Manager",
334 "GeneratorsStartTimestampMonotonic",
335 ×.generators_start_time) < 0 ||
336 bus_get_uint64_property(bus,
337 "/org/freedesktop/systemd1",
338 "org.freedesktop.systemd1.Manager",
339 "GeneratorsFinishTimestampMonotonic",
340 ×.generators_finish_time) < 0 ||
341 bus_get_uint64_property(bus,
342 "/org/freedesktop/systemd1",
343 "org.freedesktop.systemd1.Manager",
344 "UnitsLoadStartTimestampMonotonic",
345 ×.unitsload_start_time) < 0 ||
346 bus_get_uint64_property(bus,
347 "/org/freedesktop/systemd1",
348 "org.freedesktop.systemd1.Manager",
349 "UnitsLoadFinishTimestampMonotonic",
350 ×.unitsload_finish_time) < 0)
353 if (times.finish_time <= 0) {
354 log_error("Bootup is not yet finished. Please try again later.");
358 if (times.initrd_time)
359 times.kernel_done_time = times.initrd_time;
361 times.kernel_done_time = times.userspace_time;
370 static void free_host_info(struct host_info *hi) {
372 free(hi->kernel_name);
373 free(hi->kernel_release);
374 free(hi->kernel_version);
375 free(hi->os_pretty_name);
376 free(hi->virtualization);
377 free(hi->architecture);
381 static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
383 struct host_info *host;
385 static const struct bus_properties_map hostname_map[] = {
386 { "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
387 { "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
388 { "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
389 { "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
390 { "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
394 static const struct bus_properties_map manager_map[] = {
395 { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
396 { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
400 host = new0(struct host_info, 1);
404 r = bus_map_all_properties(bus,
405 "org.freedesktop.hostname1",
406 "/org/freedesktop/hostname1",
412 r = bus_map_all_properties(bus,
413 "org.freedesktop.systemd1",
414 "/org/freedesktop/systemd1",
423 free_host_info(host);
427 static int pretty_boot_time(sd_bus *bus, char **_buf) {
428 char ts[FORMAT_TIMESPAN_MAX];
429 struct boot_times *t;
430 static char buf[4096];
435 r = acquire_boot_times(bus, &t);
442 size = strpcpyf(&ptr, size, "Startup finished in ");
443 if (t->firmware_time)
444 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
446 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
448 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
449 if (t->initrd_time > 0)
450 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
452 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
453 if (t->kernel_time > 0)
454 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
456 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
466 static void svg_graph_box(double height, double begin, double end) {
469 /* outside box, fill */
470 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
471 SCALE_X * (end - begin), SCALE_Y * height);
473 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
474 /* lines for each second */
475 if (i % 5000000 == 0)
476 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
477 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
478 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
479 else if (i % 1000000 == 0)
480 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
481 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
482 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
484 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
485 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
489 static int analyze_plot(sd_bus *bus) {
490 struct unit_times *times;
491 struct boot_times *boot;
492 struct host_info *host = NULL;
495 _cleanup_free_ char *pretty_times = NULL;
496 struct unit_times *u;
498 n = acquire_boot_times(bus, &boot);
502 n = pretty_boot_time(bus, &pretty_times);
506 n = acquire_host_info(bus, &host);
510 n = acquire_time_data(bus, ×);
514 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
516 width = SCALE_X * (boot->firmware_time + boot->finish_time);
520 if (boot->firmware_time > boot->loader_time)
522 if (boot->loader_time) {
527 if (boot->initrd_time)
529 if (boot->kernel_time)
532 for (u = times; u < times + n; u++) {
533 double text_start, text_width;
535 if (u->activating < boot->userspace_time ||
536 u->activating > boot->finish_time) {
542 /* If the text cannot fit on the left side then
543 * increase the svg width so it fits on the right.
544 * TODO: calculate the text width more accurately */
545 text_width = 8.0 * strlen(u->name);
546 text_start = (boot->firmware_time + u->activating) * SCALE_X;
547 if (text_width > text_start && text_width + text_start > width)
548 width = text_width + text_start;
550 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
551 && u->activated == 0 && u->deactivating == 0)
552 u->activated = u->deactivating = u->deactivated;
553 if (u->activated < u->activating || u->activated > boot->finish_time)
554 u->activated = boot->finish_time;
555 if (u->deactivating < u->activated || u->activated > boot->finish_time)
556 u->deactivating = boot->finish_time;
557 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
558 u->deactivated = boot->finish_time;
562 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
563 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
564 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
566 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
567 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
568 80.0 + width, 150.0 + (m * SCALE_Y) +
569 5 * SCALE_Y /* legend */);
571 /* write some basic info as a comment, including some help */
572 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
573 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
574 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
575 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
576 "<!-- point your browser to this file. -->\n\n"
577 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
580 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
581 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
582 " rect.background { fill: rgb(255,255,255); }\n"
583 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
584 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
585 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
586 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
587 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
588 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
589 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
590 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
591 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
592 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
593 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
594 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
595 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
597 " line.sec5 { stroke-width: 2; }\n"
598 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
599 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
600 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
601 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
602 " text.sec { font-size: 10px; }\n"
603 " ]]>\n </style>\n</defs>\n\n");
605 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
606 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
607 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
608 isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
609 isempty(host->hostname) ? "" : host->hostname,
610 isempty(host->kernel_name) ? "" : host->kernel_name,
611 isempty(host->kernel_release) ? "" : host->kernel_release,
612 isempty(host->kernel_version) ? "" : host->kernel_version,
613 isempty(host->architecture) ? "" : host->architecture,
614 isempty(host->virtualization) ? "" : host->virtualization);
616 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
617 svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
619 if (boot->firmware_time) {
620 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
621 svg_text(true, -(double) boot->firmware_time, y, "firmware");
624 if (boot->loader_time) {
625 svg_bar("loader", -(double) boot->loader_time, 0, y);
626 svg_text(true, -(double) boot->loader_time, y, "loader");
629 if (boot->kernel_time) {
630 svg_bar("kernel", 0, boot->kernel_done_time, y);
631 svg_text(true, 0, y, "kernel");
634 if (boot->initrd_time) {
635 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
636 svg_text(true, boot->initrd_time, y, "initrd");
639 svg_bar("active", boot->userspace_time, boot->finish_time, y);
640 svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
641 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
642 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
643 svg_text(true, boot->userspace_time, y, "systemd");
646 for (u = times; u < times + n; u++) {
647 char ts[FORMAT_TIMESPAN_MAX];
653 svg_bar("activating", u->activating, u->activated, y);
654 svg_bar("active", u->activated, u->deactivating, y);
655 svg_bar("deactivating", u->deactivating, u->deactivated, y);
657 /* place the text on the left if we have passed the half of the svg width */
658 b = u->activating * SCALE_X < width / 2;
660 svg_text(b, u->activating, y, "%s (%s)",
661 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
663 svg_text(b, u->activating, y, "%s", u->name);
670 svg("<g transform=\"translate(20,100)\">\n");
672 svg_bar("activating", 0, 300000, y);
673 svg_text(true, 400000, y, "Activating");
675 svg_bar("active", 0, 300000, y);
676 svg_text(true, 400000, y, "Active");
678 svg_bar("deactivating", 0, 300000, y);
679 svg_text(true, 400000, y, "Deactivating");
681 svg_bar("security", 0, 300000, y);
682 svg_text(true, 400000, y, "Setting up security module");
684 svg_bar("generators", 0, 300000, y);
685 svg_text(true, 400000, y, "Generators");
687 svg_bar("unitsload", 0, 300000, y);
688 svg_text(true, 400000, y, "Loading unit files");
695 free_unit_times(times, (unsigned) n);
699 free_host_info(host);
703 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
704 bool last, struct unit_times *times, struct boot_times *boot) {
706 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
708 for (i = level; i != 0; i--)
709 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE));
711 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
715 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
716 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
717 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
718 else if (times->activated > boot->userspace_time)
719 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
729 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
730 _cleanup_free_ char *path = NULL;
736 path = unit_dbus_path_from_name(name);
740 return bus_get_unit_property_strv(bus, path, "After", deps);
743 static Hashmap *unit_times_hashmap;
745 static int list_dependencies_compare(const void *_a, const void *_b) {
746 const char **a = (const char**) _a, **b = (const char**) _b;
747 usec_t usa = 0, usb = 0;
748 struct unit_times *times;
750 times = hashmap_get(unit_times_hashmap, *a);
752 usa = times->activated;
753 times = hashmap_get(unit_times_hashmap, *b);
755 usb = times->activated;
760 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
761 unsigned int branches) {
762 _cleanup_strv_free_ char **deps = NULL;
765 usec_t service_longest = 0;
767 struct unit_times *times;
768 struct boot_times *boot;
770 if (strv_extend(units, name))
773 r = list_dependencies_get_dependencies(bus, name, &deps);
777 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
779 r = acquire_boot_times(bus, &boot);
783 STRV_FOREACH(c, deps) {
784 times = hashmap_get(unit_times_hashmap, *c);
787 && times->activated <= boot->finish_time
788 && (times->activated >= service_longest
789 || service_longest == 0)) {
790 service_longest = times->activated;
795 if (service_longest == 0 )
798 STRV_FOREACH(c, deps) {
799 times = hashmap_get(unit_times_hashmap, *c);
800 if (times && times->activated
801 && times->activated <= boot->finish_time
802 && (service_longest - times->activated) <= arg_fuzz) {
810 STRV_FOREACH(c, deps) {
811 times = hashmap_get(unit_times_hashmap, *c);
814 || times->activated > boot->finish_time
815 || service_longest - times->activated > arg_fuzz)
820 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
824 if (strv_contains(*units, *c)) {
825 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
832 r = list_dependencies_one(bus, *c, level + 1, units,
833 (branches << 1) | (to_print ? 1 : 0));
843 static int list_dependencies(sd_bus *bus, const char *name) {
844 _cleanup_strv_free_ char **units = NULL;
845 char ts[FORMAT_TIMESPAN_MAX];
846 struct unit_times *times;
848 const char *path, *id;
849 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
850 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
851 struct boot_times *boot;
855 path = unit_dbus_path_from_name(name);
859 r = sd_bus_get_property(
861 "org.freedesktop.systemd1",
863 "org.freedesktop.systemd1.Unit",
869 log_error("Failed to get ID: %s", bus_error_message(&error, -r));
873 r = sd_bus_message_read(reply, "s", &id);
875 return bus_log_parse_error(r);
877 times = hashmap_get(unit_times_hashmap, id);
879 r = acquire_boot_times(bus, &boot);
885 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
886 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
887 else if (times->activated > boot->userspace_time)
888 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
893 return list_dependencies_one(bus, name, 0, &units, 0);
896 static int analyze_critical_chain(sd_bus *bus, char *names[]) {
897 struct unit_times *times;
902 n = acquire_time_data(bus, ×);
906 h = hashmap_new(string_hash_func, string_compare_func);
910 for (i = 0; i < (unsigned)n; i++) {
911 r = hashmap_put(h, times[i].name, ×[i]);
915 unit_times_hashmap = h;
917 pager_open_if_enabled();
919 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
920 "The time the unit takes to start is printed after the \"+\" character.\n");
922 if (!strv_isempty(names)) {
924 STRV_FOREACH(name, names)
925 list_dependencies(bus, *name);
927 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
930 free_unit_times(times, (unsigned) n);
934 static int analyze_blame(sd_bus *bus) {
935 struct unit_times *times;
939 n = acquire_time_data(bus, ×);
943 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
945 pager_open_if_enabled();
947 for (i = 0; i < (unsigned) n; i++) {
948 char ts[FORMAT_TIMESPAN_MAX];
950 if (times[i].time > 0)
951 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
954 free_unit_times(times, (unsigned) n);
958 static int analyze_time(sd_bus *bus) {
959 _cleanup_free_ char *buf = NULL;
962 r = pretty_boot_time(bus, &buf);
970 static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[]) {
971 _cleanup_strv_free_ char **units = NULL;
979 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
983 STRV_FOREACH(unit, units) {
987 if (!strv_isempty(arg_dot_from_patterns)) {
990 STRV_FOREACH(p, arg_dot_from_patterns)
991 if (fnmatch(*p, u->id, 0) == 0) {
1000 if (!strv_isempty(arg_dot_to_patterns)) {
1001 match_found = false;
1003 STRV_FOREACH(p, arg_dot_to_patterns)
1004 if (fnmatch(*p, *unit, 0) == 0) {
1013 if (!strv_isempty(patterns)) {
1014 match_found = false;
1016 STRV_FOREACH(p, patterns)
1017 if (fnmatch(*p, u->id, 0) == 0 || fnmatch(*p, *unit, 0) == 0) {
1025 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1031 static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[]) {
1037 if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
1038 r = graph_one_property(bus, u, "After", "green", patterns);
1043 if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
1044 r = graph_one_property(bus, u, "Requires", "black", patterns);
1047 r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns);
1050 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns);
1053 r = graph_one_property(bus, u, "Wants", "grey66", patterns);
1056 r = graph_one_property(bus, u, "Conflicts", "red", patterns);
1059 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns);
1067 static int dot(sd_bus *bus, char* patterns[]) {
1068 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1069 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1073 r = sd_bus_call_method(
1075 "org.freedesktop.systemd1",
1076 "/org/freedesktop/systemd1",
1077 "org.freedesktop.systemd1.Manager",
1083 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1087 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1089 return bus_log_parse_error(r);
1091 printf("digraph systemd {\n");
1093 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1095 r = graph_one(bus, &u, patterns);
1100 return bus_log_parse_error(r);
1104 log_info(" Color legend: black = Requires\n"
1105 " dark blue = Requisite\n"
1106 " dark grey = Wants\n"
1107 " red = Conflicts\n"
1108 " green = After\n");
1111 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1112 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1117 static int dump(sd_bus *bus, char **args) {
1118 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1119 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1120 const char *text = NULL;
1123 if (!strv_isempty(args)) {
1124 log_error("Too many arguments.");
1128 pager_open_if_enabled();
1130 r = sd_bus_call_method(
1132 "org.freedesktop.systemd1",
1133 "/org/freedesktop/systemd1",
1134 "org.freedesktop.systemd1.Manager",
1140 log_error("Failed issue method call: %s", bus_error_message(&error, -r));
1144 r = sd_bus_message_read(reply, "s", &text);
1146 return bus_log_parse_error(r);
1148 fputs(text, stdout);
1152 static int set_log_level(sd_bus *bus, char **args) {
1153 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1159 if (strv_length(args) != 1) {
1160 log_error("This command expects one argument only.");
1164 r = sd_bus_set_property(
1166 "org.freedesktop.systemd1",
1167 "/org/freedesktop/systemd1",
1168 "org.freedesktop.systemd1.Manager",
1174 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1181 static int help(void) {
1183 pager_open_if_enabled();
1185 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1186 "Process systemd profiling information.\n\n"
1187 " -h --help Show this help\n"
1188 " --version Show package version\n"
1189 " --no-pager Do not pipe output into a pager\n"
1190 " --system Connect to system manager\n"
1191 " --user Connect to user manager\n"
1192 " -H --host=[USER@]HOST Operate on remote host\n"
1193 " -M --machine=CONTAINER Operate on local container\n"
1194 " --order When generating a dependency graph, show only order\n"
1195 " --require When generating a dependency graph, show only requirement\n"
1196 " --from-pattern=GLOB, --to-pattern=GLOB\n"
1197 " When generating a dependency graph, filter only origins\n"
1198 " or destinations, respectively\n"
1199 " --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
1200 " services, which finished TIMESPAN earlier, than the\n"
1201 " latest in the branch. The unit of TIMESPAN is seconds\n"
1202 " unless specified with a different unit, i.e. 50ms\n\n"
1204 " time Print time spent in the kernel before reaching userspace\n"
1205 " blame Print list of running units ordered by time to init\n"
1206 " critical-chain Print a tree of the time critical chain of units\n"
1207 " plot Output SVG graphic showing service initialization\n"
1208 " dot Output dependency graph in dot(1) format\n"
1209 " set-log-level LEVEL Set logging threshold for systemd\n"
1210 " dump Output state serialization of service manager\n",
1211 program_invocation_short_name);
1213 /* When updating this list, including descriptions, apply
1214 * changes to shell-completion/bash/systemd and
1215 * shell-completion/systemd-zsh-completion.zsh too. */
1220 static int parse_argv(int argc, char *argv[]) {
1222 ARG_VERSION = 0x100,
1227 ARG_DOT_FROM_PATTERN,
1233 static const struct option options[] = {
1234 { "help", no_argument, NULL, 'h' },
1235 { "version", no_argument, NULL, ARG_VERSION },
1236 { "order", no_argument, NULL, ARG_ORDER },
1237 { "require", no_argument, NULL, ARG_REQUIRE },
1238 { "user", no_argument, NULL, ARG_USER },
1239 { "system", no_argument, NULL, ARG_SYSTEM },
1240 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1241 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1242 { "fuzz", required_argument, NULL, ARG_FUZZ },
1243 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1244 { "host", required_argument, NULL, 'H' },
1245 { "machine", required_argument, NULL, 'M' },
1254 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
1262 puts(PACKAGE_STRING);
1263 puts(SYSTEMD_FEATURES);
1275 arg_dot = DEP_ORDER;
1279 arg_dot = DEP_REQUIRE;
1282 case ARG_DOT_FROM_PATTERN:
1283 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1288 case ARG_DOT_TO_PATTERN:
1289 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1295 r = parse_sec(optarg, &arg_fuzz);
1301 arg_no_pager = true;
1305 arg_transport = BUS_TRANSPORT_REMOTE;
1310 arg_transport = BUS_TRANSPORT_CONTAINER;
1318 assert_not_reached("Unhandled option");
1325 int main(int argc, char *argv[]) {
1326 _cleanup_bus_unref_ sd_bus *bus = NULL;
1329 setlocale(LC_ALL, "");
1330 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1331 log_parse_environment();
1334 r = parse_argv(argc, argv);
1338 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1340 log_error("Failed to create bus connection: %s", strerror(-r));
1344 if (!argv[optind] || streq(argv[optind], "time"))
1345 r = analyze_time(bus);
1346 else if (streq(argv[optind], "blame"))
1347 r = analyze_blame(bus);
1348 else if (streq(argv[optind], "critical-chain"))
1349 r = analyze_critical_chain(bus, argv+optind+1);
1350 else if (streq(argv[optind], "plot"))
1351 r = analyze_plot(bus);
1352 else if (streq(argv[optind], "dot"))
1353 r = dot(bus, argv+optind+1);
1354 else if (streq(argv[optind], "dump"))
1355 r = dump(bus, argv+optind+1);
1356 else if (streq(argv[optind], "set-log-level"))
1357 r = set_log_level(bus, argv+optind+1);
1359 log_error("Unknown operation '%s'.", argv[optind]);
1364 strv_free(arg_dot_from_patterns);
1365 strv_free(arg_dot_to_patterns);
1367 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;