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/>.
30 #include "bus-error.h"
36 #include "unit-name.h"
40 #include "analyze-verify.h"
42 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
43 #define SCALE_Y (20.0)
45 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
47 #define svg(...) printf(__VA_ARGS__)
49 #define svg_bar(class, x1, x2, y) \
50 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
52 SCALE_X * (x1), SCALE_Y * (y), \
53 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
55 #define svg_text(b, x, y, format, ...) \
57 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
58 svg(format, ## __VA_ARGS__); \
67 static char** arg_dot_from_patterns = NULL;
68 static char** arg_dot_to_patterns = NULL;
69 static usec_t arg_fuzz = 0;
70 static bool arg_no_pager = false;
71 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
72 static char *arg_host = NULL;
73 static bool arg_user = false;
74 static bool arg_man = true;
80 usec_t kernel_done_time;
82 usec_t userspace_time;
84 usec_t security_start_time;
85 usec_t security_finish_time;
86 usec_t generators_start_time;
87 usec_t generators_finish_time;
88 usec_t unitsload_start_time;
89 usec_t unitsload_finish_time;
104 char *kernel_release;
105 char *kernel_version;
106 char *os_pretty_name;
107 char *virtualization;
111 static void pager_open_if_enabled(void) {
119 static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
120 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
129 r = sd_bus_get_property_trivial(
131 "org.freedesktop.systemd1",
139 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
146 static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
147 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
155 r = sd_bus_get_property_strv(
157 "org.freedesktop.systemd1",
159 "org.freedesktop.systemd1.Unit",
164 log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
171 static int compare_unit_time(const void *a, const void *b) {
172 return compare(((struct unit_times *)b)->time,
173 ((struct unit_times *)a)->time);
176 static int compare_unit_start(const void *a, const void *b) {
177 return compare(((struct unit_times *)a)->activating,
178 ((struct unit_times *)b)->activating);
181 static void free_unit_times(struct unit_times *t, unsigned n) {
182 struct unit_times *p;
184 for (p = t; p < t + n; p++)
190 static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
191 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
192 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
194 struct unit_times *unit_times = NULL;
198 r = sd_bus_call_method(
200 "org.freedesktop.systemd1",
201 "/org/freedesktop/systemd1",
202 "org.freedesktop.systemd1.Manager",
207 log_error("Failed to list units: %s", bus_error_message(&error, -r));
211 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
213 bus_log_parse_error(r);
217 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
218 struct unit_times *t;
220 if (!GREEDY_REALLOC(unit_times, size, c+1)) {
228 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
230 if (bus_get_uint64_property(bus, u.unit_path,
231 "org.freedesktop.systemd1.Unit",
232 "InactiveExitTimestampMonotonic",
233 &t->activating) < 0 ||
234 bus_get_uint64_property(bus, u.unit_path,
235 "org.freedesktop.systemd1.Unit",
236 "ActiveEnterTimestampMonotonic",
237 &t->activated) < 0 ||
238 bus_get_uint64_property(bus, u.unit_path,
239 "org.freedesktop.systemd1.Unit",
240 "ActiveExitTimestampMonotonic",
241 &t->deactivating) < 0 ||
242 bus_get_uint64_property(bus, u.unit_path,
243 "org.freedesktop.systemd1.Unit",
244 "InactiveEnterTimestampMonotonic",
245 &t->deactivated) < 0) {
250 if (t->activated >= t->activating)
251 t->time = t->activated - t->activating;
252 else if (t->deactivated >= t->activating)
253 t->time = t->deactivated - t->activating;
257 if (t->activating == 0)
260 t->name = strdup(u.id);
261 if (t->name == NULL) {
268 bus_log_parse_error(r);
277 free_unit_times(unit_times, (unsigned) c);
281 static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
282 static struct boot_times times;
283 static bool cached = false;
288 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
290 if (bus_get_uint64_property(bus,
291 "/org/freedesktop/systemd1",
292 "org.freedesktop.systemd1.Manager",
293 "FirmwareTimestampMonotonic",
294 ×.firmware_time) < 0 ||
295 bus_get_uint64_property(bus,
296 "/org/freedesktop/systemd1",
297 "org.freedesktop.systemd1.Manager",
298 "LoaderTimestampMonotonic",
299 ×.loader_time) < 0 ||
300 bus_get_uint64_property(bus,
301 "/org/freedesktop/systemd1",
302 "org.freedesktop.systemd1.Manager",
304 ×.kernel_time) < 0 ||
305 bus_get_uint64_property(bus,
306 "/org/freedesktop/systemd1",
307 "org.freedesktop.systemd1.Manager",
308 "InitRDTimestampMonotonic",
309 ×.initrd_time) < 0 ||
310 bus_get_uint64_property(bus,
311 "/org/freedesktop/systemd1",
312 "org.freedesktop.systemd1.Manager",
313 "UserspaceTimestampMonotonic",
314 ×.userspace_time) < 0 ||
315 bus_get_uint64_property(bus,
316 "/org/freedesktop/systemd1",
317 "org.freedesktop.systemd1.Manager",
318 "FinishTimestampMonotonic",
319 ×.finish_time) < 0 ||
320 bus_get_uint64_property(bus,
321 "/org/freedesktop/systemd1",
322 "org.freedesktop.systemd1.Manager",
323 "SecurityStartTimestampMonotonic",
324 ×.security_start_time) < 0 ||
325 bus_get_uint64_property(bus,
326 "/org/freedesktop/systemd1",
327 "org.freedesktop.systemd1.Manager",
328 "SecurityFinishTimestampMonotonic",
329 ×.security_finish_time) < 0 ||
330 bus_get_uint64_property(bus,
331 "/org/freedesktop/systemd1",
332 "org.freedesktop.systemd1.Manager",
333 "GeneratorsStartTimestampMonotonic",
334 ×.generators_start_time) < 0 ||
335 bus_get_uint64_property(bus,
336 "/org/freedesktop/systemd1",
337 "org.freedesktop.systemd1.Manager",
338 "GeneratorsFinishTimestampMonotonic",
339 ×.generators_finish_time) < 0 ||
340 bus_get_uint64_property(bus,
341 "/org/freedesktop/systemd1",
342 "org.freedesktop.systemd1.Manager",
343 "UnitsLoadStartTimestampMonotonic",
344 ×.unitsload_start_time) < 0 ||
345 bus_get_uint64_property(bus,
346 "/org/freedesktop/systemd1",
347 "org.freedesktop.systemd1.Manager",
348 "UnitsLoadFinishTimestampMonotonic",
349 ×.unitsload_finish_time) < 0)
352 if (times.finish_time <= 0) {
353 log_error("Bootup is not yet finished. Please try again later.");
357 if (times.initrd_time)
358 times.kernel_done_time = times.initrd_time;
360 times.kernel_done_time = times.userspace_time;
369 static void free_host_info(struct host_info *hi) {
371 free(hi->kernel_name);
372 free(hi->kernel_release);
373 free(hi->kernel_version);
374 free(hi->os_pretty_name);
375 free(hi->virtualization);
376 free(hi->architecture);
380 static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
382 struct host_info *host;
384 static const struct bus_properties_map hostname_map[] = {
385 { "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
386 { "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
387 { "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
388 { "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
389 { "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
393 static const struct bus_properties_map manager_map[] = {
394 { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
395 { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
399 host = new0(struct host_info, 1);
403 r = bus_map_all_properties(bus,
404 "org.freedesktop.hostname1",
405 "/org/freedesktop/hostname1",
411 r = bus_map_all_properties(bus,
412 "org.freedesktop.systemd1",
413 "/org/freedesktop/systemd1",
422 free_host_info(host);
426 static int pretty_boot_time(sd_bus *bus, char **_buf) {
427 char ts[FORMAT_TIMESPAN_MAX];
428 struct boot_times *t;
429 static char buf[4096];
434 r = acquire_boot_times(bus, &t);
441 size = strpcpyf(&ptr, size, "Startup finished in ");
442 if (t->firmware_time)
443 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
445 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
447 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
448 if (t->initrd_time > 0)
449 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
451 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
452 if (t->kernel_time > 0)
453 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
455 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
465 static void svg_graph_box(double height, double begin, double end) {
468 /* outside box, fill */
469 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
470 SCALE_X * (end - begin), SCALE_Y * height);
472 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
473 /* lines for each second */
474 if (i % 5000000 == 0)
475 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
476 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
477 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
478 else if (i % 1000000 == 0)
479 svg(" <line class=\"sec1\" 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);
483 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
484 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
488 static int analyze_plot(sd_bus *bus) {
489 struct unit_times *times;
490 struct boot_times *boot;
491 struct host_info *host = NULL;
494 _cleanup_free_ char *pretty_times = NULL;
495 struct unit_times *u;
497 n = acquire_boot_times(bus, &boot);
501 n = pretty_boot_time(bus, &pretty_times);
505 n = acquire_host_info(bus, &host);
509 n = acquire_time_data(bus, ×);
513 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
515 width = SCALE_X * (boot->firmware_time + boot->finish_time);
519 if (boot->firmware_time > boot->loader_time)
521 if (boot->loader_time) {
526 if (boot->initrd_time)
528 if (boot->kernel_time)
531 for (u = times; u < times + n; u++) {
532 double text_start, text_width;
534 if (u->activating < boot->userspace_time ||
535 u->activating > boot->finish_time) {
541 /* If the text cannot fit on the left side then
542 * increase the svg width so it fits on the right.
543 * TODO: calculate the text width more accurately */
544 text_width = 8.0 * strlen(u->name);
545 text_start = (boot->firmware_time + u->activating) * SCALE_X;
546 if (text_width > text_start && text_width + text_start > width)
547 width = text_width + text_start;
549 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
550 && u->activated == 0 && u->deactivating == 0)
551 u->activated = u->deactivating = u->deactivated;
552 if (u->activated < u->activating || u->activated > boot->finish_time)
553 u->activated = boot->finish_time;
554 if (u->deactivating < u->activated || u->activated > boot->finish_time)
555 u->deactivating = boot->finish_time;
556 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
557 u->deactivated = boot->finish_time;
561 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
562 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
563 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
565 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
566 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
567 80.0 + width, 150.0 + (m * SCALE_Y) +
568 5 * SCALE_Y /* legend */);
570 /* write some basic info as a comment, including some help */
571 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
572 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
573 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
574 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
575 "<!-- point your browser to this file. -->\n\n"
576 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
579 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
580 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
581 " rect.background { fill: rgb(255,255,255); }\n"
582 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
583 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
584 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
585 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
586 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
587 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
588 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
589 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
590 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
591 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
592 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
593 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
594 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
596 " line.sec5 { stroke-width: 2; }\n"
597 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
598 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
599 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
600 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
601 " text.sec { font-size: 10px; }\n"
602 " ]]>\n </style>\n</defs>\n\n");
604 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
605 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
606 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
607 isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
608 isempty(host->hostname) ? "" : host->hostname,
609 isempty(host->kernel_name) ? "" : host->kernel_name,
610 isempty(host->kernel_release) ? "" : host->kernel_release,
611 isempty(host->kernel_version) ? "" : host->kernel_version,
612 isempty(host->architecture) ? "" : host->architecture,
613 isempty(host->virtualization) ? "" : host->virtualization);
615 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
616 svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
618 if (boot->firmware_time) {
619 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
620 svg_text(true, -(double) boot->firmware_time, y, "firmware");
623 if (boot->loader_time) {
624 svg_bar("loader", -(double) boot->loader_time, 0, y);
625 svg_text(true, -(double) boot->loader_time, y, "loader");
628 if (boot->kernel_time) {
629 svg_bar("kernel", 0, boot->kernel_done_time, y);
630 svg_text(true, 0, y, "kernel");
633 if (boot->initrd_time) {
634 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
635 svg_text(true, boot->initrd_time, y, "initrd");
638 svg_bar("active", boot->userspace_time, boot->finish_time, y);
639 svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
640 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
641 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
642 svg_text(true, boot->userspace_time, y, "systemd");
645 for (u = times; u < times + n; u++) {
646 char ts[FORMAT_TIMESPAN_MAX];
652 svg_bar("activating", u->activating, u->activated, y);
653 svg_bar("active", u->activated, u->deactivating, y);
654 svg_bar("deactivating", u->deactivating, u->deactivated, y);
656 /* place the text on the left if we have passed the half of the svg width */
657 b = u->activating * SCALE_X < width / 2;
659 svg_text(b, u->activating, y, "%s (%s)",
660 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
662 svg_text(b, u->activating, y, "%s", u->name);
669 svg("<g transform=\"translate(20,100)\">\n");
671 svg_bar("activating", 0, 300000, y);
672 svg_text(true, 400000, y, "Activating");
674 svg_bar("active", 0, 300000, y);
675 svg_text(true, 400000, y, "Active");
677 svg_bar("deactivating", 0, 300000, y);
678 svg_text(true, 400000, y, "Deactivating");
680 svg_bar("security", 0, 300000, y);
681 svg_text(true, 400000, y, "Setting up security module");
683 svg_bar("generators", 0, 300000, y);
684 svg_text(true, 400000, y, "Generators");
686 svg_bar("unitsload", 0, 300000, y);
687 svg_text(true, 400000, y, "Loading unit files");
694 free_unit_times(times, (unsigned) n);
698 free_host_info(host);
702 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
703 bool last, struct unit_times *times, struct boot_times *boot) {
705 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
707 for (i = level; i != 0; i--)
708 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE));
710 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
714 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
715 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
716 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
717 else if (times->activated > boot->userspace_time)
718 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
728 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
729 _cleanup_free_ char *path = NULL;
735 path = unit_dbus_path_from_name(name);
739 return bus_get_unit_property_strv(bus, path, "After", deps);
742 static Hashmap *unit_times_hashmap;
744 static int list_dependencies_compare(const void *_a, const void *_b) {
745 const char **a = (const char**) _a, **b = (const char**) _b;
746 usec_t usa = 0, usb = 0;
747 struct unit_times *times;
749 times = hashmap_get(unit_times_hashmap, *a);
751 usa = times->activated;
752 times = hashmap_get(unit_times_hashmap, *b);
754 usb = times->activated;
759 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
760 unsigned int branches) {
761 _cleanup_strv_free_ char **deps = NULL;
764 usec_t service_longest = 0;
766 struct unit_times *times;
767 struct boot_times *boot;
769 if (strv_extend(units, name))
772 r = list_dependencies_get_dependencies(bus, name, &deps);
776 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
778 r = acquire_boot_times(bus, &boot);
782 STRV_FOREACH(c, deps) {
783 times = hashmap_get(unit_times_hashmap, *c);
786 && times->activated <= boot->finish_time
787 && (times->activated >= service_longest
788 || service_longest == 0)) {
789 service_longest = times->activated;
794 if (service_longest == 0 )
797 STRV_FOREACH(c, deps) {
798 times = hashmap_get(unit_times_hashmap, *c);
799 if (times && times->activated
800 && times->activated <= boot->finish_time
801 && (service_longest - times->activated) <= arg_fuzz) {
809 STRV_FOREACH(c, deps) {
810 times = hashmap_get(unit_times_hashmap, *c);
813 || times->activated > boot->finish_time
814 || service_longest - times->activated > arg_fuzz)
819 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
823 if (strv_contains(*units, *c)) {
824 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
831 r = list_dependencies_one(bus, *c, level + 1, units,
832 (branches << 1) | (to_print ? 1 : 0));
842 static int list_dependencies(sd_bus *bus, const char *name) {
843 _cleanup_strv_free_ char **units = NULL;
844 char ts[FORMAT_TIMESPAN_MAX];
845 struct unit_times *times;
848 _cleanup_free_ char *path = NULL;
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_ops);
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;
980 match_patterns = strv_fnmatch(patterns, u->id, 0);
982 if (!strv_isempty(arg_dot_from_patterns) &&
984 !strv_fnmatch(arg_dot_from_patterns, u->id, 0))
987 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
991 STRV_FOREACH(unit, units) {
992 bool match_patterns2;
994 match_patterns2 = strv_fnmatch(patterns, *unit, 0);
996 if (!strv_isempty(arg_dot_to_patterns) &&
998 !strv_fnmatch(arg_dot_to_patterns, *unit, 0))
1001 if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
1004 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1010 static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[]) {
1016 if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
1017 r = graph_one_property(bus, u, "After", "green", patterns);
1022 if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
1023 r = graph_one_property(bus, u, "Requires", "black", patterns);
1026 r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns);
1029 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns);
1032 r = graph_one_property(bus, u, "Wants", "grey66", patterns);
1035 r = graph_one_property(bus, u, "Conflicts", "red", patterns);
1038 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns);
1046 static int dot(sd_bus *bus, char* patterns[]) {
1047 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1048 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1052 r = sd_bus_call_method(
1054 "org.freedesktop.systemd1",
1055 "/org/freedesktop/systemd1",
1056 "org.freedesktop.systemd1.Manager",
1062 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1066 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1068 return bus_log_parse_error(r);
1070 printf("digraph systemd {\n");
1072 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1074 r = graph_one(bus, &u, patterns);
1079 return bus_log_parse_error(r);
1083 log_info(" Color legend: black = Requires\n"
1084 " dark blue = Requisite\n"
1085 " dark grey = Wants\n"
1086 " red = Conflicts\n"
1087 " green = After\n");
1090 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1091 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1096 static int dump(sd_bus *bus, char **args) {
1097 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1098 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1099 const char *text = NULL;
1102 if (!strv_isempty(args)) {
1103 log_error("Too many arguments.");
1107 pager_open_if_enabled();
1109 r = sd_bus_call_method(
1111 "org.freedesktop.systemd1",
1112 "/org/freedesktop/systemd1",
1113 "org.freedesktop.systemd1.Manager",
1119 log_error("Failed issue method call: %s", bus_error_message(&error, -r));
1123 r = sd_bus_message_read(reply, "s", &text);
1125 return bus_log_parse_error(r);
1127 fputs(text, stdout);
1131 static int set_log_level(sd_bus *bus, char **args) {
1132 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1138 if (strv_length(args) != 1) {
1139 log_error("This command expects one argument only.");
1143 r = sd_bus_set_property(
1145 "org.freedesktop.systemd1",
1146 "/org/freedesktop/systemd1",
1147 "org.freedesktop.systemd1.Manager",
1153 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1160 static void help(void) {
1162 pager_open_if_enabled();
1164 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1165 "Profile systemd, show unit dependencies, check unit files.\n\n"
1166 " -h --help Show this help\n"
1167 " --version Show package version\n"
1168 " --no-pager Do not pipe output into a pager\n"
1169 " --system Operate on system systemd instance\n"
1170 " --user Operate on user systemd instance\n"
1171 " -H --host=[USER@]HOST Operate on remote host\n"
1172 " -M --machine=CONTAINER Operate on local container\n"
1173 " --order Show only order in the graph\n"
1174 " --require Show only requirement in the graph\n"
1175 " --from-pattern=GLOB Show only origins in the graph\n"
1176 " --to-pattern=GLOB Show only destinations in the graph\n"
1177 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1178 " earlier than the latest in the branch\n"
1179 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1181 " time Print time spent in the kernel\n"
1182 " blame Print list of running units ordered by time to init\n"
1183 " critical-chain Print a tree of the time critical chain of units\n"
1184 " plot Output SVG graphic showing service initialization\n"
1185 " dot Output dependency graph in dot(1) format\n"
1186 " set-log-level LEVEL Set logging threshold for systemd\n"
1187 " dump Output state serialization of service manager\n"
1188 " verify FILE... Check unit files for correctness\n"
1189 , program_invocation_short_name);
1191 /* When updating this list, including descriptions, apply
1192 * changes to shell-completion/bash/systemd-analyze and
1193 * shell-completion/zsh/_systemd-analyze too. */
1196 static int parse_argv(int argc, char *argv[]) {
1198 ARG_VERSION = 0x100,
1203 ARG_DOT_FROM_PATTERN,
1210 static const struct option options[] = {
1211 { "help", no_argument, NULL, 'h' },
1212 { "version", no_argument, NULL, ARG_VERSION },
1213 { "order", no_argument, NULL, ARG_ORDER },
1214 { "require", no_argument, NULL, ARG_REQUIRE },
1215 { "user", no_argument, NULL, ARG_USER },
1216 { "system", no_argument, NULL, ARG_SYSTEM },
1217 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1218 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1219 { "fuzz", required_argument, NULL, ARG_FUZZ },
1220 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1221 { "man", optional_argument, NULL, ARG_MAN },
1222 { "host", required_argument, NULL, 'H' },
1223 { "machine", required_argument, NULL, 'M' },
1232 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
1240 puts(PACKAGE_STRING);
1241 puts(SYSTEMD_FEATURES);
1253 arg_dot = DEP_ORDER;
1257 arg_dot = DEP_REQUIRE;
1260 case ARG_DOT_FROM_PATTERN:
1261 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1266 case ARG_DOT_TO_PATTERN:
1267 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1273 r = parse_sec(optarg, &arg_fuzz);
1279 arg_no_pager = true;
1283 arg_transport = BUS_TRANSPORT_REMOTE;
1288 arg_transport = BUS_TRANSPORT_MACHINE;
1294 r = parse_boolean(optarg);
1296 log_error("Failed to parse --man= argument.");
1310 assert_not_reached("Unhandled option code.");
1313 return 1; /* work to do */
1316 int main(int argc, char *argv[]) {
1319 setlocale(LC_ALL, "");
1320 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1321 log_parse_environment();
1324 r = parse_argv(argc, argv);
1328 if (streq_ptr(argv[optind], "verify"))
1329 r = verify_units(argv+optind+1,
1330 arg_user ? SYSTEMD_USER : SYSTEMD_SYSTEM,
1333 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1335 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1337 log_error_errno(r, "Failed to create bus connection: %m");
1341 if (!argv[optind] || streq(argv[optind], "time"))
1342 r = analyze_time(bus);
1343 else if (streq(argv[optind], "blame"))
1344 r = analyze_blame(bus);
1345 else if (streq(argv[optind], "critical-chain"))
1346 r = analyze_critical_chain(bus, argv+optind+1);
1347 else if (streq(argv[optind], "plot"))
1348 r = analyze_plot(bus);
1349 else if (streq(argv[optind], "dot"))
1350 r = dot(bus, argv+optind+1);
1351 else if (streq(argv[optind], "dump"))
1352 r = dump(bus, argv+optind+1);
1353 else if (streq(argv[optind], "set-log-level"))
1354 r = set_log_level(bus, argv+optind+1);
1356 log_error("Unknown operation '%s'.", argv[optind]);
1362 strv_free(arg_dot_from_patterns);
1363 strv_free(arg_dot_to_patterns);
1365 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;