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 acquire_time_data(sd_bus *bus, struct unit_times **out) {
200 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
201 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
202 int r, c = 0, n_units = 0;
203 struct unit_times *unit_times = NULL;
206 r = sd_bus_call_method(
208 "org.freedesktop.systemd1",
209 "/org/freedesktop/systemd1",
210 "org.freedesktop.systemd1.Manager",
216 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
220 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
224 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
225 struct unit_times *t;
231 struct unit_times *w;
233 n_units = MAX(2*c, 16);
234 w = realloc(unit_times, sizeof(struct unit_times) * n_units);
246 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
248 if (bus_get_uint64_property(bus, u.unit_path,
249 "org.freedesktop.systemd1.Unit",
250 "InactiveExitTimestampMonotonic",
252 bus_get_uint64_property(bus, u.unit_path,
253 "org.freedesktop.systemd1.Unit",
254 "ActiveEnterTimestampMonotonic",
256 bus_get_uint64_property(bus, u.unit_path,
257 "org.freedesktop.systemd1.Unit",
258 "ActiveExitTimestampMonotonic",
260 bus_get_uint64_property(bus, u.unit_path,
261 "org.freedesktop.systemd1.Unit",
262 "InactiveEnterTimestampMonotonic",
268 if (t->aet >= t->ixt)
269 t->time = t->aet - t->ixt;
270 else if (t->iet >= t->ixt)
271 t->time = t->iet - t->ixt;
278 t->name = strdup(u.id);
279 if (t->name == NULL) {
290 free_unit_times(unit_times, (unsigned) c);
294 static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
295 static struct boot_times times;
296 static bool cached = false;
301 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
303 if (bus_get_uint64_property(bus,
304 "/org/freedesktop/systemd1",
305 "org.freedesktop.systemd1.Manager",
306 "FirmwareTimestampMonotonic",
307 ×.firmware_time) < 0 ||
308 bus_get_uint64_property(bus,
309 "/org/freedesktop/systemd1",
310 "org.freedesktop.systemd1.Manager",
311 "LoaderTimestampMonotonic",
312 ×.loader_time) < 0 ||
313 bus_get_uint64_property(bus,
314 "/org/freedesktop/systemd1",
315 "org.freedesktop.systemd1.Manager",
317 ×.kernel_time) < 0 ||
318 bus_get_uint64_property(bus,
319 "/org/freedesktop/systemd1",
320 "org.freedesktop.systemd1.Manager",
321 "InitRDTimestampMonotonic",
322 ×.initrd_time) < 0 ||
323 bus_get_uint64_property(bus,
324 "/org/freedesktop/systemd1",
325 "org.freedesktop.systemd1.Manager",
326 "UserspaceTimestampMonotonic",
327 ×.userspace_time) < 0 ||
328 bus_get_uint64_property(bus,
329 "/org/freedesktop/systemd1",
330 "org.freedesktop.systemd1.Manager",
331 "FinishTimestampMonotonic",
332 ×.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 int pretty_boot_time(sd_bus *bus, char **_buf) {
373 char ts[FORMAT_TIMESPAN_MAX];
374 struct boot_times *t;
375 static char buf[4096];
380 r = acquire_boot_times(bus, &t);
387 size = strpcpyf(&ptr, size, "Startup finished in ");
388 if (t->firmware_time)
389 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
391 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
393 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
394 if (t->initrd_time > 0)
395 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
397 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
398 if (t->kernel_time > 0)
399 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
401 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
411 static void svg_graph_box(double height, double begin, double end) {
414 /* outside box, fill */
415 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
416 SCALE_X * (end - begin), SCALE_Y * height);
418 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
419 /* lines for each second */
420 if (i % 5000000 == 0)
421 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
422 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
423 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
424 else if (i % 1000000 == 0)
425 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
426 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
427 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
429 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
430 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
434 static int analyze_plot(sd_bus *bus) {
435 struct unit_times *times;
436 struct boot_times *boot;
440 _cleanup_free_ char *pretty_times = NULL, *osname = NULL;
441 struct unit_times *u;
443 n = acquire_boot_times(bus, &boot);
447 n = pretty_boot_time(bus, &pretty_times);
451 get_os_name(&osname);
452 assert_se(uname(&name) >= 0);
454 n = acquire_time_data(bus, ×);
458 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
460 width = SCALE_X * (boot->firmware_time + boot->finish_time);
464 if (boot->firmware_time > boot->loader_time)
466 if (boot->loader_time) {
471 if (boot->initrd_time)
473 if (boot->kernel_time)
476 for (u = times; u < times + n; u++) {
479 if (u->ixt < boot->userspace_time ||
480 u->ixt > boot->finish_time) {
485 len = ((boot->firmware_time + u->ixt) * SCALE_X)
486 + (10.0 * strlen(u->name));
490 if (u->iet > u->ixt && u->iet <= boot->finish_time
491 && u->aet == 0 && u->axt == 0)
492 u->aet = u->axt = u->iet;
493 if (u->aet < u->ixt || u->aet > boot->finish_time)
494 u->aet = boot->finish_time;
495 if (u->axt < u->aet || u->aet > boot->finish_time)
496 u->axt = boot->finish_time;
497 if (u->iet < u->axt || u->iet > boot->finish_time)
498 u->iet = boot->finish_time;
502 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
503 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
504 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
506 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
507 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
508 80.0 + width, 150.0 + (m * SCALE_Y) +
509 5 * SCALE_Y /* legend */);
511 /* write some basic info as a comment, including some help */
512 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
513 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
514 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
515 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
516 "<!-- point your browser to this file. -->\n\n"
517 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
520 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
521 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
522 " rect.background { fill: rgb(255,255,255); }\n"
523 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
524 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
525 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
526 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
527 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
528 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
529 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
530 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
531 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
532 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
533 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
534 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
536 " line.sec5 { stroke-width: 2; }\n"
537 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
538 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
539 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
540 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
541 " text.sec { font-size: 10px; }\n"
542 " ]]>\n </style>\n</defs>\n\n");
544 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
545 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
546 svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
547 isempty(osname) ? "Linux" : osname,
548 name.nodename, name.release, name.version, name.machine);
550 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
551 svg_graph_box(m, -boot->firmware_time, boot->finish_time);
553 if (boot->firmware_time) {
554 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
555 svg_text(true, -(double) boot->firmware_time, y, "firmware");
558 if (boot->loader_time) {
559 svg_bar("loader", -(double) boot->loader_time, 0, y);
560 svg_text(true, -(double) boot->loader_time, y, "loader");
563 if (boot->kernel_time) {
564 svg_bar("kernel", 0, boot->kernel_done_time, y);
565 svg_text(true, 0, y, "kernel");
568 if (boot->initrd_time) {
569 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
570 svg_text(true, boot->initrd_time, y, "initrd");
573 svg_bar("active", boot->userspace_time, boot->finish_time, y);
574 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
575 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
576 svg_text("left", boot->userspace_time, y, "systemd");
579 for (u = times; u < times + n; u++) {
580 char ts[FORMAT_TIMESPAN_MAX];
586 svg_bar("activating", u->ixt, u->aet, y);
587 svg_bar("active", u->aet, u->axt, y);
588 svg_bar("deactivating", u->axt, u->iet, y);
590 b = u->ixt * SCALE_X > width * 2 / 3;
592 svg_text(b, u->ixt, y, "%s (%s)",
593 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
595 svg_text(b, u->ixt, y, "%s", u->name);
601 svg_bar("activating", 0, 300000, y);
602 svg_text("right", 400000, y, "Activating");
604 svg_bar("active", 0, 300000, y);
605 svg_text("right", 400000, y, "Active");
607 svg_bar("deactivating", 0, 300000, y);
608 svg_text("right", 400000, y, "Deactivating");
610 svg_bar("generators", 0, 300000, y);
611 svg_text("right", 400000, y, "Generators");
613 svg_bar("unitsload", 0, 300000, y);
614 svg_text("right", 400000, y, "Loading unit files");
621 free_unit_times(times, (unsigned) n);
626 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
627 bool last, struct unit_times *times, struct boot_times *boot) {
629 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
631 for (i = level; i != 0; i--)
632 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));
634 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
638 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
639 format_timespan(ts, sizeof(ts), times->ixt - boot->userspace_time, USEC_PER_MSEC),
640 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
641 else if (times->aet > boot->userspace_time)
642 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->aet - boot->userspace_time, USEC_PER_MSEC));
645 } else printf("%s", name);
651 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
652 _cleanup_free_ char *path;
653 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
654 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
664 path = unit_dbus_path_from_name(name);
670 r = sd_bus_get_property(bus,
671 "org.freedesktop.systemd1",
673 "org.freedesktop.systemd1.Unit",
679 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
683 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s");
687 while ((r = sd_bus_message_read(reply, "s", &s)) > 0) {
688 r = strv_extend(&ret, s);
702 static Hashmap *unit_times_hashmap;
704 static int list_dependencies_compare(const void *_a, const void *_b) {
705 const char **a = (const char**) _a, **b = (const char**) _b;
706 usec_t usa = 0, usb = 0;
707 struct unit_times *times;
709 times = hashmap_get(unit_times_hashmap, *a);
712 times = hashmap_get(unit_times_hashmap, *b);
719 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
720 unsigned int branches) {
721 _cleanup_strv_free_ char **deps = NULL;
724 usec_t service_longest = 0;
726 struct unit_times *times;
727 struct boot_times *boot;
729 if(strv_extend(units, name))
732 r = list_dependencies_get_dependencies(bus, name, &deps);
736 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
738 r = acquire_boot_times(bus, &boot);
742 STRV_FOREACH(c, deps) {
743 times = hashmap_get(unit_times_hashmap, *c);
746 && times->aet <= boot->finish_time
747 && (times->aet >= service_longest
748 || service_longest == 0)) {
749 service_longest = times->aet;
754 if (service_longest == 0 )
757 STRV_FOREACH(c, deps) {
758 times = hashmap_get(unit_times_hashmap, *c);
759 if (times && times->aet
760 && times->aet <= boot->finish_time
761 && (service_longest - times->aet) <= arg_fuzz) {
769 STRV_FOREACH(c, deps) {
770 times = hashmap_get(unit_times_hashmap, *c);
773 || times->aet > boot->finish_time
774 || service_longest - times->aet > arg_fuzz)
779 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
783 if (strv_contains(*units, *c)) {
784 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
791 r = list_dependencies_one(bus, *c, level + 1, units,
792 (branches << 1) | (to_print ? 1 : 0));
802 static int list_dependencies(sd_bus *bus, const char *name) {
803 _cleanup_strv_free_ char **units = NULL;
804 char ts[FORMAT_TIMESPAN_MAX];
805 struct unit_times *times;
807 const char *path, *id;
808 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
809 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
810 struct boot_times *boot;
814 path = unit_dbus_path_from_name(name);
818 r = sd_bus_get_property(
820 "org.freedesktop.systemd1",
822 "org.freedesktop.systemd1.Unit",
828 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
832 r = sd_bus_message_read(reply, "s", &id);
834 log_error("Failed to parse reply.");
838 times = hashmap_get(unit_times_hashmap, id);
840 r = acquire_boot_times(bus, &boot);
846 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
847 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
848 else if (times->aet > boot->userspace_time)
849 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->aet - boot->userspace_time, USEC_PER_MSEC));
854 return list_dependencies_one(bus, name, 0, &units, 0);
857 static int analyze_critical_chain(sd_bus *bus, char *names[]) {
858 struct unit_times *times;
863 n = acquire_time_data(bus, ×);
867 h = hashmap_new(string_hash_func, string_compare_func);
871 for (i = 0; i < (unsigned)n; i++) {
872 r = hashmap_put(h, times[i].name, ×[i]);
876 unit_times_hashmap = h;
878 pager_open_if_enabled();
880 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
881 "The time the unit takes to start is printed after the \"+\" character.\n");
883 if (!strv_isempty(names)) {
885 STRV_FOREACH(name, names)
886 list_dependencies(bus, *name);
888 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
891 free_unit_times(times, (unsigned) n);
895 static int analyze_blame(sd_bus *bus) {
896 struct unit_times *times;
900 n = acquire_time_data(bus, ×);
904 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
906 pager_open_if_enabled();
908 for (i = 0; i < (unsigned) n; i++) {
909 char ts[FORMAT_TIMESPAN_MAX];
911 if (times[i].time > 0)
912 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
915 free_unit_times(times, (unsigned) n);
919 static int analyze_time(sd_bus *bus) {
920 _cleanup_free_ char *buf = NULL;
923 r = pretty_boot_time(bus, &buf);
931 static int graph_one_property(sd_bus *bus, const struct unit_info *u, const char* prop, const char *color, char* patterns[]) {
932 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
933 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
934 _cleanup_strv_free_ char **units = NULL;
943 r = sd_bus_get_property(bus,
944 "org.freedesktop.systemd1",
946 "org.freedesktop.systemd1.Unit",
952 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
956 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s");
960 while ((r = sd_bus_message_read(reply, "s", &s)) > 0) {
961 r = strv_extend(&units, s);
968 STRV_FOREACH(unit, units) {
972 if (!strv_isempty(arg_dot_from_patterns)) {
975 STRV_FOREACH(p, arg_dot_from_patterns)
976 if (fnmatch(*p, u->id, 0) == 0) {
985 if (!strv_isempty(arg_dot_to_patterns)) {
988 STRV_FOREACH(p, arg_dot_to_patterns)
989 if (fnmatch(*p, *unit, 0) == 0) {
998 if (!strv_isempty(patterns)) {
1001 STRV_FOREACH(p, patterns)
1002 if (fnmatch(*p, u->id, 0) == 0 || fnmatch(*p, *unit, 0) == 0) {
1010 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1016 static int graph_one(sd_bus *bus, const struct unit_info *u, char *patterns[]) {
1022 if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
1023 r = graph_one_property(bus, u, "After", "green", patterns);
1028 if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
1029 r = graph_one_property(bus, u, "Requires", "black", patterns);
1032 r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns);
1035 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns);
1038 r = graph_one_property(bus, u, "Wants", "grey66", patterns);
1041 r = graph_one_property(bus, u, "Conflicts", "red", patterns);
1044 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns);
1052 static int dot(sd_bus *bus, char* patterns[]) {
1053 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1054 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1058 r = sd_bus_call_method(
1060 "org.freedesktop.systemd1",
1061 "/org/freedesktop/systemd1",
1062 "org.freedesktop.systemd1.Manager",
1068 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
1072 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1076 printf("digraph systemd {\n");
1078 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1079 r = graph_one(bus, &u, patterns);
1086 log_info(" Color legend: black = Requires\n"
1087 " dark blue = Requisite\n"
1088 " dark grey = Wants\n"
1089 " red = Conflicts\n"
1090 " green = After\n");
1093 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1094 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1098 static int dump(sd_bus *bus, char **args) {
1099 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1100 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1101 const char *text = NULL;
1104 if (!strv_isempty(args)) {
1105 log_error("Too many arguments.");
1109 pager_open_if_enabled();
1111 r = sd_bus_call_method(
1113 "org.freedesktop.systemd1",
1114 "/org/freedesktop/systemd1",
1115 "org.freedesktop.systemd1.Manager",
1121 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
1125 r = sd_bus_message_read(reply, "s", &text);
1127 log_error("Failed to parse reply");
1131 fputs(text, stdout);
1135 static int set_log_level(sd_bus *bus, char **args) {
1136 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1143 if (strv_length(args) != 1) {
1144 log_error("This command expects one argument only.");
1150 r = sd_bus_set_property(
1152 "org.freedesktop.systemd1",
1153 "/org/freedesktop/systemd1",
1154 "org.freedesktop.systemd1.Manager",
1160 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1167 static void analyze_help(void) {
1169 pager_open_if_enabled();
1171 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1172 "Process systemd profiling information\n\n"
1173 " -h --help Show this help\n"
1174 " --version Show package version\n"
1175 " --system Connect to system manager\n"
1176 " --user Connect to user service manager\n"
1177 " --order When generating a dependency graph, show only order\n"
1178 " --require When generating a dependency graph, show only requirement\n"
1179 " --from-pattern=GLOB, --to-pattern=GLOB\n"
1180 " When generating a dependency graph, filter only origins\n"
1181 " or destinations, respectively\n"
1182 " --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
1183 " services, which finished TIMESPAN earlier, than the\n"
1184 " latest in the branch. The unit of TIMESPAN is seconds\n"
1185 " unless specified with a different unit, i.e. 50ms\n"
1186 " --no-pager Do not pipe output into a pager\n\n"
1188 " time Print time spent in the kernel before reaching userspace\n"
1189 " blame Print list of running units ordered by time to init\n"
1190 " critical-chain Print a tree of the time critical chain of units\n"
1191 " plot Output SVG graphic showing service initialization\n"
1192 " dot Output dependency graph in dot(1) format\n"
1193 " set-log-level LEVEL Set logging threshold for systemd\n"
1194 " dump Output state serialization of service manager\n",
1195 program_invocation_short_name);
1197 /* When updating this list, including descriptions, apply
1198 * changes to shell-completion/bash/systemd and
1199 * shell-completion/systemd-zsh-completion.zsh too. */
1202 static int parse_argv(int argc, char *argv[]) {
1206 ARG_VERSION = 0x100,
1211 ARG_DOT_FROM_PATTERN,
1217 static const struct option options[] = {
1218 { "help", no_argument, NULL, 'h' },
1219 { "version", no_argument, NULL, ARG_VERSION },
1220 { "order", no_argument, NULL, ARG_ORDER },
1221 { "require", no_argument, NULL, ARG_REQUIRE },
1222 { "user", no_argument, NULL, ARG_USER },
1223 { "system", no_argument, NULL, ARG_SYSTEM },
1224 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1225 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1226 { "fuzz", required_argument, NULL, ARG_FUZZ },
1227 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1228 { NULL, 0, NULL, 0 }
1235 switch (getopt_long(argc, argv, "h", options, NULL)) {
1242 puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES);
1246 arg_scope = UNIT_FILE_USER;
1250 arg_scope = UNIT_FILE_SYSTEM;
1254 arg_dot = DEP_ORDER;
1258 arg_dot = DEP_REQUIRE;
1261 case ARG_DOT_FROM_PATTERN:
1262 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1267 case ARG_DOT_TO_PATTERN:
1268 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1274 r = parse_sec(optarg, &arg_fuzz);
1280 arg_no_pager = true;
1290 assert_not_reached("Unhandled option");
1295 int main(int argc, char *argv[]) {
1296 _cleanup_bus_unref_ sd_bus *bus = NULL;
1299 setlocale(LC_ALL, "");
1300 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1301 log_parse_environment();
1304 r = parse_argv(argc, argv);
1308 if (arg_scope == UNIT_FILE_SYSTEM)
1309 r = sd_bus_open_system(&bus);
1311 r = sd_bus_open_user(&bus);
1314 log_error("Failed to connect to bus: %s", strerror(-r));
1318 if (!argv[optind] || streq(argv[optind], "time"))
1319 r = analyze_time(bus);
1320 else if (streq(argv[optind], "blame"))
1321 r = analyze_blame(bus);
1322 else if (streq(argv[optind], "critical-chain"))
1323 r = analyze_critical_chain(bus, argv+optind+1);
1324 else if (streq(argv[optind], "plot"))
1325 r = analyze_plot(bus);
1326 else if (streq(argv[optind], "dot"))
1327 r = dot(bus, argv+optind+1);
1328 else if (streq(argv[optind], "dump"))
1329 r = dump(bus, argv+optind+1);
1330 else if (streq(argv[optind], "set-log-level"))
1331 r = set_log_level(bus, argv+optind+1);
1333 log_error("Unknown operation '%s'.", argv[optind]);
1338 strv_free(arg_dot_from_patterns);
1339 strv_free(arg_dot_to_patterns);
1341 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;