1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010-2013 Lennart Poettering
6 Copyright 2013 Simon Peeters
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include "alloc-util.h"
30 #include "analyze-verify.h"
31 #include "bus-error.h"
32 #include "bus-unit-util.h"
34 #include "calendarspec.h"
35 #include "glob-util.h"
37 #include "locale-util.h"
40 #include "parse-util.h"
42 #include "seccomp-util.h"
47 #include "terminal-util.h"
48 #include "unit-name.h"
52 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
53 #define SCALE_Y (20.0)
55 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
57 #define svg(...) printf(__VA_ARGS__)
59 #define svg_bar(class, x1, x2, y) \
60 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
62 SCALE_X * (x1), SCALE_Y * (y), \
63 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
65 #define svg_text(b, x, y, format, ...) \
67 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
68 svg(format, ## __VA_ARGS__); \
77 static char** arg_dot_from_patterns
= NULL
;
78 static char** arg_dot_to_patterns
= NULL
;
79 static usec_t arg_fuzz
= 0;
80 static bool arg_no_pager
= false;
81 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
82 static const char *arg_host
= NULL
;
83 static bool arg_user
= false;
84 static bool arg_man
= true;
85 static bool arg_generators
= false;
91 usec_t kernel_done_time
;
93 usec_t userspace_time
;
95 usec_t security_start_time
;
96 usec_t security_finish_time
;
97 usec_t generators_start_time
;
98 usec_t generators_finish_time
;
99 usec_t unitsload_start_time
;
100 usec_t unitsload_finish_time
;
103 * If we're analyzing the user instance, all timestamps will be offset
104 * by its own start-up timestamp, which may be arbitrarily big.
105 * With "plot", this causes arbitrarily wide output SVG files which almost
106 * completely consist of empty space. Thus we cancel out this offset.
108 * This offset is subtracted from times above by acquire_boot_times(),
109 * but it still needs to be subtracted from unit-specific timestamps
110 * (so it is stored here for reference).
112 usec_t reverse_offset
;
127 char *kernel_release
;
128 char *kernel_version
;
129 char *os_pretty_name
;
130 char *virtualization
;
134 static int acquire_bus(bool need_full_bus
, sd_bus
**bus
) {
136 return bus_connect_transport(arg_transport
, arg_host
, arg_user
, bus
);
138 return bus_connect_transport_systemd(arg_transport
, arg_host
, arg_user
, bus
);
141 static int bus_get_uint64_property(sd_bus
*bus
, const char *path
, const char *interface
, const char *property
, uint64_t *val
) {
142 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
151 r
= sd_bus_get_property_trivial(
153 "org.freedesktop.systemd1",
161 log_error("Failed to parse reply: %s", bus_error_message(&error
, -r
));
168 static int bus_get_unit_property_strv(sd_bus
*bus
, const char *path
, const char *property
, char ***strv
) {
169 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
177 r
= sd_bus_get_property_strv(
179 "org.freedesktop.systemd1",
181 "org.freedesktop.systemd1.Unit",
186 log_error("Failed to get unit property %s: %s", property
, bus_error_message(&error
, -r
));
193 static int compare_unit_time(const void *a
, const void *b
) {
194 return compare(((struct unit_times
*)b
)->time
,
195 ((struct unit_times
*)a
)->time
);
198 static int compare_unit_start(const void *a
, const void *b
) {
199 return compare(((struct unit_times
*)a
)->activating
,
200 ((struct unit_times
*)b
)->activating
);
203 static void free_unit_times(struct unit_times
*t
, unsigned n
) {
204 struct unit_times
*p
;
206 for (p
= t
; p
< t
+ n
; p
++)
212 static void subtract_timestamp(usec_t
*a
, usec_t b
) {
221 static int acquire_boot_times(sd_bus
*bus
, struct boot_times
**bt
) {
222 static struct boot_times times
;
223 static bool cached
= false;
228 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
230 if (bus_get_uint64_property(bus
,
231 "/org/freedesktop/systemd1",
232 "org.freedesktop.systemd1.Manager",
233 "FirmwareTimestampMonotonic",
234 ×
.firmware_time
) < 0 ||
235 bus_get_uint64_property(bus
,
236 "/org/freedesktop/systemd1",
237 "org.freedesktop.systemd1.Manager",
238 "LoaderTimestampMonotonic",
239 ×
.loader_time
) < 0 ||
240 bus_get_uint64_property(bus
,
241 "/org/freedesktop/systemd1",
242 "org.freedesktop.systemd1.Manager",
244 ×
.kernel_time
) < 0 ||
245 bus_get_uint64_property(bus
,
246 "/org/freedesktop/systemd1",
247 "org.freedesktop.systemd1.Manager",
248 "InitRDTimestampMonotonic",
249 ×
.initrd_time
) < 0 ||
250 bus_get_uint64_property(bus
,
251 "/org/freedesktop/systemd1",
252 "org.freedesktop.systemd1.Manager",
253 "UserspaceTimestampMonotonic",
254 ×
.userspace_time
) < 0 ||
255 bus_get_uint64_property(bus
,
256 "/org/freedesktop/systemd1",
257 "org.freedesktop.systemd1.Manager",
258 "FinishTimestampMonotonic",
259 ×
.finish_time
) < 0 ||
260 bus_get_uint64_property(bus
,
261 "/org/freedesktop/systemd1",
262 "org.freedesktop.systemd1.Manager",
263 "SecurityStartTimestampMonotonic",
264 ×
.security_start_time
) < 0 ||
265 bus_get_uint64_property(bus
,
266 "/org/freedesktop/systemd1",
267 "org.freedesktop.systemd1.Manager",
268 "SecurityFinishTimestampMonotonic",
269 ×
.security_finish_time
) < 0 ||
270 bus_get_uint64_property(bus
,
271 "/org/freedesktop/systemd1",
272 "org.freedesktop.systemd1.Manager",
273 "GeneratorsStartTimestampMonotonic",
274 ×
.generators_start_time
) < 0 ||
275 bus_get_uint64_property(bus
,
276 "/org/freedesktop/systemd1",
277 "org.freedesktop.systemd1.Manager",
278 "GeneratorsFinishTimestampMonotonic",
279 ×
.generators_finish_time
) < 0 ||
280 bus_get_uint64_property(bus
,
281 "/org/freedesktop/systemd1",
282 "org.freedesktop.systemd1.Manager",
283 "UnitsLoadStartTimestampMonotonic",
284 ×
.unitsload_start_time
) < 0 ||
285 bus_get_uint64_property(bus
,
286 "/org/freedesktop/systemd1",
287 "org.freedesktop.systemd1.Manager",
288 "UnitsLoadFinishTimestampMonotonic",
289 ×
.unitsload_finish_time
) < 0)
292 if (times
.finish_time
<= 0) {
293 log_error("Bootup is not yet finished. Please try again later.");
299 * User-instance-specific timestamps processing
300 * (see comment to reverse_offset in struct boot_times).
302 times
.reverse_offset
= times
.userspace_time
;
304 times
.firmware_time
= times
.loader_time
= times
.kernel_time
= times
.initrd_time
= times
.userspace_time
= 0;
305 subtract_timestamp(×
.finish_time
, times
.reverse_offset
);
307 subtract_timestamp(×
.security_start_time
, times
.reverse_offset
);
308 subtract_timestamp(×
.security_finish_time
, times
.reverse_offset
);
310 subtract_timestamp(×
.generators_start_time
, times
.reverse_offset
);
311 subtract_timestamp(×
.generators_finish_time
, times
.reverse_offset
);
313 subtract_timestamp(×
.unitsload_start_time
, times
.reverse_offset
);
314 subtract_timestamp(×
.unitsload_finish_time
, times
.reverse_offset
);
316 if (times
.initrd_time
)
317 times
.kernel_done_time
= times
.initrd_time
;
319 times
.kernel_done_time
= times
.userspace_time
;
329 static void free_host_info(struct host_info
*hi
) {
335 free(hi
->kernel_name
);
336 free(hi
->kernel_release
);
337 free(hi
->kernel_version
);
338 free(hi
->os_pretty_name
);
339 free(hi
->virtualization
);
340 free(hi
->architecture
);
344 DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info
*, free_host_info
);
346 static int acquire_time_data(sd_bus
*bus
, struct unit_times
**out
) {
347 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
348 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
350 struct boot_times
*boot_times
= NULL
;
351 struct unit_times
*unit_times
= NULL
;
355 r
= acquire_boot_times(bus
, &boot_times
);
359 r
= sd_bus_call_method(
361 "org.freedesktop.systemd1",
362 "/org/freedesktop/systemd1",
363 "org.freedesktop.systemd1.Manager",
368 log_error("Failed to list units: %s", bus_error_message(&error
, -r
));
372 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
374 bus_log_parse_error(r
);
378 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
379 struct unit_times
*t
;
381 if (!GREEDY_REALLOC(unit_times
, size
, c
+1)) {
389 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
391 if (bus_get_uint64_property(bus
, u
.unit_path
,
392 "org.freedesktop.systemd1.Unit",
393 "InactiveExitTimestampMonotonic",
394 &t
->activating
) < 0 ||
395 bus_get_uint64_property(bus
, u
.unit_path
,
396 "org.freedesktop.systemd1.Unit",
397 "ActiveEnterTimestampMonotonic",
398 &t
->activated
) < 0 ||
399 bus_get_uint64_property(bus
, u
.unit_path
,
400 "org.freedesktop.systemd1.Unit",
401 "ActiveExitTimestampMonotonic",
402 &t
->deactivating
) < 0 ||
403 bus_get_uint64_property(bus
, u
.unit_path
,
404 "org.freedesktop.systemd1.Unit",
405 "InactiveEnterTimestampMonotonic",
406 &t
->deactivated
) < 0) {
411 subtract_timestamp(&t
->activating
, boot_times
->reverse_offset
);
412 subtract_timestamp(&t
->activated
, boot_times
->reverse_offset
);
413 subtract_timestamp(&t
->deactivating
, boot_times
->reverse_offset
);
414 subtract_timestamp(&t
->deactivated
, boot_times
->reverse_offset
);
416 if (t
->activated
>= t
->activating
)
417 t
->time
= t
->activated
- t
->activating
;
418 else if (t
->deactivated
>= t
->activating
)
419 t
->time
= t
->deactivated
- t
->activating
;
423 if (t
->activating
== 0)
426 t
->name
= strdup(u
.id
);
434 bus_log_parse_error(r
);
443 free_unit_times(unit_times
, (unsigned) c
);
447 static int acquire_host_info(sd_bus
*bus
, struct host_info
**hi
) {
448 static const struct bus_properties_map hostname_map
[] = {
449 { "Hostname", "s", NULL
, offsetof(struct host_info
, hostname
) },
450 { "KernelName", "s", NULL
, offsetof(struct host_info
, kernel_name
) },
451 { "KernelRelease", "s", NULL
, offsetof(struct host_info
, kernel_release
) },
452 { "KernelVersion", "s", NULL
, offsetof(struct host_info
, kernel_version
) },
453 { "OperatingSystemPrettyName", "s", NULL
, offsetof(struct host_info
, os_pretty_name
) },
457 static const struct bus_properties_map manager_map
[] = {
458 { "Virtualization", "s", NULL
, offsetof(struct host_info
, virtualization
) },
459 { "Architecture", "s", NULL
, offsetof(struct host_info
, architecture
) },
463 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
464 _cleanup_(free_host_infop
) struct host_info
*host
;
467 host
= new0(struct host_info
, 1);
471 r
= bus_map_all_properties(bus
,
472 "org.freedesktop.hostname1",
473 "/org/freedesktop/hostname1",
478 log_debug_errno(r
, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error
, r
));
480 r
= bus_map_all_properties(bus
,
481 "org.freedesktop.systemd1",
482 "/org/freedesktop/systemd1",
487 return log_error_errno(r
, "Failed to get host information from systemd: %s", bus_error_message(&error
, r
));
495 static int pretty_boot_time(sd_bus
*bus
, char **_buf
) {
496 char ts
[FORMAT_TIMESPAN_MAX
];
497 struct boot_times
*t
;
498 static char buf
[4096];
502 usec_t activated_time
= USEC_INFINITY
;
503 _cleanup_free_
char* path
= NULL
, *unit_id
= NULL
;
504 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
506 r
= acquire_boot_times(bus
, &t
);
510 path
= unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET
);
514 r
= sd_bus_get_property_string(
516 "org.freedesktop.systemd1",
518 "org.freedesktop.systemd1.Unit",
523 log_error_errno(r
, "default.target doesn't seem to exist: %s", bus_error_message(&error
, r
));
527 r
= bus_get_uint64_property(bus
, path
,
528 "org.freedesktop.systemd1.Unit",
529 "ActiveEnterTimestampMonotonic",
532 log_info_errno(r
, "Could not get time to reach default.target. Continuing...");
533 activated_time
= USEC_INFINITY
;
539 size
= strpcpyf(&ptr
, size
, "Startup finished in ");
540 if (t
->firmware_time
)
541 size
= strpcpyf(&ptr
, size
, "%s (firmware) + ", format_timespan(ts
, sizeof(ts
), t
->firmware_time
- t
->loader_time
, USEC_PER_MSEC
));
543 size
= strpcpyf(&ptr
, size
, "%s (loader) + ", format_timespan(ts
, sizeof(ts
), t
->loader_time
, USEC_PER_MSEC
));
545 size
= strpcpyf(&ptr
, size
, "%s (kernel) + ", format_timespan(ts
, sizeof(ts
), t
->kernel_done_time
, USEC_PER_MSEC
));
546 if (t
->initrd_time
> 0)
547 size
= strpcpyf(&ptr
, size
, "%s (initrd) + ", format_timespan(ts
, sizeof(ts
), t
->userspace_time
- t
->initrd_time
, USEC_PER_MSEC
));
549 size
= strpcpyf(&ptr
, size
, "%s (userspace) ", format_timespan(ts
, sizeof(ts
), t
->finish_time
- t
->userspace_time
, USEC_PER_MSEC
));
550 strpcpyf(&ptr
, size
, "= %s", format_timespan(ts
, sizeof(ts
), t
->firmware_time
+ t
->finish_time
, USEC_PER_MSEC
));
552 if (unit_id
&& (activated_time
> 0 && activated_time
!= USEC_INFINITY
))
553 size
= strpcpyf(&ptr
, size
, "\n%s reached after %s in userspace", unit_id
, format_timespan(ts
, sizeof(ts
), activated_time
- t
->userspace_time
, USEC_PER_MSEC
));
554 else if (unit_id
&& activated_time
== 0)
555 size
= strpcpyf(&ptr
, size
, "\n%s was never reached", unit_id
);
556 else if (unit_id
&& activated_time
== USEC_INFINITY
)
557 size
= strpcpyf(&ptr
, size
, "\nCould not get time to reach %s.",unit_id
);
559 size
= strpcpyf(&ptr
, size
, "\ncould not find default.target");
570 static void svg_graph_box(double height
, double begin
, double end
) {
573 /* outside box, fill */
574 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
575 SCALE_X
* (end
- begin
), SCALE_Y
* height
);
577 for (i
= ((long long) (begin
/ 100000)) * 100000; i
<= end
; i
+=100000) {
578 /* lines for each second */
579 if (i
% 5000000 == 0)
580 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
581 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
582 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
583 else if (i
% 1000000 == 0)
584 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
585 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
586 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
588 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
589 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
);
593 static int analyze_plot(int argc
, char *argv
[], void *userdata
) {
594 _cleanup_(free_host_infop
) struct host_info
*host
= NULL
;
595 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
596 struct unit_times
*times
;
597 struct boot_times
*boot
;
598 int n
, m
= 1, y
= 0, r
;
600 _cleanup_free_
char *pretty_times
= NULL
;
601 struct unit_times
*u
;
603 r
= acquire_bus(true, &bus
);
605 return log_error_errno(r
, "Failed to create bus connection: %m");
607 n
= acquire_boot_times(bus
, &boot
);
611 n
= pretty_boot_time(bus
, &pretty_times
);
615 n
= acquire_host_info(bus
, &host
);
619 n
= acquire_time_data(bus
, ×
);
623 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_start
);
625 width
= SCALE_X
* (boot
->firmware_time
+ boot
->finish_time
);
629 if (boot
->firmware_time
> boot
->loader_time
)
631 if (boot
->loader_time
) {
636 if (boot
->initrd_time
)
638 if (boot
->kernel_time
)
641 for (u
= times
; u
< times
+ n
; u
++) {
642 double text_start
, text_width
;
644 if (u
->activating
< boot
->userspace_time
||
645 u
->activating
> boot
->finish_time
) {
646 u
->name
= mfree(u
->name
);
650 /* If the text cannot fit on the left side then
651 * increase the svg width so it fits on the right.
652 * TODO: calculate the text width more accurately */
653 text_width
= 8.0 * strlen(u
->name
);
654 text_start
= (boot
->firmware_time
+ u
->activating
) * SCALE_X
;
655 if (text_width
> text_start
&& text_width
+ text_start
> width
)
656 width
= text_width
+ text_start
;
658 if (u
->deactivated
> u
->activating
&& u
->deactivated
<= boot
->finish_time
659 && u
->activated
== 0 && u
->deactivating
== 0)
660 u
->activated
= u
->deactivating
= u
->deactivated
;
661 if (u
->activated
< u
->activating
|| u
->activated
> boot
->finish_time
)
662 u
->activated
= boot
->finish_time
;
663 if (u
->deactivating
< u
->activated
|| u
->activated
> boot
->finish_time
)
664 u
->deactivating
= boot
->finish_time
;
665 if (u
->deactivated
< u
->deactivating
|| u
->deactivated
> boot
->finish_time
)
666 u
->deactivated
= boot
->finish_time
;
670 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
671 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
672 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
674 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
675 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
676 80.0 + width
, 150.0 + (m
* SCALE_Y
) +
677 5 * SCALE_Y
/* legend */);
679 /* write some basic info as a comment, including some help */
680 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
681 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
682 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
683 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
684 "<!-- point your browser to this file. -->\n\n"
685 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION
);
688 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
689 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
690 " rect.background { fill: rgb(255,255,255); }\n"
691 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
692 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
693 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
694 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
695 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
696 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
697 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
698 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
699 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
700 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
701 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
702 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
703 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
705 " line.sec5 { stroke-width: 2; }\n"
706 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
707 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
708 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
709 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
710 " text.sec { font-size: 10px; }\n"
711 " ]]>\n </style>\n</defs>\n\n");
713 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
714 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times
);
715 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
716 isempty(host
->os_pretty_name
) ? "Linux" : host
->os_pretty_name
,
717 strempty(host
->hostname
),
718 strempty(host
->kernel_name
),
719 strempty(host
->kernel_release
),
720 strempty(host
->kernel_version
),
721 strempty(host
->architecture
),
722 strempty(host
->virtualization
));
724 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X
* boot
->firmware_time
));
725 svg_graph_box(m
, -(double) boot
->firmware_time
, boot
->finish_time
);
727 if (boot
->firmware_time
) {
728 svg_bar("firmware", -(double) boot
->firmware_time
, -(double) boot
->loader_time
, y
);
729 svg_text(true, -(double) boot
->firmware_time
, y
, "firmware");
732 if (boot
->loader_time
) {
733 svg_bar("loader", -(double) boot
->loader_time
, 0, y
);
734 svg_text(true, -(double) boot
->loader_time
, y
, "loader");
737 if (boot
->kernel_time
) {
738 svg_bar("kernel", 0, boot
->kernel_done_time
, y
);
739 svg_text(true, 0, y
, "kernel");
742 if (boot
->initrd_time
) {
743 svg_bar("initrd", boot
->initrd_time
, boot
->userspace_time
, y
);
744 svg_text(true, boot
->initrd_time
, y
, "initrd");
747 svg_bar("active", boot
->userspace_time
, boot
->finish_time
, y
);
748 svg_bar("security", boot
->security_start_time
, boot
->security_finish_time
, y
);
749 svg_bar("generators", boot
->generators_start_time
, boot
->generators_finish_time
, y
);
750 svg_bar("unitsload", boot
->unitsload_start_time
, boot
->unitsload_finish_time
, y
);
751 svg_text(true, boot
->userspace_time
, y
, "systemd");
754 for (u
= times
; u
< times
+ n
; u
++) {
755 char ts
[FORMAT_TIMESPAN_MAX
];
761 svg_bar("activating", u
->activating
, u
->activated
, y
);
762 svg_bar("active", u
->activated
, u
->deactivating
, y
);
763 svg_bar("deactivating", u
->deactivating
, u
->deactivated
, y
);
765 /* place the text on the left if we have passed the half of the svg width */
766 b
= u
->activating
* SCALE_X
< width
/ 2;
768 svg_text(b
, u
->activating
, y
, "%s (%s)",
769 u
->name
, format_timespan(ts
, sizeof(ts
), u
->time
, USEC_PER_MSEC
));
771 svg_text(b
, u
->activating
, y
, "%s", u
->name
);
778 svg("<g transform=\"translate(20,100)\">\n");
780 svg_bar("activating", 0, 300000, y
);
781 svg_text(true, 400000, y
, "Activating");
783 svg_bar("active", 0, 300000, y
);
784 svg_text(true, 400000, y
, "Active");
786 svg_bar("deactivating", 0, 300000, y
);
787 svg_text(true, 400000, y
, "Deactivating");
789 svg_bar("security", 0, 300000, y
);
790 svg_text(true, 400000, y
, "Setting up security module");
792 svg_bar("generators", 0, 300000, y
);
793 svg_text(true, 400000, y
, "Generators");
795 svg_bar("unitsload", 0, 300000, y
);
796 svg_text(true, 400000, y
, "Loading unit files");
803 free_unit_times(times
, (unsigned) n
);
809 static int list_dependencies_print(const char *name
, unsigned int level
, unsigned int branches
,
810 bool last
, struct unit_times
*times
, struct boot_times
*boot
) {
812 char ts
[FORMAT_TIMESPAN_MAX
], ts2
[FORMAT_TIMESPAN_MAX
];
814 for (i
= level
; i
!= 0; i
--)
815 printf("%s", special_glyph(branches
& (1 << (i
-1)) ? TREE_VERTICAL
: TREE_SPACE
));
817 printf("%s", special_glyph(last
? TREE_RIGHT
: TREE_BRANCH
));
821 printf("%s%s @%s +%s%s", ansi_highlight_red(), name
,
822 format_timespan(ts
, sizeof(ts
), times
->activating
- boot
->userspace_time
, USEC_PER_MSEC
),
823 format_timespan(ts2
, sizeof(ts2
), times
->time
, USEC_PER_MSEC
), ansi_normal());
824 else if (times
->activated
> boot
->userspace_time
)
825 printf("%s @%s", name
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
835 static int list_dependencies_get_dependencies(sd_bus
*bus
, const char *name
, char ***deps
) {
836 _cleanup_free_
char *path
= NULL
;
842 path
= unit_dbus_path_from_name(name
);
846 return bus_get_unit_property_strv(bus
, path
, "After", deps
);
849 static Hashmap
*unit_times_hashmap
;
851 static int list_dependencies_compare(const void *_a
, const void *_b
) {
852 const char **a
= (const char**) _a
, **b
= (const char**) _b
;
853 usec_t usa
= 0, usb
= 0;
854 struct unit_times
*times
;
856 times
= hashmap_get(unit_times_hashmap
, *a
);
858 usa
= times
->activated
;
859 times
= hashmap_get(unit_times_hashmap
, *b
);
861 usb
= times
->activated
;
866 static int list_dependencies_one(sd_bus
*bus
, const char *name
, unsigned int level
, char ***units
,
867 unsigned int branches
) {
868 _cleanup_strv_free_
char **deps
= NULL
;
871 usec_t service_longest
= 0;
873 struct unit_times
*times
;
874 struct boot_times
*boot
;
876 if (strv_extend(units
, name
))
879 r
= list_dependencies_get_dependencies(bus
, name
, &deps
);
883 qsort_safe(deps
, strv_length(deps
), sizeof (char*), list_dependencies_compare
);
885 r
= acquire_boot_times(bus
, &boot
);
889 STRV_FOREACH(c
, deps
) {
890 times
= hashmap_get(unit_times_hashmap
, *c
);
893 && times
->activated
<= boot
->finish_time
894 && (times
->activated
>= service_longest
895 || service_longest
== 0)) {
896 service_longest
= times
->activated
;
901 if (service_longest
== 0)
904 STRV_FOREACH(c
, deps
) {
905 times
= hashmap_get(unit_times_hashmap
, *c
);
906 if (times
&& times
->activated
&& times
->activated
<= boot
->finish_time
&& (service_longest
- times
->activated
) <= arg_fuzz
)
913 STRV_FOREACH(c
, deps
) {
914 times
= hashmap_get(unit_times_hashmap
, *c
);
917 || times
->activated
> boot
->finish_time
918 || service_longest
- times
->activated
> arg_fuzz
)
923 r
= list_dependencies_print(*c
, level
, branches
, to_print
== 0, times
, boot
);
927 if (strv_contains(*units
, *c
)) {
928 r
= list_dependencies_print("...", level
+ 1, (branches
<< 1) | (to_print
? 1 : 0),
935 r
= list_dependencies_one(bus
, *c
, level
+ 1, units
,
936 (branches
<< 1) | (to_print
? 1 : 0));
946 static int list_dependencies(sd_bus
*bus
, const char *name
) {
947 _cleanup_strv_free_
char **units
= NULL
;
948 char ts
[FORMAT_TIMESPAN_MAX
];
949 struct unit_times
*times
;
952 _cleanup_free_
char *path
= NULL
;
953 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
954 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
955 struct boot_times
*boot
;
959 path
= unit_dbus_path_from_name(name
);
963 r
= sd_bus_get_property(
965 "org.freedesktop.systemd1",
967 "org.freedesktop.systemd1.Unit",
973 log_error("Failed to get ID: %s", bus_error_message(&error
, -r
));
977 r
= sd_bus_message_read(reply
, "s", &id
);
979 return bus_log_parse_error(r
);
981 times
= hashmap_get(unit_times_hashmap
, id
);
983 r
= acquire_boot_times(bus
, &boot
);
989 printf("%s%s +%s%s\n", ansi_highlight_red(), id
,
990 format_timespan(ts
, sizeof(ts
), times
->time
, USEC_PER_MSEC
), ansi_normal());
991 else if (times
->activated
> boot
->userspace_time
)
992 printf("%s @%s\n", id
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
997 return list_dependencies_one(bus
, name
, 0, &units
, 0);
1000 static int analyze_critical_chain(int argc
, char *argv
[], void *userdata
) {
1001 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1002 struct unit_times
*times
;
1007 r
= acquire_bus(false, &bus
);
1009 return log_error_errno(r
, "Failed to create bus connection: %m");
1011 n
= acquire_time_data(bus
, ×
);
1015 h
= hashmap_new(&string_hash_ops
);
1019 for (i
= 0; i
< (unsigned) n
; i
++) {
1020 r
= hashmap_put(h
, times
[i
].name
, ×
[i
]);
1022 return log_error_errno(r
, "Failed to add entry to hashmap: %m");
1024 unit_times_hashmap
= h
;
1026 pager_open(arg_no_pager
, false);
1028 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
1029 "The time the unit takes to start is printed after the \"+\" character.\n");
1033 STRV_FOREACH(name
, strv_skip(argv
, 1))
1034 list_dependencies(bus
, *name
);
1036 list_dependencies(bus
, SPECIAL_DEFAULT_TARGET
);
1038 h
= hashmap_free(h
);
1039 free_unit_times(times
, (unsigned) n
);
1043 static int analyze_blame(int argc
, char *argv
[], void *userdata
) {
1044 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1045 struct unit_times
*times
;
1049 r
= acquire_bus(false, &bus
);
1051 return log_error_errno(r
, "Failed to create bus connection: %m");
1053 n
= acquire_time_data(bus
, ×
);
1057 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_time
);
1059 pager_open(arg_no_pager
, false);
1061 for (i
= 0; i
< (unsigned) n
; i
++) {
1062 char ts
[FORMAT_TIMESPAN_MAX
];
1064 if (times
[i
].time
> 0)
1065 printf("%16s %s\n", format_timespan(ts
, sizeof(ts
), times
[i
].time
, USEC_PER_MSEC
), times
[i
].name
);
1068 free_unit_times(times
, (unsigned) n
);
1072 static int analyze_time(int argc
, char *argv
[], void *userdata
) {
1073 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1074 _cleanup_free_
char *buf
= NULL
;
1077 r
= acquire_bus(false, &bus
);
1079 return log_error_errno(r
, "Failed to create bus connection: %m");
1081 r
= pretty_boot_time(bus
, &buf
);
1089 static int graph_one_property(sd_bus
*bus
, const UnitInfo
*u
, const char* prop
, const char *color
, char* patterns
[], char* from_patterns
[], char* to_patterns
[]) {
1090 _cleanup_strv_free_
char **units
= NULL
;
1093 bool match_patterns
;
1099 match_patterns
= strv_fnmatch(patterns
, u
->id
, 0);
1101 if (!strv_isempty(from_patterns
) &&
1103 !strv_fnmatch(from_patterns
, u
->id
, 0))
1106 r
= bus_get_unit_property_strv(bus
, u
->unit_path
, prop
, &units
);
1110 STRV_FOREACH(unit
, units
) {
1111 bool match_patterns2
;
1113 match_patterns2
= strv_fnmatch(patterns
, *unit
, 0);
1115 if (!strv_isempty(to_patterns
) &&
1117 !strv_fnmatch(to_patterns
, *unit
, 0))
1120 if (!strv_isempty(patterns
) && !match_patterns
&& !match_patterns2
)
1123 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u
->id
, *unit
, color
);
1129 static int graph_one(sd_bus
*bus
, const UnitInfo
*u
, char *patterns
[], char *from_patterns
[], char *to_patterns
[]) {
1135 if (IN_SET(arg_dot
, DEP_ORDER
, DEP_ALL
)) {
1136 r
= graph_one_property(bus
, u
, "After", "green", patterns
, from_patterns
, to_patterns
);
1141 if (IN_SET(arg_dot
, DEP_REQUIRE
, DEP_ALL
)) {
1142 r
= graph_one_property(bus
, u
, "Requires", "black", patterns
, from_patterns
, to_patterns
);
1145 r
= graph_one_property(bus
, u
, "Requisite", "darkblue", patterns
, from_patterns
, to_patterns
);
1148 r
= graph_one_property(bus
, u
, "Wants", "grey66", patterns
, from_patterns
, to_patterns
);
1151 r
= graph_one_property(bus
, u
, "Conflicts", "red", patterns
, from_patterns
, to_patterns
);
1159 static int expand_patterns(sd_bus
*bus
, char **patterns
, char ***ret
) {
1160 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1164 STRV_FOREACH(pattern
, patterns
) {
1165 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1166 _cleanup_free_
char *unit
= NULL
, *unit_id
= NULL
;
1168 if (strv_extend(&expanded_patterns
, *pattern
) < 0)
1171 if (string_is_glob(*pattern
))
1174 unit
= unit_dbus_path_from_name(*pattern
);
1178 r
= sd_bus_get_property_string(
1180 "org.freedesktop.systemd1",
1182 "org.freedesktop.systemd1.Unit",
1187 return log_error_errno(r
, "Failed to get ID: %s", bus_error_message(&error
, r
));
1189 if (!streq(*pattern
, unit_id
)) {
1190 if (strv_extend(&expanded_patterns
, unit_id
) < 0)
1195 *ret
= expanded_patterns
;
1196 expanded_patterns
= NULL
; /* do not free */
1201 static int dot(int argc
, char *argv
[], void *userdata
) {
1202 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1203 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1204 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1205 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1206 _cleanup_strv_free_
char **expanded_from_patterns
= NULL
;
1207 _cleanup_strv_free_
char **expanded_to_patterns
= NULL
;
1211 r
= acquire_bus(false, &bus
);
1213 return log_error_errno(r
, "Failed to create bus connection: %m");
1215 r
= expand_patterns(bus
, strv_skip(argv
, 1), &expanded_patterns
);
1219 r
= expand_patterns(bus
, arg_dot_from_patterns
, &expanded_from_patterns
);
1223 r
= expand_patterns(bus
, arg_dot_to_patterns
, &expanded_to_patterns
);
1227 r
= sd_bus_call_method(
1229 "org.freedesktop.systemd1",
1230 "/org/freedesktop/systemd1",
1231 "org.freedesktop.systemd1.Manager",
1237 log_error("Failed to list units: %s", bus_error_message(&error
, -r
));
1241 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1243 return bus_log_parse_error(r
);
1245 printf("digraph systemd {\n");
1247 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
1249 r
= graph_one(bus
, &u
, expanded_patterns
, expanded_from_patterns
, expanded_to_patterns
);
1254 return bus_log_parse_error(r
);
1258 log_info(" Color legend: black = Requires\n"
1259 " dark blue = Requisite\n"
1260 " dark grey = Wants\n"
1261 " red = Conflicts\n"
1262 " green = After\n");
1265 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1266 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1271 static int dump(int argc
, char *argv
[], void *userdata
) {
1272 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1273 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1274 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1275 const char *text
= NULL
;
1278 r
= acquire_bus(false, &bus
);
1280 return log_error_errno(r
, "Failed to create bus connection: %m");
1282 pager_open(arg_no_pager
, false);
1284 r
= sd_bus_call_method(
1286 "org.freedesktop.systemd1",
1287 "/org/freedesktop/systemd1",
1288 "org.freedesktop.systemd1.Manager",
1294 return log_error_errno(r
, "Failed issue method call: %s", bus_error_message(&error
, r
));
1296 r
= sd_bus_message_read(reply
, "s", &text
);
1298 return bus_log_parse_error(r
);
1300 fputs(text
, stdout
);
1304 static int set_log_level(int argc
, char *argv
[], void *userdata
) {
1305 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1306 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1312 r
= acquire_bus(false, &bus
);
1314 return log_error_errno(r
, "Failed to create bus connection: %m");
1316 r
= sd_bus_set_property(
1318 "org.freedesktop.systemd1",
1319 "/org/freedesktop/systemd1",
1320 "org.freedesktop.systemd1.Manager",
1326 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1331 static int get_log_level(int argc
, char *argv
[], void *userdata
) {
1332 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1333 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1334 _cleanup_free_
char *level
= NULL
;
1337 r
= acquire_bus(false, &bus
);
1339 return log_error_errno(r
, "Failed to create bus connection: %m");
1341 r
= sd_bus_get_property_string(
1343 "org.freedesktop.systemd1",
1344 "/org/freedesktop/systemd1",
1345 "org.freedesktop.systemd1.Manager",
1350 return log_error_errno(r
, "Failed to get log level: %s", bus_error_message(&error
, r
));
1356 static int set_log_target(int argc
, char *argv
[], void *userdata
) {
1357 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1358 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1364 r
= acquire_bus(false, &bus
);
1366 return log_error_errno(r
, "Failed to create bus connection: %m");
1368 r
= sd_bus_set_property(
1370 "org.freedesktop.systemd1",
1371 "/org/freedesktop/systemd1",
1372 "org.freedesktop.systemd1.Manager",
1378 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1383 static int get_log_target(int argc
, char *argv
[], void *userdata
) {
1384 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1385 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1386 _cleanup_free_
char *target
= NULL
;
1389 r
= acquire_bus(false, &bus
);
1391 return log_error_errno(r
, "Failed to create bus connection: %m");
1393 r
= sd_bus_get_property_string(
1395 "org.freedesktop.systemd1",
1396 "/org/freedesktop/systemd1",
1397 "org.freedesktop.systemd1.Manager",
1402 return log_error_errno(r
, "Failed to get log target: %s", bus_error_message(&error
, r
));
1409 static void dump_syscall_filter(const SyscallFilterSet
*set
) {
1410 const char *syscall
;
1412 printf("%s\n", set
->name
);
1413 printf(" # %s\n", set
->help
);
1414 NULSTR_FOREACH(syscall
, set
->value
)
1415 printf(" %s\n", syscall
);
1418 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
) {
1421 pager_open(arg_no_pager
, false);
1423 if (strv_isempty(strv_skip(argv
, 1))) {
1426 for (i
= 0; i
< _SYSCALL_FILTER_SET_MAX
; i
++) {
1429 dump_syscall_filter(syscall_filter_sets
+ i
);
1435 STRV_FOREACH(name
, strv_skip(argv
, 1)) {
1436 const SyscallFilterSet
*set
;
1441 set
= syscall_filter_set_find(*name
);
1443 /* make sure the error appears below normal output */
1446 log_error("Filter set \"%s\" not found.", *name
);
1450 dump_syscall_filter(set
);
1459 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
) {
1460 log_error("Not compiled with syscall filters, sorry.");
1465 static int test_calendar(int argc
, char *argv
[], void *userdata
) {
1470 n
= now(CLOCK_REALTIME
);
1472 STRV_FOREACH(p
, strv_skip(argv
, 1)) {
1473 _cleanup_(calendar_spec_freep
) CalendarSpec
*spec
= NULL
;
1474 _cleanup_free_
char *t
= NULL
;
1477 r
= calendar_spec_from_string(*p
, &spec
);
1479 ret
= log_error_errno(r
, "Failed to parse calendar specification '%s': %m", *p
);
1483 r
= calendar_spec_normalize(spec
);
1485 ret
= log_error_errno(r
, "Failed to normalize calendar specification '%s': %m", *p
);
1489 r
= calendar_spec_to_string(spec
, &t
);
1491 ret
= log_error_errno(r
, "Failed to fomat calendar specification '%s': %m", *p
);
1496 printf(" Original form: %s\n", *p
);
1498 printf("Normalized form: %s\n", t
);
1500 r
= calendar_spec_next_usec(spec
, n
, &next
);
1502 printf(" Next elapse: never\n");
1504 ret
= log_error_errno(r
, "Failed to determine next elapse for '%s': %m", *p
);
1507 char buffer
[CONST_MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_TIMESTAMP_RELATIVE_MAX
)];
1509 printf(" Next elapse: %s\n", format_timestamp(buffer
, sizeof(buffer
), next
));
1511 if (!in_utc_timezone())
1512 printf(" (in UTC): %s\n", format_timestamp_utc(buffer
, sizeof(buffer
), next
));
1514 printf(" From now: %s\n", format_timestamp_relative(buffer
, sizeof(buffer
), next
));
1524 static int service_watchdogs(int argc
, char *argv
[], void *userdata
) {
1525 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1526 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1532 b
= parse_boolean(argv
[1]);
1534 log_error("Failed to parse service-watchdogs argument.");
1538 r
= acquire_bus(false, &bus
);
1540 return log_error_errno(r
, "Failed to create bus connection: %m");
1542 r
= sd_bus_set_property(
1544 "org.freedesktop.systemd1",
1545 "/org/freedesktop/systemd1",
1546 "org.freedesktop.systemd1.Manager",
1552 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1557 static int do_verify(int argc
, char *argv
[], void *userdata
) {
1558 return verify_units(strv_skip(argv
, 1),
1559 arg_user
? UNIT_FILE_USER
: UNIT_FILE_SYSTEM
,
1564 static int help(int argc
, char *argv
[], void *userdata
) {
1566 pager_open(arg_no_pager
, false);
1568 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1569 "Profile systemd, show unit dependencies, check unit files.\n\n"
1570 " -h --help Show this help\n"
1571 " --version Show package version\n"
1572 " --no-pager Do not pipe output into a pager\n"
1573 " --system Operate on system systemd instance\n"
1574 " --user Operate on user systemd instance\n"
1575 " -H --host=[USER@]HOST Operate on remote host\n"
1576 " -M --machine=CONTAINER Operate on local container\n"
1577 " --order Show only order in the graph\n"
1578 " --require Show only requirement in the graph\n"
1579 " --from-pattern=GLOB Show only origins in the graph\n"
1580 " --to-pattern=GLOB Show only destinations in the graph\n"
1581 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1582 " earlier than the latest in the branch\n"
1583 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1584 " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n"
1586 " time Print time spent in the kernel\n"
1587 " blame Print list of running units ordered by time to init\n"
1588 " critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
1589 " plot Output SVG graphic showing service initialization\n"
1590 " dot [UNIT...] Output dependency graph in man:dot(1) format\n"
1591 " set-log-level LEVEL Set logging threshold for manager\n"
1592 " set-log-target TARGET Set logging target for manager\n"
1593 " get-log-level Get logging threshold for manager\n"
1594 " get-log-target Get logging target for manager\n"
1595 " dump Output state serialization of service manager\n"
1596 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
1597 " verify FILE... Check unit files for correctness\n"
1598 " calendar SPEC... Validate repetitive calendar time events\n"
1599 " service-watchdogs on/off Enable/disable service watchdogs\n"
1600 , program_invocation_short_name
);
1602 /* When updating this list, including descriptions, apply
1603 * changes to shell-completion/bash/systemd-analyze and
1604 * shell-completion/zsh/_systemd-analyze too. */
1609 static int parse_argv(int argc
, char *argv
[]) {
1611 ARG_VERSION
= 0x100,
1616 ARG_DOT_FROM_PATTERN
,
1624 static const struct option options
[] = {
1625 { "help", no_argument
, NULL
, 'h' },
1626 { "version", no_argument
, NULL
, ARG_VERSION
},
1627 { "order", no_argument
, NULL
, ARG_ORDER
},
1628 { "require", no_argument
, NULL
, ARG_REQUIRE
},
1629 { "user", no_argument
, NULL
, ARG_USER
},
1630 { "system", no_argument
, NULL
, ARG_SYSTEM
},
1631 { "from-pattern", required_argument
, NULL
, ARG_DOT_FROM_PATTERN
},
1632 { "to-pattern", required_argument
, NULL
, ARG_DOT_TO_PATTERN
},
1633 { "fuzz", required_argument
, NULL
, ARG_FUZZ
},
1634 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1635 { "man", optional_argument
, NULL
, ARG_MAN
},
1636 { "generators", optional_argument
, NULL
, ARG_GENERATORS
},
1637 { "host", required_argument
, NULL
, 'H' },
1638 { "machine", required_argument
, NULL
, 'M' },
1647 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
1651 return help(0, NULL
, NULL
);
1665 arg_dot
= DEP_ORDER
;
1669 arg_dot
= DEP_REQUIRE
;
1672 case ARG_DOT_FROM_PATTERN
:
1673 if (strv_extend(&arg_dot_from_patterns
, optarg
) < 0)
1678 case ARG_DOT_TO_PATTERN
:
1679 if (strv_extend(&arg_dot_to_patterns
, optarg
) < 0)
1685 r
= parse_sec(optarg
, &arg_fuzz
);
1691 arg_no_pager
= true;
1695 arg_transport
= BUS_TRANSPORT_REMOTE
;
1700 arg_transport
= BUS_TRANSPORT_MACHINE
;
1706 r
= parse_boolean(optarg
);
1708 log_error("Failed to parse --man= argument.");
1718 case ARG_GENERATORS
:
1720 r
= parse_boolean(optarg
);
1722 log_error("Failed to parse --generators= argument.");
1726 arg_generators
= !!r
;
1728 arg_generators
= true;
1736 assert_not_reached("Unhandled option code.");
1739 return 1; /* work to do */
1742 int main(int argc
, char *argv
[]) {
1744 static const Verb verbs
[] = {
1745 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
1746 { "time", VERB_ANY
, 1, VERB_DEFAULT
, analyze_time
},
1747 { "blame", VERB_ANY
, 1, 0, analyze_blame
},
1748 { "critical-chain", VERB_ANY
, VERB_ANY
, 0, analyze_critical_chain
},
1749 { "plot", VERB_ANY
, 1, 0, analyze_plot
},
1750 { "dot", VERB_ANY
, VERB_ANY
, 0, dot
},
1751 { "set-log-level", 2, 2, 0, set_log_level
},
1752 { "get-log-level", VERB_ANY
, 1, 0, get_log_level
},
1753 { "set-log-target", 2, 2, 0, set_log_target
},
1754 { "get-log-target", VERB_ANY
, 1, 0, get_log_target
},
1755 { "dump", VERB_ANY
, 1, 0, dump
},
1756 { "syscall-filter", VERB_ANY
, VERB_ANY
, 0, dump_syscall_filters
},
1757 { "verify", 2, VERB_ANY
, 0, do_verify
},
1758 { "calendar", 2, VERB_ANY
, 0, test_calendar
},
1759 { "service-watchdogs", 2, 2, 0, service_watchdogs
},
1765 setlocale(LC_ALL
, "");
1766 setlocale(LC_NUMERIC
, "C"); /* we want to format/parse floats in C style */
1768 log_parse_environment();
1771 r
= parse_argv(argc
, argv
);
1775 r
= dispatch_verb(argc
, argv
, verbs
, NULL
);
1780 strv_free(arg_dot_from_patterns
);
1781 strv_free(arg_dot_to_patterns
);
1783 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;