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"
41 #include "terminal-util.h"
43 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
44 #define SCALE_Y (20.0)
46 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
48 #define svg(...) printf(__VA_ARGS__)
50 #define svg_bar(class, x1, x2, y) \
51 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
53 SCALE_X * (x1), SCALE_Y * (y), \
54 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
56 #define svg_text(b, x, y, format, ...) \
58 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
59 svg(format, ## __VA_ARGS__); \
68 static char** arg_dot_from_patterns
= NULL
;
69 static char** arg_dot_to_patterns
= NULL
;
70 static usec_t arg_fuzz
= 0;
71 static bool arg_no_pager
= false;
72 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
73 static char *arg_host
= NULL
;
74 static bool arg_user
= false;
75 static bool arg_man
= true;
81 usec_t kernel_done_time
;
83 usec_t userspace_time
;
85 usec_t security_start_time
;
86 usec_t security_finish_time
;
87 usec_t generators_start_time
;
88 usec_t generators_finish_time
;
89 usec_t unitsload_start_time
;
90 usec_t unitsload_finish_time
;
105 char *kernel_release
;
106 char *kernel_version
;
107 char *os_pretty_name
;
108 char *virtualization
;
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
;
130 r
= sd_bus_get_property_trivial(
132 "org.freedesktop.systemd1",
140 log_error("Failed to parse reply: %s", bus_error_message(&error
, -r
));
147 static int bus_get_unit_property_strv(sd_bus
*bus
, const char *path
, const char *property
, char ***strv
) {
148 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
156 r
= sd_bus_get_property_strv(
158 "org.freedesktop.systemd1",
160 "org.freedesktop.systemd1.Unit",
165 log_error("Failed to get unit property %s: %s", property
, bus_error_message(&error
, -r
));
172 static int compare_unit_time(const void *a
, const void *b
) {
173 return compare(((struct unit_times
*)b
)->time
,
174 ((struct unit_times
*)a
)->time
);
177 static int compare_unit_start(const void *a
, const void *b
) {
178 return compare(((struct unit_times
*)a
)->activating
,
179 ((struct unit_times
*)b
)->activating
);
182 static void free_unit_times(struct unit_times
*t
, unsigned n
) {
183 struct unit_times
*p
;
185 for (p
= t
; p
< t
+ n
; p
++)
191 static int acquire_time_data(sd_bus
*bus
, struct unit_times
**out
) {
192 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
193 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
195 struct unit_times
*unit_times
= NULL
;
199 r
= sd_bus_call_method(
201 "org.freedesktop.systemd1",
202 "/org/freedesktop/systemd1",
203 "org.freedesktop.systemd1.Manager",
208 log_error("Failed to list units: %s", bus_error_message(&error
, -r
));
212 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
214 bus_log_parse_error(r
);
218 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
219 struct unit_times
*t
;
221 if (!GREEDY_REALLOC(unit_times
, size
, c
+1)) {
229 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
231 if (bus_get_uint64_property(bus
, u
.unit_path
,
232 "org.freedesktop.systemd1.Unit",
233 "InactiveExitTimestampMonotonic",
234 &t
->activating
) < 0 ||
235 bus_get_uint64_property(bus
, u
.unit_path
,
236 "org.freedesktop.systemd1.Unit",
237 "ActiveEnterTimestampMonotonic",
238 &t
->activated
) < 0 ||
239 bus_get_uint64_property(bus
, u
.unit_path
,
240 "org.freedesktop.systemd1.Unit",
241 "ActiveExitTimestampMonotonic",
242 &t
->deactivating
) < 0 ||
243 bus_get_uint64_property(bus
, u
.unit_path
,
244 "org.freedesktop.systemd1.Unit",
245 "InactiveEnterTimestampMonotonic",
246 &t
->deactivated
) < 0) {
251 if (t
->activated
>= t
->activating
)
252 t
->time
= t
->activated
- t
->activating
;
253 else if (t
->deactivated
>= t
->activating
)
254 t
->time
= t
->deactivated
- t
->activating
;
258 if (t
->activating
== 0)
261 t
->name
= strdup(u
.id
);
262 if (t
->name
== NULL
) {
269 bus_log_parse_error(r
);
278 free_unit_times(unit_times
, (unsigned) c
);
282 static int acquire_boot_times(sd_bus
*bus
, struct boot_times
**bt
) {
283 static struct boot_times times
;
284 static bool cached
= false;
289 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
291 if (bus_get_uint64_property(bus
,
292 "/org/freedesktop/systemd1",
293 "org.freedesktop.systemd1.Manager",
294 "FirmwareTimestampMonotonic",
295 ×
.firmware_time
) < 0 ||
296 bus_get_uint64_property(bus
,
297 "/org/freedesktop/systemd1",
298 "org.freedesktop.systemd1.Manager",
299 "LoaderTimestampMonotonic",
300 ×
.loader_time
) < 0 ||
301 bus_get_uint64_property(bus
,
302 "/org/freedesktop/systemd1",
303 "org.freedesktop.systemd1.Manager",
305 ×
.kernel_time
) < 0 ||
306 bus_get_uint64_property(bus
,
307 "/org/freedesktop/systemd1",
308 "org.freedesktop.systemd1.Manager",
309 "InitRDTimestampMonotonic",
310 ×
.initrd_time
) < 0 ||
311 bus_get_uint64_property(bus
,
312 "/org/freedesktop/systemd1",
313 "org.freedesktop.systemd1.Manager",
314 "UserspaceTimestampMonotonic",
315 ×
.userspace_time
) < 0 ||
316 bus_get_uint64_property(bus
,
317 "/org/freedesktop/systemd1",
318 "org.freedesktop.systemd1.Manager",
319 "FinishTimestampMonotonic",
320 ×
.finish_time
) < 0 ||
321 bus_get_uint64_property(bus
,
322 "/org/freedesktop/systemd1",
323 "org.freedesktop.systemd1.Manager",
324 "SecurityStartTimestampMonotonic",
325 ×
.security_start_time
) < 0 ||
326 bus_get_uint64_property(bus
,
327 "/org/freedesktop/systemd1",
328 "org.freedesktop.systemd1.Manager",
329 "SecurityFinishTimestampMonotonic",
330 ×
.security_finish_time
) < 0 ||
331 bus_get_uint64_property(bus
,
332 "/org/freedesktop/systemd1",
333 "org.freedesktop.systemd1.Manager",
334 "GeneratorsStartTimestampMonotonic",
335 ×
.generators_start_time
) < 0 ||
336 bus_get_uint64_property(bus
,
337 "/org/freedesktop/systemd1",
338 "org.freedesktop.systemd1.Manager",
339 "GeneratorsFinishTimestampMonotonic",
340 ×
.generators_finish_time
) < 0 ||
341 bus_get_uint64_property(bus
,
342 "/org/freedesktop/systemd1",
343 "org.freedesktop.systemd1.Manager",
344 "UnitsLoadStartTimestampMonotonic",
345 ×
.unitsload_start_time
) < 0 ||
346 bus_get_uint64_property(bus
,
347 "/org/freedesktop/systemd1",
348 "org.freedesktop.systemd1.Manager",
349 "UnitsLoadFinishTimestampMonotonic",
350 ×
.unitsload_finish_time
) < 0)
353 if (times
.finish_time
<= 0) {
354 log_error("Bootup is not yet finished. Please try again later.");
358 if (times
.initrd_time
)
359 times
.kernel_done_time
= times
.initrd_time
;
361 times
.kernel_done_time
= times
.userspace_time
;
370 static void free_host_info(struct host_info
*hi
) {
372 free(hi
->kernel_name
);
373 free(hi
->kernel_release
);
374 free(hi
->kernel_version
);
375 free(hi
->os_pretty_name
);
376 free(hi
->virtualization
);
377 free(hi
->architecture
);
381 static int acquire_host_info(sd_bus
*bus
, struct host_info
**hi
) {
383 struct host_info
*host
;
385 static const struct bus_properties_map hostname_map
[] = {
386 { "Hostname", "s", NULL
, offsetof(struct host_info
, hostname
) },
387 { "KernelName", "s", NULL
, offsetof(struct host_info
, kernel_name
) },
388 { "KernelRelease", "s", NULL
, offsetof(struct host_info
, kernel_release
) },
389 { "KernelVersion", "s", NULL
, offsetof(struct host_info
, kernel_version
) },
390 { "OperatingSystemPrettyName", "s", NULL
, offsetof(struct host_info
, os_pretty_name
) },
394 static const struct bus_properties_map manager_map
[] = {
395 { "Virtualization", "s", NULL
, offsetof(struct host_info
, virtualization
) },
396 { "Architecture", "s", NULL
, offsetof(struct host_info
, architecture
) },
400 host
= new0(struct host_info
, 1);
404 r
= bus_map_all_properties(bus
,
405 "org.freedesktop.hostname1",
406 "/org/freedesktop/hostname1",
412 r
= bus_map_all_properties(bus
,
413 "org.freedesktop.systemd1",
414 "/org/freedesktop/systemd1",
423 free_host_info(host
);
427 static int pretty_boot_time(sd_bus
*bus
, char **_buf
) {
428 char ts
[FORMAT_TIMESPAN_MAX
];
429 struct boot_times
*t
;
430 static char buf
[4096];
435 r
= acquire_boot_times(bus
, &t
);
442 size
= strpcpyf(&ptr
, size
, "Startup finished in ");
443 if (t
->firmware_time
)
444 size
= strpcpyf(&ptr
, size
, "%s (firmware) + ", format_timespan(ts
, sizeof(ts
), t
->firmware_time
- t
->loader_time
, USEC_PER_MSEC
));
446 size
= strpcpyf(&ptr
, size
, "%s (loader) + ", format_timespan(ts
, sizeof(ts
), t
->loader_time
, USEC_PER_MSEC
));
448 size
= strpcpyf(&ptr
, size
, "%s (kernel) + ", format_timespan(ts
, sizeof(ts
), t
->kernel_done_time
, USEC_PER_MSEC
));
449 if (t
->initrd_time
> 0)
450 size
= strpcpyf(&ptr
, size
, "%s (initrd) + ", format_timespan(ts
, sizeof(ts
), t
->userspace_time
- t
->initrd_time
, USEC_PER_MSEC
));
452 size
= strpcpyf(&ptr
, size
, "%s (userspace) ", format_timespan(ts
, sizeof(ts
), t
->finish_time
- t
->userspace_time
, USEC_PER_MSEC
));
453 if (t
->kernel_time
> 0)
454 strpcpyf(&ptr
, size
, "= %s", format_timespan(ts
, sizeof(ts
), t
->firmware_time
+ t
->finish_time
, USEC_PER_MSEC
));
456 strpcpyf(&ptr
, size
, "= %s", format_timespan(ts
, sizeof(ts
), t
->finish_time
- t
->userspace_time
, USEC_PER_MSEC
));
466 static void svg_graph_box(double height
, double begin
, double end
) {
469 /* outside box, fill */
470 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
471 SCALE_X
* (end
- begin
), SCALE_Y
* height
);
473 for (i
= ((long long) (begin
/ 100000)) * 100000; i
<= end
; i
+=100000) {
474 /* lines for each second */
475 if (i
% 5000000 == 0)
476 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
477 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
478 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
479 else if (i
% 1000000 == 0)
480 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
481 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
482 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
484 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
485 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
);
489 static int analyze_plot(sd_bus
*bus
) {
490 struct unit_times
*times
;
491 struct boot_times
*boot
;
492 struct host_info
*host
= NULL
;
495 _cleanup_free_
char *pretty_times
= NULL
;
496 struct unit_times
*u
;
498 n
= acquire_boot_times(bus
, &boot
);
502 n
= pretty_boot_time(bus
, &pretty_times
);
506 n
= acquire_host_info(bus
, &host
);
510 n
= acquire_time_data(bus
, ×
);
514 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_start
);
516 width
= SCALE_X
* (boot
->firmware_time
+ boot
->finish_time
);
520 if (boot
->firmware_time
> boot
->loader_time
)
522 if (boot
->loader_time
) {
527 if (boot
->initrd_time
)
529 if (boot
->kernel_time
)
532 for (u
= times
; u
< times
+ n
; u
++) {
533 double text_start
, text_width
;
535 if (u
->activating
< boot
->userspace_time
||
536 u
->activating
> boot
->finish_time
) {
542 /* If the text cannot fit on the left side then
543 * increase the svg width so it fits on the right.
544 * TODO: calculate the text width more accurately */
545 text_width
= 8.0 * strlen(u
->name
);
546 text_start
= (boot
->firmware_time
+ u
->activating
) * SCALE_X
;
547 if (text_width
> text_start
&& text_width
+ text_start
> width
)
548 width
= text_width
+ text_start
;
550 if (u
->deactivated
> u
->activating
&& u
->deactivated
<= boot
->finish_time
551 && u
->activated
== 0 && u
->deactivating
== 0)
552 u
->activated
= u
->deactivating
= u
->deactivated
;
553 if (u
->activated
< u
->activating
|| u
->activated
> boot
->finish_time
)
554 u
->activated
= boot
->finish_time
;
555 if (u
->deactivating
< u
->activated
|| u
->activated
> boot
->finish_time
)
556 u
->deactivating
= boot
->finish_time
;
557 if (u
->deactivated
< u
->deactivating
|| u
->deactivated
> boot
->finish_time
)
558 u
->deactivated
= boot
->finish_time
;
562 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
563 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
564 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
566 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
567 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
568 80.0 + width
, 150.0 + (m
* SCALE_Y
) +
569 5 * SCALE_Y
/* legend */);
571 /* write some basic info as a comment, including some help */
572 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
573 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
574 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
575 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
576 "<!-- point your browser to this file. -->\n\n"
577 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION
);
580 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
581 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
582 " rect.background { fill: rgb(255,255,255); }\n"
583 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
584 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
585 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
586 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
587 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
588 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
589 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
590 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
591 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
592 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
593 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
594 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
595 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
597 " line.sec5 { stroke-width: 2; }\n"
598 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
599 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
600 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
601 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
602 " text.sec { font-size: 10px; }\n"
603 " ]]>\n </style>\n</defs>\n\n");
605 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
606 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times
);
607 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
608 isempty(host
->os_pretty_name
) ? "Linux" : host
->os_pretty_name
,
609 isempty(host
->hostname
) ? "" : host
->hostname
,
610 isempty(host
->kernel_name
) ? "" : host
->kernel_name
,
611 isempty(host
->kernel_release
) ? "" : host
->kernel_release
,
612 isempty(host
->kernel_version
) ? "" : host
->kernel_version
,
613 isempty(host
->architecture
) ? "" : host
->architecture
,
614 isempty(host
->virtualization
) ? "" : host
->virtualization
);
616 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X
* boot
->firmware_time
));
617 svg_graph_box(m
, -(double) boot
->firmware_time
, boot
->finish_time
);
619 if (boot
->firmware_time
) {
620 svg_bar("firmware", -(double) boot
->firmware_time
, -(double) boot
->loader_time
, y
);
621 svg_text(true, -(double) boot
->firmware_time
, y
, "firmware");
624 if (boot
->loader_time
) {
625 svg_bar("loader", -(double) boot
->loader_time
, 0, y
);
626 svg_text(true, -(double) boot
->loader_time
, y
, "loader");
629 if (boot
->kernel_time
) {
630 svg_bar("kernel", 0, boot
->kernel_done_time
, y
);
631 svg_text(true, 0, y
, "kernel");
634 if (boot
->initrd_time
) {
635 svg_bar("initrd", boot
->initrd_time
, boot
->userspace_time
, y
);
636 svg_text(true, boot
->initrd_time
, y
, "initrd");
639 svg_bar("active", boot
->userspace_time
, boot
->finish_time
, y
);
640 svg_bar("security", boot
->security_start_time
, boot
->security_finish_time
, y
);
641 svg_bar("generators", boot
->generators_start_time
, boot
->generators_finish_time
, y
);
642 svg_bar("unitsload", boot
->unitsload_start_time
, boot
->unitsload_finish_time
, y
);
643 svg_text(true, boot
->userspace_time
, y
, "systemd");
646 for (u
= times
; u
< times
+ n
; u
++) {
647 char ts
[FORMAT_TIMESPAN_MAX
];
653 svg_bar("activating", u
->activating
, u
->activated
, y
);
654 svg_bar("active", u
->activated
, u
->deactivating
, y
);
655 svg_bar("deactivating", u
->deactivating
, u
->deactivated
, y
);
657 /* place the text on the left if we have passed the half of the svg width */
658 b
= u
->activating
* SCALE_X
< width
/ 2;
660 svg_text(b
, u
->activating
, y
, "%s (%s)",
661 u
->name
, format_timespan(ts
, sizeof(ts
), u
->time
, USEC_PER_MSEC
));
663 svg_text(b
, u
->activating
, y
, "%s", u
->name
);
670 svg("<g transform=\"translate(20,100)\">\n");
672 svg_bar("activating", 0, 300000, y
);
673 svg_text(true, 400000, y
, "Activating");
675 svg_bar("active", 0, 300000, y
);
676 svg_text(true, 400000, y
, "Active");
678 svg_bar("deactivating", 0, 300000, y
);
679 svg_text(true, 400000, y
, "Deactivating");
681 svg_bar("security", 0, 300000, y
);
682 svg_text(true, 400000, y
, "Setting up security module");
684 svg_bar("generators", 0, 300000, y
);
685 svg_text(true, 400000, y
, "Generators");
687 svg_bar("unitsload", 0, 300000, y
);
688 svg_text(true, 400000, y
, "Loading unit files");
695 free_unit_times(times
, (unsigned) n
);
699 free_host_info(host
);
703 static int list_dependencies_print(const char *name
, unsigned int level
, unsigned int branches
,
704 bool last
, struct unit_times
*times
, struct boot_times
*boot
) {
706 char ts
[FORMAT_TIMESPAN_MAX
], ts2
[FORMAT_TIMESPAN_MAX
];
708 for (i
= level
; i
!= 0; i
--)
709 printf("%s", draw_special_char(branches
& (1 << (i
-1)) ? DRAW_TREE_VERTICAL
: DRAW_TREE_SPACE
));
711 printf("%s", draw_special_char(last
? DRAW_TREE_RIGHT
: DRAW_TREE_BRANCH
));
715 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON
, name
,
716 format_timespan(ts
, sizeof(ts
), times
->activating
- boot
->userspace_time
, USEC_PER_MSEC
),
717 format_timespan(ts2
, sizeof(ts2
), times
->time
, USEC_PER_MSEC
), ANSI_HIGHLIGHT_OFF
);
718 else if (times
->activated
> boot
->userspace_time
)
719 printf("%s @%s", name
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
729 static int list_dependencies_get_dependencies(sd_bus
*bus
, const char *name
, char ***deps
) {
730 _cleanup_free_
char *path
= NULL
;
736 path
= unit_dbus_path_from_name(name
);
740 return bus_get_unit_property_strv(bus
, path
, "After", deps
);
743 static Hashmap
*unit_times_hashmap
;
745 static int list_dependencies_compare(const void *_a
, const void *_b
) {
746 const char **a
= (const char**) _a
, **b
= (const char**) _b
;
747 usec_t usa
= 0, usb
= 0;
748 struct unit_times
*times
;
750 times
= hashmap_get(unit_times_hashmap
, *a
);
752 usa
= times
->activated
;
753 times
= hashmap_get(unit_times_hashmap
, *b
);
755 usb
= times
->activated
;
760 static int list_dependencies_one(sd_bus
*bus
, const char *name
, unsigned int level
, char ***units
,
761 unsigned int branches
) {
762 _cleanup_strv_free_
char **deps
= NULL
;
765 usec_t service_longest
= 0;
767 struct unit_times
*times
;
768 struct boot_times
*boot
;
770 if (strv_extend(units
, name
))
773 r
= list_dependencies_get_dependencies(bus
, name
, &deps
);
777 qsort_safe(deps
, strv_length(deps
), sizeof (char*), list_dependencies_compare
);
779 r
= acquire_boot_times(bus
, &boot
);
783 STRV_FOREACH(c
, deps
) {
784 times
= hashmap_get(unit_times_hashmap
, *c
);
787 && times
->activated
<= boot
->finish_time
788 && (times
->activated
>= service_longest
789 || service_longest
== 0)) {
790 service_longest
= times
->activated
;
795 if (service_longest
== 0 )
798 STRV_FOREACH(c
, deps
) {
799 times
= hashmap_get(unit_times_hashmap
, *c
);
800 if (times
&& times
->activated
801 && times
->activated
<= boot
->finish_time
802 && (service_longest
- times
->activated
) <= arg_fuzz
) {
810 STRV_FOREACH(c
, deps
) {
811 times
= hashmap_get(unit_times_hashmap
, *c
);
814 || times
->activated
> boot
->finish_time
815 || service_longest
- times
->activated
> arg_fuzz
)
820 r
= list_dependencies_print(*c
, level
, branches
, to_print
== 0, times
, boot
);
824 if (strv_contains(*units
, *c
)) {
825 r
= list_dependencies_print("...", level
+ 1, (branches
<< 1) | (to_print
? 1 : 0),
832 r
= list_dependencies_one(bus
, *c
, level
+ 1, units
,
833 (branches
<< 1) | (to_print
? 1 : 0));
843 static int list_dependencies(sd_bus
*bus
, const char *name
) {
844 _cleanup_strv_free_
char **units
= NULL
;
845 char ts
[FORMAT_TIMESPAN_MAX
];
846 struct unit_times
*times
;
849 _cleanup_free_
char *path
= NULL
;
850 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
851 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
852 struct boot_times
*boot
;
856 path
= unit_dbus_path_from_name(name
);
860 r
= sd_bus_get_property(
862 "org.freedesktop.systemd1",
864 "org.freedesktop.systemd1.Unit",
870 log_error("Failed to get ID: %s", bus_error_message(&error
, -r
));
874 r
= sd_bus_message_read(reply
, "s", &id
);
876 return bus_log_parse_error(r
);
878 times
= hashmap_get(unit_times_hashmap
, id
);
880 r
= acquire_boot_times(bus
, &boot
);
886 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON
, id
,
887 format_timespan(ts
, sizeof(ts
), times
->time
, USEC_PER_MSEC
), ANSI_HIGHLIGHT_OFF
);
888 else if (times
->activated
> boot
->userspace_time
)
889 printf("%s @%s\n", id
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
894 return list_dependencies_one(bus
, name
, 0, &units
, 0);
897 static int analyze_critical_chain(sd_bus
*bus
, char *names
[]) {
898 struct unit_times
*times
;
903 n
= acquire_time_data(bus
, ×
);
907 h
= hashmap_new(&string_hash_ops
);
911 for (i
= 0; i
< (unsigned)n
; i
++) {
912 r
= hashmap_put(h
, times
[i
].name
, ×
[i
]);
916 unit_times_hashmap
= h
;
918 pager_open_if_enabled();
920 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
921 "The time the unit takes to start is printed after the \"+\" character.\n");
923 if (!strv_isempty(names
)) {
925 STRV_FOREACH(name
, names
)
926 list_dependencies(bus
, *name
);
928 list_dependencies(bus
, SPECIAL_DEFAULT_TARGET
);
931 free_unit_times(times
, (unsigned) n
);
935 static int analyze_blame(sd_bus
*bus
) {
936 struct unit_times
*times
;
940 n
= acquire_time_data(bus
, ×
);
944 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_time
);
946 pager_open_if_enabled();
948 for (i
= 0; i
< (unsigned) n
; i
++) {
949 char ts
[FORMAT_TIMESPAN_MAX
];
951 if (times
[i
].time
> 0)
952 printf("%16s %s\n", format_timespan(ts
, sizeof(ts
), times
[i
].time
, USEC_PER_MSEC
), times
[i
].name
);
955 free_unit_times(times
, (unsigned) n
);
959 static int analyze_time(sd_bus
*bus
) {
960 _cleanup_free_
char *buf
= NULL
;
963 r
= pretty_boot_time(bus
, &buf
);
971 static int graph_one_property(sd_bus
*bus
, const UnitInfo
*u
, const char* prop
, const char *color
, char* patterns
[]) {
972 _cleanup_strv_free_
char **units
= NULL
;
981 match_patterns
= strv_fnmatch(patterns
, u
->id
, 0);
983 if (!strv_isempty(arg_dot_from_patterns
) &&
985 !strv_fnmatch(arg_dot_from_patterns
, u
->id
, 0))
988 r
= bus_get_unit_property_strv(bus
, u
->unit_path
, prop
, &units
);
992 STRV_FOREACH(unit
, units
) {
993 bool match_patterns2
;
995 match_patterns2
= strv_fnmatch(patterns
, *unit
, 0);
997 if (!strv_isempty(arg_dot_to_patterns
) &&
999 !strv_fnmatch(arg_dot_to_patterns
, *unit
, 0))
1002 if (!strv_isempty(patterns
) && !match_patterns
&& !match_patterns2
)
1005 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u
->id
, *unit
, color
);
1011 static int graph_one(sd_bus
*bus
, const UnitInfo
*u
, char *patterns
[]) {
1017 if (arg_dot
== DEP_ORDER
||arg_dot
== DEP_ALL
) {
1018 r
= graph_one_property(bus
, u
, "After", "green", patterns
);
1023 if (arg_dot
== DEP_REQUIRE
||arg_dot
== DEP_ALL
) {
1024 r
= graph_one_property(bus
, u
, "Requires", "black", patterns
);
1027 r
= graph_one_property(bus
, u
, "RequiresOverridable", "black", patterns
);
1030 r
= graph_one_property(bus
, u
, "RequisiteOverridable", "darkblue", patterns
);
1033 r
= graph_one_property(bus
, u
, "Wants", "grey66", patterns
);
1036 r
= graph_one_property(bus
, u
, "Conflicts", "red", patterns
);
1039 r
= graph_one_property(bus
, u
, "ConflictedBy", "red", patterns
);
1047 static int dot(sd_bus
*bus
, char* patterns
[]) {
1048 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1049 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1053 r
= sd_bus_call_method(
1055 "org.freedesktop.systemd1",
1056 "/org/freedesktop/systemd1",
1057 "org.freedesktop.systemd1.Manager",
1063 log_error("Failed to list units: %s", bus_error_message(&error
, -r
));
1067 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1069 return bus_log_parse_error(r
);
1071 printf("digraph systemd {\n");
1073 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
1075 r
= graph_one(bus
, &u
, patterns
);
1080 return bus_log_parse_error(r
);
1084 log_info(" Color legend: black = Requires\n"
1085 " dark blue = Requisite\n"
1086 " dark grey = Wants\n"
1087 " red = Conflicts\n"
1088 " green = After\n");
1091 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1092 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1097 static int dump(sd_bus
*bus
, char **args
) {
1098 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1099 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1100 const char *text
= NULL
;
1103 if (!strv_isempty(args
)) {
1104 log_error("Too many arguments.");
1108 pager_open_if_enabled();
1110 r
= sd_bus_call_method(
1112 "org.freedesktop.systemd1",
1113 "/org/freedesktop/systemd1",
1114 "org.freedesktop.systemd1.Manager",
1120 log_error("Failed issue method call: %s", bus_error_message(&error
, -r
));
1124 r
= sd_bus_message_read(reply
, "s", &text
);
1126 return bus_log_parse_error(r
);
1128 fputs(text
, stdout
);
1132 static int set_log_level(sd_bus
*bus
, char **args
) {
1133 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1139 if (strv_length(args
) != 1) {
1140 log_error("This command expects one argument only.");
1144 r
= sd_bus_set_property(
1146 "org.freedesktop.systemd1",
1147 "/org/freedesktop/systemd1",
1148 "org.freedesktop.systemd1.Manager",
1154 log_error("Failed to issue method call: %s", bus_error_message(&error
, -r
));
1161 static void help(void) {
1163 pager_open_if_enabled();
1165 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1166 "Profile systemd, show unit dependencies, check unit files.\n\n"
1167 " -h --help Show this help\n"
1168 " --version Show package version\n"
1169 " --no-pager Do not pipe output into a pager\n"
1170 " --system Operate on system systemd instance\n"
1171 " --user Operate on user systemd instance\n"
1172 " -H --host=[USER@]HOST Operate on remote host\n"
1173 " -M --machine=CONTAINER Operate on local container\n"
1174 " --order Show only order in the graph\n"
1175 " --require Show only requirement in the graph\n"
1176 " --from-pattern=GLOB Show only origins in the graph\n"
1177 " --to-pattern=GLOB Show only destinations in the graph\n"
1178 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1179 " earlier than the latest in the branch\n"
1180 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1182 " time Print time spent in the kernel\n"
1183 " blame Print list of running units ordered by time to init\n"
1184 " critical-chain Print a tree of the time critical chain of units\n"
1185 " plot Output SVG graphic showing service initialization\n"
1186 " dot Output dependency graph in dot(1) format\n"
1187 " set-log-level LEVEL Set logging threshold for systemd\n"
1188 " dump Output state serialization of service manager\n"
1189 " verify FILE... Check unit files for correctness\n"
1190 , program_invocation_short_name
);
1192 /* When updating this list, including descriptions, apply
1193 * changes to shell-completion/bash/systemd-analyze and
1194 * shell-completion/zsh/_systemd-analyze too. */
1197 static int parse_argv(int argc
, char *argv
[]) {
1199 ARG_VERSION
= 0x100,
1204 ARG_DOT_FROM_PATTERN
,
1211 static const struct option options
[] = {
1212 { "help", no_argument
, NULL
, 'h' },
1213 { "version", no_argument
, NULL
, ARG_VERSION
},
1214 { "order", no_argument
, NULL
, ARG_ORDER
},
1215 { "require", no_argument
, NULL
, ARG_REQUIRE
},
1216 { "user", no_argument
, NULL
, ARG_USER
},
1217 { "system", no_argument
, NULL
, ARG_SYSTEM
},
1218 { "from-pattern", required_argument
, NULL
, ARG_DOT_FROM_PATTERN
},
1219 { "to-pattern", required_argument
, NULL
, ARG_DOT_TO_PATTERN
},
1220 { "fuzz", required_argument
, NULL
, ARG_FUZZ
},
1221 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1222 { "man", optional_argument
, NULL
, ARG_MAN
},
1223 { "host", required_argument
, NULL
, 'H' },
1224 { "machine", required_argument
, NULL
, 'M' },
1233 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
1241 puts(PACKAGE_STRING
);
1242 puts(SYSTEMD_FEATURES
);
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;
1284 arg_transport
= BUS_TRANSPORT_REMOTE
;
1289 arg_transport
= BUS_TRANSPORT_MACHINE
;
1295 r
= parse_boolean(optarg
);
1297 log_error("Failed to parse --man= argument.");
1311 assert_not_reached("Unhandled option code.");
1314 return 1; /* work to do */
1317 int main(int argc
, char *argv
[]) {
1320 setlocale(LC_ALL
, "");
1321 setlocale(LC_NUMERIC
, "C"); /* we want to format/parse floats in C style */
1322 log_parse_environment();
1325 r
= parse_argv(argc
, argv
);
1329 if (streq_ptr(argv
[optind
], "verify"))
1330 r
= verify_units(argv
+optind
+1,
1331 arg_user
? MANAGER_USER
: MANAGER_SYSTEM
,
1334 _cleanup_bus_close_unref_ sd_bus
*bus
= NULL
;
1336 r
= bus_open_transport_systemd(arg_transport
, arg_host
, arg_user
, &bus
);
1338 log_error_errno(r
, "Failed to create bus connection: %m");
1342 if (!argv
[optind
] || streq(argv
[optind
], "time"))
1343 r
= analyze_time(bus
);
1344 else if (streq(argv
[optind
], "blame"))
1345 r
= analyze_blame(bus
);
1346 else if (streq(argv
[optind
], "critical-chain"))
1347 r
= analyze_critical_chain(bus
, argv
+optind
+1);
1348 else if (streq(argv
[optind
], "plot"))
1349 r
= analyze_plot(bus
);
1350 else if (streq(argv
[optind
], "dot"))
1351 r
= dot(bus
, argv
+optind
+1);
1352 else if (streq(argv
[optind
], "dump"))
1353 r
= dump(bus
, argv
+optind
+1);
1354 else if (streq(argv
[optind
], "set-log-level"))
1355 r
= set_log_level(bus
, argv
+optind
+1);
1357 log_error("Unknown operation '%s'.", argv
[optind
]);
1363 strv_free(arg_dot_from_patterns
);
1364 strv_free(arg_dot_to_patterns
);
1366 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;