1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010-2013 Lennart Poettering
7 Copyright 2013 Simon Peeters
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include <sys/utsname.h>
32 #include "bus-error.h"
40 #include "unit-name.h"
45 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
48 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
50 #define svg(...) printf(__VA_ARGS__)
52 #define svg_bar(class, x1, x2, y) \
53 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
55 SCALE_X * (x1), SCALE_Y * (y), \
56 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
58 #define svg_text(b, x, y, format, ...) \
60 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
61 svg(format, ## __VA_ARGS__); \
65 static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
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;
80 usec_t kernel_done_time;
82 usec_t userspace_time;
84 usec_t generators_start_time;
85 usec_t generators_finish_time;
86 usec_t unitsload_start_time;
87 usec_t unitsload_finish_time;
92 const char *description;
93 const char *load_state;
94 const char *active_state;
95 const char *sub_state;
96 const char *following;
97 const char *unit_path;
100 const char *job_path;
112 static void pager_open_if_enabled(void) {
120 static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
121 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
124 r = sd_bus_get_property_trivial(
126 "org.freedesktop.systemd1",
134 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
141 static int compare_unit_time(const void *a, const void *b) {
142 return compare(((struct unit_times *)b)->time,
143 ((struct unit_times *)a)->time);
146 static int compare_unit_start(const void *a, const void *b) {
147 return compare(((struct unit_times *)a)->activating,
148 ((struct unit_times *)b)->activating);
151 static int get_os_name(char **_n) {
155 r = parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &n, NULL);
166 static void free_unit_times(struct unit_times *t, unsigned n) {
167 struct unit_times *p;
169 for (p = t; p < t + n; p++)
175 static int bus_parse_unit_info(sd_bus_message *message, struct unit_info *u) {
181 r = sd_bus_message_read(message, "(ssssssouso)", &u->id,
192 log_error("Failed to parse message as unit_info.");
199 static int bus_get_unit_property_strv(sd_bus *bus, const char *unit_path, const char *prop, char ***strv) {
200 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
201 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
205 r = sd_bus_get_property(
207 "org.freedesktop.systemd1",
209 "org.freedesktop.systemd1.Unit",
215 log_error("Failed to get unit property: %s %s", prop, bus_error_message(&error, -r));
219 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s");
223 while ((r = sd_bus_message_read(reply, "s", &s)) > 0) {
224 r = strv_extend(strv, s);
234 static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
235 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
236 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
237 int r, c = 0, n_units = 0;
238 struct unit_times *unit_times = NULL;
241 r = sd_bus_call_method(
243 "org.freedesktop.systemd1",
244 "/org/freedesktop/systemd1",
245 "org.freedesktop.systemd1.Manager",
251 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
255 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
259 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
260 struct unit_times *t;
266 struct unit_times *w;
268 n_units = MAX(2*c, 16);
269 w = realloc(unit_times, sizeof(struct unit_times) * n_units);
281 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
283 if (bus_get_uint64_property(bus, u.unit_path,
284 "org.freedesktop.systemd1.Unit",
285 "InactiveExitTimestampMonotonic",
286 &t->activating) < 0 ||
287 bus_get_uint64_property(bus, u.unit_path,
288 "org.freedesktop.systemd1.Unit",
289 "ActiveEnterTimestampMonotonic",
290 &t->activated) < 0 ||
291 bus_get_uint64_property(bus, u.unit_path,
292 "org.freedesktop.systemd1.Unit",
293 "ActiveExitTimestampMonotonic",
294 &t->deactivating) < 0 ||
295 bus_get_uint64_property(bus, u.unit_path,
296 "org.freedesktop.systemd1.Unit",
297 "InactiveEnterTimestampMonotonic",
298 &t->deactivated) < 0) {
303 if (t->activated >= t->activating)
304 t->time = t->activated - t->activating;
305 else if (t->deactivated >= t->activating)
306 t->time = t->deactivated - t->activating;
310 if (t->activating == 0)
313 t->name = strdup(u.id);
314 if (t->name == NULL) {
325 free_unit_times(unit_times, (unsigned) c);
329 static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
330 static struct boot_times times;
331 static bool cached = false;
336 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
338 if (bus_get_uint64_property(bus,
339 "/org/freedesktop/systemd1",
340 "org.freedesktop.systemd1.Manager",
341 "FirmwareTimestampMonotonic",
342 ×.firmware_time) < 0 ||
343 bus_get_uint64_property(bus,
344 "/org/freedesktop/systemd1",
345 "org.freedesktop.systemd1.Manager",
346 "LoaderTimestampMonotonic",
347 ×.loader_time) < 0 ||
348 bus_get_uint64_property(bus,
349 "/org/freedesktop/systemd1",
350 "org.freedesktop.systemd1.Manager",
352 ×.kernel_time) < 0 ||
353 bus_get_uint64_property(bus,
354 "/org/freedesktop/systemd1",
355 "org.freedesktop.systemd1.Manager",
356 "InitRDTimestampMonotonic",
357 ×.initrd_time) < 0 ||
358 bus_get_uint64_property(bus,
359 "/org/freedesktop/systemd1",
360 "org.freedesktop.systemd1.Manager",
361 "UserspaceTimestampMonotonic",
362 ×.userspace_time) < 0 ||
363 bus_get_uint64_property(bus,
364 "/org/freedesktop/systemd1",
365 "org.freedesktop.systemd1.Manager",
366 "FinishTimestampMonotonic",
367 ×.finish_time) < 0 ||
368 bus_get_uint64_property(bus,
369 "/org/freedesktop/systemd1",
370 "org.freedesktop.systemd1.Manager",
371 "GeneratorsStartTimestampMonotonic",
372 ×.generators_start_time) < 0 ||
373 bus_get_uint64_property(bus,
374 "/org/freedesktop/systemd1",
375 "org.freedesktop.systemd1.Manager",
376 "GeneratorsFinishTimestampMonotonic",
377 ×.generators_finish_time) < 0 ||
378 bus_get_uint64_property(bus,
379 "/org/freedesktop/systemd1",
380 "org.freedesktop.systemd1.Manager",
381 "UnitsLoadStartTimestampMonotonic",
382 ×.unitsload_start_time) < 0 ||
383 bus_get_uint64_property(bus,
384 "/org/freedesktop/systemd1",
385 "org.freedesktop.systemd1.Manager",
386 "UnitsLoadFinishTimestampMonotonic",
387 ×.unitsload_finish_time) < 0)
390 if (times.finish_time <= 0) {
391 log_error("Bootup is not yet finished. Please try again later.");
395 if (times.initrd_time)
396 times.kernel_done_time = times.initrd_time;
398 times.kernel_done_time = times.userspace_time;
407 static int pretty_boot_time(sd_bus *bus, char **_buf) {
408 char ts[FORMAT_TIMESPAN_MAX];
409 struct boot_times *t;
410 static char buf[4096];
415 r = acquire_boot_times(bus, &t);
422 size = strpcpyf(&ptr, size, "Startup finished in ");
423 if (t->firmware_time)
424 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
426 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
428 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
429 if (t->initrd_time > 0)
430 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
432 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
433 if (t->kernel_time > 0)
434 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
436 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
446 static void svg_graph_box(double height, double begin, double end) {
449 /* outside box, fill */
450 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
451 SCALE_X * (end - begin), SCALE_Y * height);
453 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
454 /* lines for each second */
455 if (i % 5000000 == 0)
456 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
457 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
458 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
459 else if (i % 1000000 == 0)
460 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
461 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
462 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
464 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
465 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
469 static int analyze_plot(sd_bus *bus) {
470 struct unit_times *times;
471 struct boot_times *boot;
475 _cleanup_free_ char *pretty_times = NULL, *osname = NULL;
476 struct unit_times *u;
478 n = acquire_boot_times(bus, &boot);
482 n = pretty_boot_time(bus, &pretty_times);
486 get_os_name(&osname);
487 assert_se(uname(&name) >= 0);
489 n = acquire_time_data(bus, ×);
493 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
495 width = SCALE_X * (boot->firmware_time + boot->finish_time);
499 if (boot->firmware_time > boot->loader_time)
501 if (boot->loader_time) {
506 if (boot->initrd_time)
508 if (boot->kernel_time)
511 for (u = times; u < times + n; u++) {
512 double text_start, text_width;
514 if (u->activating < boot->userspace_time ||
515 u->activating > boot->finish_time) {
521 /* If the text cannot fit on the left side then
522 * increase the svg width so it fits on the right.
523 * TODO: calculate the text width more accurately */
524 text_width = 8.0 * strlen(u->name);
525 text_start = (boot->firmware_time + u->activating) * SCALE_X;
526 if (text_width > text_start && text_width + text_start > width)
527 width = text_width + text_start;
529 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
530 && u->activated == 0 && u->deactivating == 0)
531 u->activated = u->deactivating = u->deactivated;
532 if (u->activated < u->activating || u->activated > boot->finish_time)
533 u->activated = boot->finish_time;
534 if (u->deactivating < u->activated || u->activated > boot->finish_time)
535 u->deactivating = boot->finish_time;
536 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
537 u->deactivated = boot->finish_time;
541 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
542 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
543 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
545 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
546 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
547 80.0 + width, 150.0 + (m * SCALE_Y) +
548 5 * SCALE_Y /* legend */);
550 /* write some basic info as a comment, including some help */
551 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
552 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
553 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
554 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
555 "<!-- point your browser to this file. -->\n\n"
556 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
559 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
560 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
561 " rect.background { fill: rgb(255,255,255); }\n"
562 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
563 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
564 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
565 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
566 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
567 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
568 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
569 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
570 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
571 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
572 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
573 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
575 " line.sec5 { stroke-width: 2; }\n"
576 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
577 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
578 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
579 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
580 " text.sec { font-size: 10px; }\n"
581 " ]]>\n </style>\n</defs>\n\n");
583 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
584 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
585 svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
586 isempty(osname) ? "Linux" : osname,
587 name.nodename, name.release, name.version, name.machine);
589 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
590 svg_graph_box(m, -boot->firmware_time, boot->finish_time);
592 if (boot->firmware_time) {
593 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
594 svg_text(true, -(double) boot->firmware_time, y, "firmware");
597 if (boot->loader_time) {
598 svg_bar("loader", -(double) boot->loader_time, 0, y);
599 svg_text(true, -(double) boot->loader_time, y, "loader");
602 if (boot->kernel_time) {
603 svg_bar("kernel", 0, boot->kernel_done_time, y);
604 svg_text(true, 0, y, "kernel");
607 if (boot->initrd_time) {
608 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
609 svg_text(true, boot->initrd_time, y, "initrd");
612 svg_bar("active", boot->userspace_time, boot->finish_time, y);
613 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
614 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
615 svg_text(true, boot->userspace_time, y, "systemd");
618 for (u = times; u < times + n; u++) {
619 char ts[FORMAT_TIMESPAN_MAX];
625 svg_bar("activating", u->activating, u->activated, y);
626 svg_bar("active", u->activated, u->deactivating, y);
627 svg_bar("deactivating", u->deactivating, u->deactivated, y);
629 /* place the text on the left if we have passed the half of the svg width */
630 b = u->activating * SCALE_X < width / 2;
632 svg_text(b, u->activating, y, "%s (%s)",
633 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
635 svg_text(b, u->activating, y, "%s", u->name);
641 svg_bar("activating", 0, 300000, y);
642 svg_text(true, 400000, y, "Activating");
644 svg_bar("active", 0, 300000, y);
645 svg_text(true, 400000, y, "Active");
647 svg_bar("deactivating", 0, 300000, y);
648 svg_text(true, 400000, y, "Deactivating");
650 svg_bar("generators", 0, 300000, y);
651 svg_text(true, 400000, y, "Generators");
653 svg_bar("unitsload", 0, 300000, y);
654 svg_text(true, 400000, y, "Loading unit files");
661 free_unit_times(times, (unsigned) n);
666 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
667 bool last, struct unit_times *times, struct boot_times *boot) {
669 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
671 for (i = level; i != 0; i--)
672 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));
674 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
678 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
679 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
680 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
681 else if (times->activated > boot->userspace_time)
682 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
685 } else printf("%s", name);
691 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
692 _cleanup_free_ char *path;
701 path = unit_dbus_path_from_name(name);
705 r = bus_get_unit_property_strv(bus, path, "After", &ret);
714 static Hashmap *unit_times_hashmap;
716 static int list_dependencies_compare(const void *_a, const void *_b) {
717 const char **a = (const char**) _a, **b = (const char**) _b;
718 usec_t usa = 0, usb = 0;
719 struct unit_times *times;
721 times = hashmap_get(unit_times_hashmap, *a);
723 usa = times->activated;
724 times = hashmap_get(unit_times_hashmap, *b);
726 usb = times->activated;
731 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
732 unsigned int branches) {
733 _cleanup_strv_free_ char **deps = NULL;
736 usec_t service_longest = 0;
738 struct unit_times *times;
739 struct boot_times *boot;
741 if(strv_extend(units, name))
744 r = list_dependencies_get_dependencies(bus, name, &deps);
748 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
750 r = acquire_boot_times(bus, &boot);
754 STRV_FOREACH(c, deps) {
755 times = hashmap_get(unit_times_hashmap, *c);
758 && times->activated <= boot->finish_time
759 && (times->activated >= service_longest
760 || service_longest == 0)) {
761 service_longest = times->activated;
766 if (service_longest == 0 )
769 STRV_FOREACH(c, deps) {
770 times = hashmap_get(unit_times_hashmap, *c);
771 if (times && times->activated
772 && times->activated <= boot->finish_time
773 && (service_longest - times->activated) <= arg_fuzz) {
781 STRV_FOREACH(c, deps) {
782 times = hashmap_get(unit_times_hashmap, *c);
785 || times->activated > boot->finish_time
786 || service_longest - times->activated > arg_fuzz)
791 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
795 if (strv_contains(*units, *c)) {
796 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
803 r = list_dependencies_one(bus, *c, level + 1, units,
804 (branches << 1) | (to_print ? 1 : 0));
814 static int list_dependencies(sd_bus *bus, const char *name) {
815 _cleanup_strv_free_ char **units = NULL;
816 char ts[FORMAT_TIMESPAN_MAX];
817 struct unit_times *times;
819 const char *path, *id;
820 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
821 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
822 struct boot_times *boot;
826 path = unit_dbus_path_from_name(name);
830 r = sd_bus_get_property(
832 "org.freedesktop.systemd1",
834 "org.freedesktop.systemd1.Unit",
840 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
844 r = sd_bus_message_read(reply, "s", &id);
846 log_error("Failed to parse reply.");
850 times = hashmap_get(unit_times_hashmap, id);
852 r = acquire_boot_times(bus, &boot);
858 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
859 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
860 else if (times->activated > boot->userspace_time)
861 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
866 return list_dependencies_one(bus, name, 0, &units, 0);
869 static int analyze_critical_chain(sd_bus *bus, char *names[]) {
870 struct unit_times *times;
875 n = acquire_time_data(bus, ×);
879 h = hashmap_new(string_hash_func, string_compare_func);
883 for (i = 0; i < (unsigned)n; i++) {
884 r = hashmap_put(h, times[i].name, ×[i]);
888 unit_times_hashmap = h;
890 pager_open_if_enabled();
892 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
893 "The time the unit takes to start is printed after the \"+\" character.\n");
895 if (!strv_isempty(names)) {
897 STRV_FOREACH(name, names)
898 list_dependencies(bus, *name);
900 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
903 free_unit_times(times, (unsigned) n);
907 static int analyze_blame(sd_bus *bus) {
908 struct unit_times *times;
912 n = acquire_time_data(bus, ×);
916 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
918 pager_open_if_enabled();
920 for (i = 0; i < (unsigned) n; i++) {
921 char ts[FORMAT_TIMESPAN_MAX];
923 if (times[i].time > 0)
924 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
927 free_unit_times(times, (unsigned) n);
931 static int analyze_time(sd_bus *bus) {
932 _cleanup_free_ char *buf = NULL;
935 r = pretty_boot_time(bus, &buf);
943 static int graph_one_property(sd_bus *bus, const struct unit_info *u, const char* prop, const char *color, char* patterns[]) {
944 _cleanup_strv_free_ char **units = NULL;
952 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
957 STRV_FOREACH(unit, units) {
961 if (!strv_isempty(arg_dot_from_patterns)) {
964 STRV_FOREACH(p, arg_dot_from_patterns)
965 if (fnmatch(*p, u->id, 0) == 0) {
974 if (!strv_isempty(arg_dot_to_patterns)) {
977 STRV_FOREACH(p, arg_dot_to_patterns)
978 if (fnmatch(*p, *unit, 0) == 0) {
987 if (!strv_isempty(patterns)) {
990 STRV_FOREACH(p, patterns)
991 if (fnmatch(*p, u->id, 0) == 0 || fnmatch(*p, *unit, 0) == 0) {
999 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1005 static int graph_one(sd_bus *bus, const struct unit_info *u, char *patterns[]) {
1011 if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
1012 r = graph_one_property(bus, u, "After", "green", patterns);
1017 if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
1018 r = graph_one_property(bus, u, "Requires", "black", patterns);
1021 r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns);
1024 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns);
1027 r = graph_one_property(bus, u, "Wants", "grey66", patterns);
1030 r = graph_one_property(bus, u, "Conflicts", "red", patterns);
1033 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns);
1041 static int dot(sd_bus *bus, char* patterns[]) {
1042 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1043 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1047 r = sd_bus_call_method(
1049 "org.freedesktop.systemd1",
1050 "/org/freedesktop/systemd1",
1051 "org.freedesktop.systemd1.Manager",
1057 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
1061 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1065 printf("digraph systemd {\n");
1067 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1068 r = graph_one(bus, &u, patterns);
1075 log_info(" Color legend: black = Requires\n"
1076 " dark blue = Requisite\n"
1077 " dark grey = Wants\n"
1078 " red = Conflicts\n"
1079 " green = After\n");
1082 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1083 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1087 static int dump(sd_bus *bus, char **args) {
1088 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1089 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1090 const char *text = NULL;
1093 if (!strv_isempty(args)) {
1094 log_error("Too many arguments.");
1098 pager_open_if_enabled();
1100 r = sd_bus_call_method(
1102 "org.freedesktop.systemd1",
1103 "/org/freedesktop/systemd1",
1104 "org.freedesktop.systemd1.Manager",
1110 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
1114 r = sd_bus_message_read(reply, "s", &text);
1116 log_error("Failed to parse reply");
1120 fputs(text, stdout);
1124 static int set_log_level(sd_bus *bus, char **args) {
1125 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1132 if (strv_length(args) != 1) {
1133 log_error("This command expects one argument only.");
1139 r = sd_bus_set_property(
1141 "org.freedesktop.systemd1",
1142 "/org/freedesktop/systemd1",
1143 "org.freedesktop.systemd1.Manager",
1149 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1156 static void analyze_help(void) {
1158 pager_open_if_enabled();
1160 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1161 "Process systemd profiling information\n\n"
1162 " -h --help Show this help\n"
1163 " --version Show package version\n"
1164 " --system Connect to system manager\n"
1165 " --user Connect to user service manager\n"
1166 " --order When generating a dependency graph, show only order\n"
1167 " --require When generating a dependency graph, show only requirement\n"
1168 " --from-pattern=GLOB, --to-pattern=GLOB\n"
1169 " When generating a dependency graph, filter only origins\n"
1170 " or destinations, respectively\n"
1171 " --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
1172 " services, which finished TIMESPAN earlier, than the\n"
1173 " latest in the branch. The unit of TIMESPAN is seconds\n"
1174 " unless specified with a different unit, i.e. 50ms\n"
1175 " --no-pager Do not pipe output into a pager\n\n"
1177 " time Print time spent in the kernel before reaching userspace\n"
1178 " blame Print list of running units ordered by time to init\n"
1179 " critical-chain Print a tree of the time critical chain of units\n"
1180 " plot Output SVG graphic showing service initialization\n"
1181 " dot Output dependency graph in dot(1) format\n"
1182 " set-log-level LEVEL Set logging threshold for systemd\n"
1183 " dump Output state serialization of service manager\n",
1184 program_invocation_short_name);
1186 /* When updating this list, including descriptions, apply
1187 * changes to shell-completion/bash/systemd and
1188 * shell-completion/systemd-zsh-completion.zsh too. */
1191 static int parse_argv(int argc, char *argv[]) {
1195 ARG_VERSION = 0x100,
1200 ARG_DOT_FROM_PATTERN,
1206 static const struct option options[] = {
1207 { "help", no_argument, NULL, 'h' },
1208 { "version", no_argument, NULL, ARG_VERSION },
1209 { "order", no_argument, NULL, ARG_ORDER },
1210 { "require", no_argument, NULL, ARG_REQUIRE },
1211 { "user", no_argument, NULL, ARG_USER },
1212 { "system", no_argument, NULL, ARG_SYSTEM },
1213 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1214 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1215 { "fuzz", required_argument, NULL, ARG_FUZZ },
1216 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1217 { NULL, 0, NULL, 0 }
1224 switch (getopt_long(argc, argv, "h", options, NULL)) {
1231 puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES);
1235 arg_scope = UNIT_FILE_USER;
1239 arg_scope = UNIT_FILE_SYSTEM;
1243 arg_dot = DEP_ORDER;
1247 arg_dot = DEP_REQUIRE;
1250 case ARG_DOT_FROM_PATTERN:
1251 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1256 case ARG_DOT_TO_PATTERN:
1257 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1263 r = parse_sec(optarg, &arg_fuzz);
1269 arg_no_pager = true;
1279 assert_not_reached("Unhandled option");
1284 int main(int argc, char *argv[]) {
1285 _cleanup_bus_unref_ sd_bus *bus = NULL;
1288 setlocale(LC_ALL, "");
1289 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1290 log_parse_environment();
1293 r = parse_argv(argc, argv);
1297 if (arg_scope == UNIT_FILE_SYSTEM)
1298 r = sd_bus_open_system(&bus);
1300 r = sd_bus_open_user(&bus);
1303 log_error("Failed to connect to bus: %s", strerror(-r));
1307 if (!argv[optind] || streq(argv[optind], "time"))
1308 r = analyze_time(bus);
1309 else if (streq(argv[optind], "blame"))
1310 r = analyze_blame(bus);
1311 else if (streq(argv[optind], "critical-chain"))
1312 r = analyze_critical_chain(bus, argv+optind+1);
1313 else if (streq(argv[optind], "plot"))
1314 r = analyze_plot(bus);
1315 else if (streq(argv[optind], "dot"))
1316 r = dot(bus, argv+optind+1);
1317 else if (streq(argv[optind], "dump"))
1318 r = dump(bus, argv+optind+1);
1319 else if (streq(argv[optind], "set-log-level"))
1320 r = set_log_level(bus, argv+optind+1);
1322 log_error("Unknown operation '%s'.", argv[optind]);
1327 strv_free(arg_dot_from_patterns);
1328 strv_free(arg_dot_to_patterns);
1330 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;