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);
280 free_unit_times(unit_times, (unsigned) c);
284 static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
285 static struct boot_times times;
286 static bool cached = false;
291 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
293 if (bus_get_uint64_property(bus,
294 "/org/freedesktop/systemd1",
295 "org.freedesktop.systemd1.Manager",
296 "FirmwareTimestampMonotonic",
297 ×.firmware_time) < 0 ||
298 bus_get_uint64_property(bus,
299 "/org/freedesktop/systemd1",
300 "org.freedesktop.systemd1.Manager",
301 "LoaderTimestampMonotonic",
302 ×.loader_time) < 0 ||
303 bus_get_uint64_property(bus,
304 "/org/freedesktop/systemd1",
305 "org.freedesktop.systemd1.Manager",
307 ×.kernel_time) < 0 ||
308 bus_get_uint64_property(bus,
309 "/org/freedesktop/systemd1",
310 "org.freedesktop.systemd1.Manager",
311 "InitRDTimestampMonotonic",
312 ×.initrd_time) < 0 ||
313 bus_get_uint64_property(bus,
314 "/org/freedesktop/systemd1",
315 "org.freedesktop.systemd1.Manager",
316 "UserspaceTimestampMonotonic",
317 ×.userspace_time) < 0 ||
318 bus_get_uint64_property(bus,
319 "/org/freedesktop/systemd1",
320 "org.freedesktop.systemd1.Manager",
321 "FinishTimestampMonotonic",
322 ×.finish_time) < 0 ||
323 bus_get_uint64_property(bus,
324 "/org/freedesktop/systemd1",
325 "org.freedesktop.systemd1.Manager",
326 "SecurityStartTimestampMonotonic",
327 ×.security_start_time) < 0 ||
328 bus_get_uint64_property(bus,
329 "/org/freedesktop/systemd1",
330 "org.freedesktop.systemd1.Manager",
331 "SecurityFinishTimestampMonotonic",
332 ×.security_finish_time) < 0 ||
333 bus_get_uint64_property(bus,
334 "/org/freedesktop/systemd1",
335 "org.freedesktop.systemd1.Manager",
336 "GeneratorsStartTimestampMonotonic",
337 ×.generators_start_time) < 0 ||
338 bus_get_uint64_property(bus,
339 "/org/freedesktop/systemd1",
340 "org.freedesktop.systemd1.Manager",
341 "GeneratorsFinishTimestampMonotonic",
342 ×.generators_finish_time) < 0 ||
343 bus_get_uint64_property(bus,
344 "/org/freedesktop/systemd1",
345 "org.freedesktop.systemd1.Manager",
346 "UnitsLoadStartTimestampMonotonic",
347 ×.unitsload_start_time) < 0 ||
348 bus_get_uint64_property(bus,
349 "/org/freedesktop/systemd1",
350 "org.freedesktop.systemd1.Manager",
351 "UnitsLoadFinishTimestampMonotonic",
352 ×.unitsload_finish_time) < 0)
355 if (times.finish_time <= 0) {
356 log_error("Bootup is not yet finished. Please try again later.");
360 if (times.initrd_time)
361 times.kernel_done_time = times.initrd_time;
363 times.kernel_done_time = times.userspace_time;
372 static void free_host_info(struct host_info *hi) {
374 free(hi->kernel_name);
375 free(hi->kernel_release);
376 free(hi->kernel_version);
377 free(hi->os_pretty_name);
378 free(hi->virtualization);
379 free(hi->architecture);
383 static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
385 struct host_info *host;
387 static const struct bus_properties_map hostname_map[] = {
388 { "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
389 { "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
390 { "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
391 { "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
392 { "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
396 static const struct bus_properties_map manager_map[] = {
397 { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
398 { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
402 host = new0(struct host_info, 1);
406 r = bus_map_all_properties(bus,
407 "org.freedesktop.hostname1",
408 "/org/freedesktop/hostname1",
414 r = bus_map_all_properties(bus,
415 "org.freedesktop.systemd1",
416 "/org/freedesktop/systemd1",
425 free_host_info(host);
429 static int pretty_boot_time(sd_bus *bus, char **_buf) {
430 char ts[FORMAT_TIMESPAN_MAX];
431 struct boot_times *t;
432 static char buf[4096];
437 r = acquire_boot_times(bus, &t);
444 size = strpcpyf(&ptr, size, "Startup finished in ");
445 if (t->firmware_time)
446 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
448 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
450 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
451 if (t->initrd_time > 0)
452 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
454 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
455 if (t->kernel_time > 0)
456 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
458 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
468 static void svg_graph_box(double height, double begin, double end) {
471 /* outside box, fill */
472 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
473 SCALE_X * (end - begin), SCALE_Y * height);
475 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
476 /* lines for each second */
477 if (i % 5000000 == 0)
478 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
479 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
480 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
481 else if (i % 1000000 == 0)
482 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
483 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
484 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
486 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
487 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
491 static int analyze_plot(sd_bus *bus) {
492 struct unit_times *times;
493 struct boot_times *boot;
494 struct host_info *host = NULL;
497 _cleanup_free_ char *pretty_times = NULL;
498 struct unit_times *u;
500 n = acquire_boot_times(bus, &boot);
504 n = pretty_boot_time(bus, &pretty_times);
508 n = acquire_host_info(bus, &host);
512 n = acquire_time_data(bus, ×);
516 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
518 width = SCALE_X * (boot->firmware_time + boot->finish_time);
522 if (boot->firmware_time > boot->loader_time)
524 if (boot->loader_time) {
529 if (boot->initrd_time)
531 if (boot->kernel_time)
534 for (u = times; u < times + n; u++) {
535 double text_start, text_width;
537 if (u->activating < boot->userspace_time ||
538 u->activating > boot->finish_time) {
544 /* If the text cannot fit on the left side then
545 * increase the svg width so it fits on the right.
546 * TODO: calculate the text width more accurately */
547 text_width = 8.0 * strlen(u->name);
548 text_start = (boot->firmware_time + u->activating) * SCALE_X;
549 if (text_width > text_start && text_width + text_start > width)
550 width = text_width + text_start;
552 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
553 && u->activated == 0 && u->deactivating == 0)
554 u->activated = u->deactivating = u->deactivated;
555 if (u->activated < u->activating || u->activated > boot->finish_time)
556 u->activated = boot->finish_time;
557 if (u->deactivating < u->activated || u->activated > boot->finish_time)
558 u->deactivating = boot->finish_time;
559 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
560 u->deactivated = boot->finish_time;
564 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
565 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
566 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
568 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
569 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
570 80.0 + width, 150.0 + (m * SCALE_Y) +
571 5 * SCALE_Y /* legend */);
573 /* write some basic info as a comment, including some help */
574 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
575 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
576 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
577 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
578 "<!-- point your browser to this file. -->\n\n"
579 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
582 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
583 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
584 " rect.background { fill: rgb(255,255,255); }\n"
585 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
586 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
587 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
588 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
589 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
590 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
591 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
592 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
593 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
594 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
595 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
596 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
597 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
599 " line.sec5 { stroke-width: 2; }\n"
600 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
601 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
602 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
603 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
604 " text.sec { font-size: 10px; }\n"
605 " ]]>\n </style>\n</defs>\n\n");
607 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
608 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
609 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
610 isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
611 isempty(host->hostname) ? "" : host->hostname,
612 isempty(host->kernel_name) ? "" : host->kernel_name,
613 isempty(host->kernel_release) ? "" : host->kernel_release,
614 isempty(host->kernel_version) ? "" : host->kernel_version,
615 isempty(host->architecture) ? "" : host->architecture,
616 isempty(host->virtualization) ? "" : host->virtualization);
618 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
619 svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
621 if (boot->firmware_time) {
622 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
623 svg_text(true, -(double) boot->firmware_time, y, "firmware");
626 if (boot->loader_time) {
627 svg_bar("loader", -(double) boot->loader_time, 0, y);
628 svg_text(true, -(double) boot->loader_time, y, "loader");
631 if (boot->kernel_time) {
632 svg_bar("kernel", 0, boot->kernel_done_time, y);
633 svg_text(true, 0, y, "kernel");
636 if (boot->initrd_time) {
637 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
638 svg_text(true, boot->initrd_time, y, "initrd");
641 svg_bar("active", boot->userspace_time, boot->finish_time, y);
642 svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
643 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
644 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
645 svg_text(true, boot->userspace_time, y, "systemd");
648 for (u = times; u < times + n; u++) {
649 char ts[FORMAT_TIMESPAN_MAX];
655 svg_bar("activating", u->activating, u->activated, y);
656 svg_bar("active", u->activated, u->deactivating, y);
657 svg_bar("deactivating", u->deactivating, u->deactivated, y);
659 /* place the text on the left if we have passed the half of the svg width */
660 b = u->activating * SCALE_X < width / 2;
662 svg_text(b, u->activating, y, "%s (%s)",
663 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
665 svg_text(b, u->activating, y, "%s", u->name);
672 svg("<g transform=\"translate(20,100)\">\n");
674 svg_bar("activating", 0, 300000, y);
675 svg_text(true, 400000, y, "Activating");
677 svg_bar("active", 0, 300000, y);
678 svg_text(true, 400000, y, "Active");
680 svg_bar("deactivating", 0, 300000, y);
681 svg_text(true, 400000, y, "Deactivating");
683 svg_bar("security", 0, 300000, y);
684 svg_text(true, 400000, y, "Setting up security module");
686 svg_bar("generators", 0, 300000, y);
687 svg_text(true, 400000, y, "Generators");
689 svg_bar("unitsload", 0, 300000, y);
690 svg_text(true, 400000, y, "Loading unit files");
697 free_unit_times(times, (unsigned) n);
701 free_host_info(host);
705 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
706 bool last, struct unit_times *times, struct boot_times *boot) {
708 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
710 for (i = level; i != 0; i--)
711 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE));
713 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
717 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
718 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
719 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
720 else if (times->activated > boot->userspace_time)
721 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
731 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
732 _cleanup_free_ char *path = NULL;
738 path = unit_dbus_path_from_name(name);
742 return bus_get_unit_property_strv(bus, path, "After", deps);
745 static Hashmap *unit_times_hashmap;
747 static int list_dependencies_compare(const void *_a, const void *_b) {
748 const char **a = (const char**) _a, **b = (const char**) _b;
749 usec_t usa = 0, usb = 0;
750 struct unit_times *times;
752 times = hashmap_get(unit_times_hashmap, *a);
754 usa = times->activated;
755 times = hashmap_get(unit_times_hashmap, *b);
757 usb = times->activated;
762 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
763 unsigned int branches) {
764 _cleanup_strv_free_ char **deps = NULL;
767 usec_t service_longest = 0;
769 struct unit_times *times;
770 struct boot_times *boot;
772 if (strv_extend(units, name))
775 r = list_dependencies_get_dependencies(bus, name, &deps);
779 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
781 r = acquire_boot_times(bus, &boot);
785 STRV_FOREACH(c, deps) {
786 times = hashmap_get(unit_times_hashmap, *c);
789 && times->activated <= boot->finish_time
790 && (times->activated >= service_longest
791 || service_longest == 0)) {
792 service_longest = times->activated;
797 if (service_longest == 0 )
800 STRV_FOREACH(c, deps) {
801 times = hashmap_get(unit_times_hashmap, *c);
802 if (times && times->activated
803 && times->activated <= boot->finish_time
804 && (service_longest - times->activated) <= arg_fuzz) {
812 STRV_FOREACH(c, deps) {
813 times = hashmap_get(unit_times_hashmap, *c);
816 || times->activated > boot->finish_time
817 || service_longest - times->activated > arg_fuzz)
822 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
826 if (strv_contains(*units, *c)) {
827 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
834 r = list_dependencies_one(bus, *c, level + 1, units,
835 (branches << 1) | (to_print ? 1 : 0));
845 static int list_dependencies(sd_bus *bus, const char *name) {
846 _cleanup_strv_free_ char **units = NULL;
847 char ts[FORMAT_TIMESPAN_MAX];
848 struct unit_times *times;
850 const char *path, *id;
851 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
852 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
853 struct boot_times *boot;
857 path = unit_dbus_path_from_name(name);
861 r = sd_bus_get_property(
863 "org.freedesktop.systemd1",
865 "org.freedesktop.systemd1.Unit",
871 log_error("Failed to get ID: %s", bus_error_message(&error, -r));
875 r = sd_bus_message_read(reply, "s", &id);
877 return bus_log_parse_error(r);
879 times = hashmap_get(unit_times_hashmap, id);
881 r = acquire_boot_times(bus, &boot);
887 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
888 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
889 else if (times->activated > boot->userspace_time)
890 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
895 return list_dependencies_one(bus, name, 0, &units, 0);
898 static int analyze_critical_chain(sd_bus *bus, char *names[]) {
899 struct unit_times *times;
904 n = acquire_time_data(bus, ×);
908 h = hashmap_new(string_hash_func, string_compare_func);
912 for (i = 0; i < (unsigned)n; i++) {
913 r = hashmap_put(h, times[i].name, ×[i]);
917 unit_times_hashmap = h;
919 pager_open_if_enabled();
921 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
922 "The time the unit takes to start is printed after the \"+\" character.\n");
924 if (!strv_isempty(names)) {
926 STRV_FOREACH(name, names)
927 list_dependencies(bus, *name);
929 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
932 free_unit_times(times, (unsigned) n);
936 static int analyze_blame(sd_bus *bus) {
937 struct unit_times *times;
941 n = acquire_time_data(bus, ×);
945 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
947 pager_open_if_enabled();
949 for (i = 0; i < (unsigned) n; i++) {
950 char ts[FORMAT_TIMESPAN_MAX];
952 if (times[i].time > 0)
953 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
956 free_unit_times(times, (unsigned) n);
960 static int analyze_time(sd_bus *bus) {
961 _cleanup_free_ char *buf = NULL;
964 r = pretty_boot_time(bus, &buf);
972 static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[]) {
973 _cleanup_strv_free_ char **units = NULL;
981 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
985 STRV_FOREACH(unit, units) {
989 if (!strv_isempty(arg_dot_from_patterns)) {
992 STRV_FOREACH(p, arg_dot_from_patterns)
993 if (fnmatch(*p, u->id, 0) == 0) {
1002 if (!strv_isempty(arg_dot_to_patterns)) {
1003 match_found = false;
1005 STRV_FOREACH(p, arg_dot_to_patterns)
1006 if (fnmatch(*p, *unit, 0) == 0) {
1015 if (!strv_isempty(patterns)) {
1016 match_found = false;
1018 STRV_FOREACH(p, patterns)
1019 if (fnmatch(*p, u->id, 0) == 0 || fnmatch(*p, *unit, 0) == 0) {
1027 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1033 static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[]) {
1039 if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
1040 r = graph_one_property(bus, u, "After", "green", patterns);
1045 if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
1046 r = graph_one_property(bus, u, "Requires", "black", patterns);
1049 r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns);
1052 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns);
1055 r = graph_one_property(bus, u, "Wants", "grey66", patterns);
1058 r = graph_one_property(bus, u, "Conflicts", "red", patterns);
1061 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns);
1069 static int dot(sd_bus *bus, char* patterns[]) {
1070 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1071 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1075 r = sd_bus_call_method(
1077 "org.freedesktop.systemd1",
1078 "/org/freedesktop/systemd1",
1079 "org.freedesktop.systemd1.Manager",
1085 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1089 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1091 return bus_log_parse_error(r);
1093 printf("digraph systemd {\n");
1095 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1097 r = graph_one(bus, &u, patterns);
1102 return bus_log_parse_error(r);
1106 log_info(" Color legend: black = Requires\n"
1107 " dark blue = Requisite\n"
1108 " dark grey = Wants\n"
1109 " red = Conflicts\n"
1110 " green = After\n");
1113 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1114 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1119 static int dump(sd_bus *bus, char **args) {
1120 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1121 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1122 const char *text = NULL;
1125 if (!strv_isempty(args)) {
1126 log_error("Too many arguments.");
1130 pager_open_if_enabled();
1132 r = sd_bus_call_method(
1134 "org.freedesktop.systemd1",
1135 "/org/freedesktop/systemd1",
1136 "org.freedesktop.systemd1.Manager",
1142 log_error("Failed issue method call: %s", bus_error_message(&error, -r));
1146 r = sd_bus_message_read(reply, "s", &text);
1148 return bus_log_parse_error(r);
1150 fputs(text, stdout);
1154 static int set_log_level(sd_bus *bus, char **args) {
1155 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1161 if (strv_length(args) != 1) {
1162 log_error("This command expects one argument only.");
1166 r = sd_bus_set_property(
1168 "org.freedesktop.systemd1",
1169 "/org/freedesktop/systemd1",
1170 "org.freedesktop.systemd1.Manager",
1176 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1183 static void help(void) {
1185 pager_open_if_enabled();
1187 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1188 "Profile systemd, show unit dependencies, check unit files.\n\n"
1189 " -h --help Show this help\n"
1190 " --version Show package version\n"
1191 " --no-pager Do not pipe output into a pager\n"
1192 " --system Operate on system systemd instance\n"
1193 " --user Operate on user systemd instance\n"
1194 " -H --host=[USER@]HOST Operate on remote host\n"
1195 " -M --machine=CONTAINER Operate on local container\n"
1196 " --order When generating a dependency graph, show only order\n"
1197 " --require When generating a dependency graph, show only requirement\n"
1198 " --from-pattern=GLOB, --to-pattern=GLOB\n"
1199 " When generating a dependency graph, filter only origins\n"
1200 " or destinations, respectively\n"
1201 " --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
1202 " services, which finished TIMESPAN earlier, than the\n"
1203 " latest in the branch. The unit of TIMESPAN is seconds\n"
1204 " unless specified with a different unit, i.e. 50ms\n"
1205 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1207 " time Print time spent in the kernel before reaching userspace\n"
1208 " blame Print list of running units ordered by time to init\n"
1209 " critical-chain Print a tree of the time critical chain of units\n"
1210 " plot Output SVG graphic showing service initialization\n"
1211 " dot Output dependency graph in dot(1) format\n"
1212 " set-log-level LEVEL Set logging threshold for systemd\n"
1213 " dump Output state serialization of service manager\n"
1214 " verify FILE... Check unit files for correctness\n"
1215 , program_invocation_short_name);
1217 /* When updating this list, including descriptions, apply
1218 * changes to shell-completion/bash/systemd-analyze and
1219 * shell-completion/zsh/_systemd-analyze too. */
1222 static int parse_argv(int argc, char *argv[]) {
1224 ARG_VERSION = 0x100,
1229 ARG_DOT_FROM_PATTERN,
1236 static const struct option options[] = {
1237 { "help", no_argument, NULL, 'h' },
1238 { "version", no_argument, NULL, ARG_VERSION },
1239 { "order", no_argument, NULL, ARG_ORDER },
1240 { "require", no_argument, NULL, ARG_REQUIRE },
1241 { "user", no_argument, NULL, ARG_USER },
1242 { "system", no_argument, NULL, ARG_SYSTEM },
1243 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1244 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1245 { "fuzz", required_argument, NULL, ARG_FUZZ },
1246 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1247 { "man", optional_argument, NULL, ARG_MAN },
1248 { "host", required_argument, NULL, 'H' },
1249 { "machine", required_argument, NULL, 'M' },
1258 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
1266 puts(PACKAGE_STRING);
1267 puts(SYSTEMD_FEATURES);
1279 arg_dot = DEP_ORDER;
1283 arg_dot = DEP_REQUIRE;
1286 case ARG_DOT_FROM_PATTERN:
1287 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1292 case ARG_DOT_TO_PATTERN:
1293 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1299 r = parse_sec(optarg, &arg_fuzz);
1305 arg_no_pager = true;
1309 arg_transport = BUS_TRANSPORT_REMOTE;
1314 arg_transport = BUS_TRANSPORT_CONTAINER;
1320 r = parse_boolean(optarg);
1322 log_error("Failed to parse --man= argument.");
1336 assert_not_reached("Unhandled option code.");
1339 return 1; /* work to do */
1342 int main(int argc, char *argv[]) {
1345 setlocale(LC_ALL, "");
1346 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1347 log_parse_environment();
1350 r = parse_argv(argc, argv);
1354 if (streq_ptr(argv[optind], "verify"))
1355 r = verify_units(argv+optind+1,
1356 arg_user ? SYSTEMD_USER : SYSTEMD_SYSTEM,
1359 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1361 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1363 log_error("Failed to create bus connection: %s", strerror(-r));
1367 if (!argv[optind] || streq(argv[optind], "time"))
1368 r = analyze_time(bus);
1369 else if (streq(argv[optind], "blame"))
1370 r = analyze_blame(bus);
1371 else if (streq(argv[optind], "critical-chain"))
1372 r = analyze_critical_chain(bus, argv+optind+1);
1373 else if (streq(argv[optind], "plot"))
1374 r = analyze_plot(bus);
1375 else if (streq(argv[optind], "dot"))
1376 r = dot(bus, argv+optind+1);
1377 else if (streq(argv[optind], "dump"))
1378 r = dump(bus, argv+optind+1);
1379 else if (streq(argv[optind], "set-log-level"))
1380 r = set_log_level(bus, argv+optind+1);
1382 log_error("Unknown operation '%s'.", argv[optind]);
1388 strv_free(arg_dot_from_patterns);
1389 strv_free(arg_dot_to_patterns);
1391 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;