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)->ixt,
148 ((struct unit_times *)b)->ixt);
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",
287 bus_get_uint64_property(bus, u.unit_path,
288 "org.freedesktop.systemd1.Unit",
289 "ActiveEnterTimestampMonotonic",
291 bus_get_uint64_property(bus, u.unit_path,
292 "org.freedesktop.systemd1.Unit",
293 "ActiveExitTimestampMonotonic",
295 bus_get_uint64_property(bus, u.unit_path,
296 "org.freedesktop.systemd1.Unit",
297 "InactiveEnterTimestampMonotonic",
303 if (t->aet >= t->ixt)
304 t->time = t->aet - t->ixt;
305 else if (t->iet >= t->ixt)
306 t->time = t->iet - t->ixt;
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++) {
514 if (u->ixt < boot->userspace_time ||
515 u->ixt > boot->finish_time) {
520 len = ((boot->firmware_time + u->ixt) * SCALE_X)
521 + (10.0 * strlen(u->name));
525 if (u->iet > u->ixt && u->iet <= boot->finish_time
526 && u->aet == 0 && u->axt == 0)
527 u->aet = u->axt = u->iet;
528 if (u->aet < u->ixt || u->aet > boot->finish_time)
529 u->aet = boot->finish_time;
530 if (u->axt < u->aet || u->aet > boot->finish_time)
531 u->axt = boot->finish_time;
532 if (u->iet < u->axt || u->iet > boot->finish_time)
533 u->iet = boot->finish_time;
537 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
538 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
539 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
541 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
542 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
543 80.0 + width, 150.0 + (m * SCALE_Y) +
544 5 * SCALE_Y /* legend */);
546 /* write some basic info as a comment, including some help */
547 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
548 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
549 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
550 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
551 "<!-- point your browser to this file. -->\n\n"
552 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
555 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
556 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
557 " rect.background { fill: rgb(255,255,255); }\n"
558 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
559 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
560 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
561 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
562 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
563 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
564 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
565 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
566 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
567 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
568 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
569 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
571 " line.sec5 { stroke-width: 2; }\n"
572 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
573 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
574 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
575 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
576 " text.sec { font-size: 10px; }\n"
577 " ]]>\n </style>\n</defs>\n\n");
579 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
580 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
581 svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
582 isempty(osname) ? "Linux" : osname,
583 name.nodename, name.release, name.version, name.machine);
585 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
586 svg_graph_box(m, -boot->firmware_time, boot->finish_time);
588 if (boot->firmware_time) {
589 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
590 svg_text(true, -(double) boot->firmware_time, y, "firmware");
593 if (boot->loader_time) {
594 svg_bar("loader", -(double) boot->loader_time, 0, y);
595 svg_text(true, -(double) boot->loader_time, y, "loader");
598 if (boot->kernel_time) {
599 svg_bar("kernel", 0, boot->kernel_done_time, y);
600 svg_text(true, 0, y, "kernel");
603 if (boot->initrd_time) {
604 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
605 svg_text(true, boot->initrd_time, y, "initrd");
608 svg_bar("active", boot->userspace_time, boot->finish_time, y);
609 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
610 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
611 svg_text("left", boot->userspace_time, y, "systemd");
614 for (u = times; u < times + n; u++) {
615 char ts[FORMAT_TIMESPAN_MAX];
621 svg_bar("activating", u->ixt, u->aet, y);
622 svg_bar("active", u->aet, u->axt, y);
623 svg_bar("deactivating", u->axt, u->iet, y);
625 b = u->ixt * SCALE_X > width * 2 / 3;
627 svg_text(b, u->ixt, y, "%s (%s)",
628 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
630 svg_text(b, u->ixt, y, "%s", u->name);
636 svg_bar("activating", 0, 300000, y);
637 svg_text("right", 400000, y, "Activating");
639 svg_bar("active", 0, 300000, y);
640 svg_text("right", 400000, y, "Active");
642 svg_bar("deactivating", 0, 300000, y);
643 svg_text("right", 400000, y, "Deactivating");
645 svg_bar("generators", 0, 300000, y);
646 svg_text("right", 400000, y, "Generators");
648 svg_bar("unitsload", 0, 300000, y);
649 svg_text("right", 400000, y, "Loading unit files");
656 free_unit_times(times, (unsigned) n);
661 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
662 bool last, struct unit_times *times, struct boot_times *boot) {
664 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
666 for (i = level; i != 0; i--)
667 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));
669 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
673 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
674 format_timespan(ts, sizeof(ts), times->ixt - boot->userspace_time, USEC_PER_MSEC),
675 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
676 else if (times->aet > boot->userspace_time)
677 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->aet - boot->userspace_time, USEC_PER_MSEC));
680 } else printf("%s", name);
686 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
687 _cleanup_free_ char *path;
696 path = unit_dbus_path_from_name(name);
700 r = bus_get_unit_property_strv(bus, path, "After", &ret);
709 static Hashmap *unit_times_hashmap;
711 static int list_dependencies_compare(const void *_a, const void *_b) {
712 const char **a = (const char**) _a, **b = (const char**) _b;
713 usec_t usa = 0, usb = 0;
714 struct unit_times *times;
716 times = hashmap_get(unit_times_hashmap, *a);
719 times = hashmap_get(unit_times_hashmap, *b);
726 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
727 unsigned int branches) {
728 _cleanup_strv_free_ char **deps = NULL;
731 usec_t service_longest = 0;
733 struct unit_times *times;
734 struct boot_times *boot;
736 if(strv_extend(units, name))
739 r = list_dependencies_get_dependencies(bus, name, &deps);
743 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
745 r = acquire_boot_times(bus, &boot);
749 STRV_FOREACH(c, deps) {
750 times = hashmap_get(unit_times_hashmap, *c);
753 && times->aet <= boot->finish_time
754 && (times->aet >= service_longest
755 || service_longest == 0)) {
756 service_longest = times->aet;
761 if (service_longest == 0 )
764 STRV_FOREACH(c, deps) {
765 times = hashmap_get(unit_times_hashmap, *c);
766 if (times && times->aet
767 && times->aet <= boot->finish_time
768 && (service_longest - times->aet) <= arg_fuzz) {
776 STRV_FOREACH(c, deps) {
777 times = hashmap_get(unit_times_hashmap, *c);
780 || times->aet > boot->finish_time
781 || service_longest - times->aet > arg_fuzz)
786 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
790 if (strv_contains(*units, *c)) {
791 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
798 r = list_dependencies_one(bus, *c, level + 1, units,
799 (branches << 1) | (to_print ? 1 : 0));
809 static int list_dependencies(sd_bus *bus, const char *name) {
810 _cleanup_strv_free_ char **units = NULL;
811 char ts[FORMAT_TIMESPAN_MAX];
812 struct unit_times *times;
814 const char *path, *id;
815 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
816 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
817 struct boot_times *boot;
821 path = unit_dbus_path_from_name(name);
825 r = sd_bus_get_property(
827 "org.freedesktop.systemd1",
829 "org.freedesktop.systemd1.Unit",
835 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
839 r = sd_bus_message_read(reply, "s", &id);
841 log_error("Failed to parse reply.");
845 times = hashmap_get(unit_times_hashmap, id);
847 r = acquire_boot_times(bus, &boot);
853 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
854 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
855 else if (times->aet > boot->userspace_time)
856 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->aet - boot->userspace_time, USEC_PER_MSEC));
861 return list_dependencies_one(bus, name, 0, &units, 0);
864 static int analyze_critical_chain(sd_bus *bus, char *names[]) {
865 struct unit_times *times;
870 n = acquire_time_data(bus, ×);
874 h = hashmap_new(string_hash_func, string_compare_func);
878 for (i = 0; i < (unsigned)n; i++) {
879 r = hashmap_put(h, times[i].name, ×[i]);
883 unit_times_hashmap = h;
885 pager_open_if_enabled();
887 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
888 "The time the unit takes to start is printed after the \"+\" character.\n");
890 if (!strv_isempty(names)) {
892 STRV_FOREACH(name, names)
893 list_dependencies(bus, *name);
895 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
898 free_unit_times(times, (unsigned) n);
902 static int analyze_blame(sd_bus *bus) {
903 struct unit_times *times;
907 n = acquire_time_data(bus, ×);
911 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
913 pager_open_if_enabled();
915 for (i = 0; i < (unsigned) n; i++) {
916 char ts[FORMAT_TIMESPAN_MAX];
918 if (times[i].time > 0)
919 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
922 free_unit_times(times, (unsigned) n);
926 static int analyze_time(sd_bus *bus) {
927 _cleanup_free_ char *buf = NULL;
930 r = pretty_boot_time(bus, &buf);
938 static int graph_one_property(sd_bus *bus, const struct unit_info *u, const char* prop, const char *color, char* patterns[]) {
939 _cleanup_strv_free_ char **units = NULL;
947 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
952 STRV_FOREACH(unit, units) {
956 if (!strv_isempty(arg_dot_from_patterns)) {
959 STRV_FOREACH(p, arg_dot_from_patterns)
960 if (fnmatch(*p, u->id, 0) == 0) {
969 if (!strv_isempty(arg_dot_to_patterns)) {
972 STRV_FOREACH(p, arg_dot_to_patterns)
973 if (fnmatch(*p, *unit, 0) == 0) {
982 if (!strv_isempty(patterns)) {
985 STRV_FOREACH(p, patterns)
986 if (fnmatch(*p, u->id, 0) == 0 || fnmatch(*p, *unit, 0) == 0) {
994 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1000 static int graph_one(sd_bus *bus, const struct unit_info *u, char *patterns[]) {
1006 if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
1007 r = graph_one_property(bus, u, "After", "green", patterns);
1012 if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
1013 r = graph_one_property(bus, u, "Requires", "black", patterns);
1016 r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns);
1019 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns);
1022 r = graph_one_property(bus, u, "Wants", "grey66", patterns);
1025 r = graph_one_property(bus, u, "Conflicts", "red", patterns);
1028 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns);
1036 static int dot(sd_bus *bus, char* patterns[]) {
1037 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1038 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1042 r = sd_bus_call_method(
1044 "org.freedesktop.systemd1",
1045 "/org/freedesktop/systemd1",
1046 "org.freedesktop.systemd1.Manager",
1052 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
1056 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1060 printf("digraph systemd {\n");
1062 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1063 r = graph_one(bus, &u, patterns);
1070 log_info(" Color legend: black = Requires\n"
1071 " dark blue = Requisite\n"
1072 " dark grey = Wants\n"
1073 " red = Conflicts\n"
1074 " green = After\n");
1077 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1078 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1082 static int dump(sd_bus *bus, char **args) {
1083 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1084 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1085 const char *text = NULL;
1088 if (!strv_isempty(args)) {
1089 log_error("Too many arguments.");
1093 pager_open_if_enabled();
1095 r = sd_bus_call_method(
1097 "org.freedesktop.systemd1",
1098 "/org/freedesktop/systemd1",
1099 "org.freedesktop.systemd1.Manager",
1105 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
1109 r = sd_bus_message_read(reply, "s", &text);
1111 log_error("Failed to parse reply");
1115 fputs(text, stdout);
1119 static int set_log_level(sd_bus *bus, char **args) {
1120 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1127 if (strv_length(args) != 1) {
1128 log_error("This command expects one argument only.");
1134 r = sd_bus_set_property(
1136 "org.freedesktop.systemd1",
1137 "/org/freedesktop/systemd1",
1138 "org.freedesktop.systemd1.Manager",
1144 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1151 static void analyze_help(void) {
1153 pager_open_if_enabled();
1155 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1156 "Process systemd profiling information\n\n"
1157 " -h --help Show this help\n"
1158 " --version Show package version\n"
1159 " --system Connect to system manager\n"
1160 " --user Connect to user service manager\n"
1161 " --order When generating a dependency graph, show only order\n"
1162 " --require When generating a dependency graph, show only requirement\n"
1163 " --from-pattern=GLOB, --to-pattern=GLOB\n"
1164 " When generating a dependency graph, filter only origins\n"
1165 " or destinations, respectively\n"
1166 " --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
1167 " services, which finished TIMESPAN earlier, than the\n"
1168 " latest in the branch. The unit of TIMESPAN is seconds\n"
1169 " unless specified with a different unit, i.e. 50ms\n"
1170 " --no-pager Do not pipe output into a pager\n\n"
1172 " time Print time spent in the kernel before reaching userspace\n"
1173 " blame Print list of running units ordered by time to init\n"
1174 " critical-chain Print a tree of the time critical chain of units\n"
1175 " plot Output SVG graphic showing service initialization\n"
1176 " dot Output dependency graph in dot(1) format\n"
1177 " set-log-level LEVEL Set logging threshold for systemd\n"
1178 " dump Output state serialization of service manager\n",
1179 program_invocation_short_name);
1181 /* When updating this list, including descriptions, apply
1182 * changes to shell-completion/bash/systemd and
1183 * shell-completion/systemd-zsh-completion.zsh too. */
1186 static int parse_argv(int argc, char *argv[]) {
1190 ARG_VERSION = 0x100,
1195 ARG_DOT_FROM_PATTERN,
1201 static const struct option options[] = {
1202 { "help", no_argument, NULL, 'h' },
1203 { "version", no_argument, NULL, ARG_VERSION },
1204 { "order", no_argument, NULL, ARG_ORDER },
1205 { "require", no_argument, NULL, ARG_REQUIRE },
1206 { "user", no_argument, NULL, ARG_USER },
1207 { "system", no_argument, NULL, ARG_SYSTEM },
1208 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1209 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1210 { "fuzz", required_argument, NULL, ARG_FUZZ },
1211 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1212 { NULL, 0, NULL, 0 }
1219 switch (getopt_long(argc, argv, "h", options, NULL)) {
1226 puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES);
1230 arg_scope = UNIT_FILE_USER;
1234 arg_scope = UNIT_FILE_SYSTEM;
1238 arg_dot = DEP_ORDER;
1242 arg_dot = DEP_REQUIRE;
1245 case ARG_DOT_FROM_PATTERN:
1246 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1251 case ARG_DOT_TO_PATTERN:
1252 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1258 r = parse_sec(optarg, &arg_fuzz);
1264 arg_no_pager = true;
1274 assert_not_reached("Unhandled option");
1279 int main(int argc, char *argv[]) {
1280 _cleanup_bus_unref_ sd_bus *bus = NULL;
1283 setlocale(LC_ALL, "");
1284 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1285 log_parse_environment();
1288 r = parse_argv(argc, argv);
1292 if (arg_scope == UNIT_FILE_SYSTEM)
1293 r = sd_bus_open_system(&bus);
1295 r = sd_bus_open_user(&bus);
1298 log_error("Failed to connect to bus: %s", strerror(-r));
1302 if (!argv[optind] || streq(argv[optind], "time"))
1303 r = analyze_time(bus);
1304 else if (streq(argv[optind], "blame"))
1305 r = analyze_blame(bus);
1306 else if (streq(argv[optind], "critical-chain"))
1307 r = analyze_critical_chain(bus, argv+optind+1);
1308 else if (streq(argv[optind], "plot"))
1309 r = analyze_plot(bus);
1310 else if (streq(argv[optind], "dot"))
1311 r = dot(bus, argv+optind+1);
1312 else if (streq(argv[optind], "dump"))
1313 r = dump(bus, argv+optind+1);
1314 else if (streq(argv[optind], "set-log-level"))
1315 r = set_log_level(bus, argv+optind+1);
1317 log_error("Unknown operation '%s'.", argv[optind]);
1322 strv_free(arg_dot_from_patterns);
1323 strv_free(arg_dot_to_patterns);
1325 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;