1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "analyze-critical-chain.h"
4 #include "analyze-time-data.h"
11 #include "static-destruct.h"
13 #include "terminal-util.h"
15 static Hashmap
*unit_times_hashmap
= NULL
;
16 STATIC_DESTRUCTOR_REGISTER(unit_times_hashmap
, hashmap_freep
);
18 static int list_dependencies_print(
26 for (unsigned i
= level
; i
!= 0; i
--)
27 printf("%s", special_glyph(branches
& (1 << (i
-1)) ? SPECIAL_GLYPH_TREE_VERTICAL
: SPECIAL_GLYPH_TREE_SPACE
));
29 printf("%s", special_glyph(last
? SPECIAL_GLYPH_TREE_RIGHT
: SPECIAL_GLYPH_TREE_BRANCH
));
32 if (timestamp_is_set(times
->time
))
33 printf("%s%s @%s +%s%s", ansi_highlight_red(), name
,
34 FORMAT_TIMESPAN(times
->activating
- boot
->userspace_time
, USEC_PER_MSEC
),
35 FORMAT_TIMESPAN(times
->time
, USEC_PER_MSEC
), ansi_normal());
36 else if (times
->activated
> boot
->userspace_time
)
37 printf("%s @%s", name
, FORMAT_TIMESPAN(times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
47 static int list_dependencies_get_dependencies(sd_bus
*bus
, const char *name
, char ***deps
) {
48 _cleanup_free_
char *path
= NULL
;
54 path
= unit_dbus_path_from_name(name
);
58 return bus_get_unit_property_strv(bus
, path
, "After", deps
);
61 static int list_dependencies_compare(char *const *a
, char *const *b
) {
62 usec_t usa
= 0, usb
= 0;
65 times
= hashmap_get(unit_times_hashmap
, *a
);
67 usa
= times
->activated
;
68 times
= hashmap_get(unit_times_hashmap
, *b
);
70 usb
= times
->activated
;
75 static bool times_in_range(const UnitTimes
*times
, const BootTimes
*boot
) {
76 return times
&& times
->activated
> 0 && times
->activated
<= boot
->finish_time
;
79 static int list_dependencies_one(sd_bus
*bus
, const char *name
, unsigned level
, char ***units
, unsigned branches
) {
80 _cleanup_strv_free_
char **deps
= NULL
;
82 usec_t service_longest
= 0;
87 if (strv_extend(units
, name
))
90 r
= list_dependencies_get_dependencies(bus
, name
, &deps
);
94 typesafe_qsort(deps
, strv_length(deps
), list_dependencies_compare
);
96 r
= acquire_boot_times(bus
, /* require_finished = */ true, &boot
);
100 STRV_FOREACH(c
, deps
) {
101 times
= hashmap_get(unit_times_hashmap
, *c
);
102 if (times_in_range(times
, boot
) && times
->activated
>= service_longest
)
103 service_longest
= times
->activated
;
106 if (service_longest
== 0)
109 STRV_FOREACH(c
, deps
) {
110 times
= hashmap_get(unit_times_hashmap
, *c
);
111 if (times_in_range(times
, boot
) && service_longest
- times
->activated
<= arg_fuzz
)
118 STRV_FOREACH(c
, deps
) {
119 times
= hashmap_get(unit_times_hashmap
, *c
);
120 if (!times_in_range(times
, boot
) || service_longest
- times
->activated
> arg_fuzz
)
125 r
= list_dependencies_print(*c
, level
, branches
, to_print
== 0, times
, boot
);
129 if (strv_contains(*units
, *c
)) {
130 r
= list_dependencies_print("...", level
+ 1, (branches
<< 1) | (to_print
? 1 : 0),
137 r
= list_dependencies_one(bus
, *c
, level
+ 1, units
, (branches
<< 1) | (to_print
? 1 : 0));
147 static int list_dependencies(sd_bus
*bus
, const char *name
) {
148 _cleanup_strv_free_
char **units
= NULL
;
152 _cleanup_free_
char *path
= NULL
;
153 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
154 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
159 path
= unit_dbus_path_from_name(name
);
163 r
= sd_bus_get_property(
165 "org.freedesktop.systemd1",
167 "org.freedesktop.systemd1.Unit",
173 return log_error_errno(r
, "Failed to get ID: %s", bus_error_message(&error
, r
));
175 r
= sd_bus_message_read(reply
, "s", &id
);
177 return bus_log_parse_error(r
);
179 times
= hashmap_get(unit_times_hashmap
, id
);
181 r
= acquire_boot_times(bus
, /* require_finished = */ true, &boot
);
187 printf("%s%s +%s%s\n", ansi_highlight_red(), id
,
188 FORMAT_TIMESPAN(times
->time
, USEC_PER_MSEC
), ansi_normal());
189 else if (times
->activated
> boot
->userspace_time
)
190 printf("%s @%s\n", id
,
191 FORMAT_TIMESPAN(times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
196 return list_dependencies_one(bus
, name
, 0, &units
, 0);
199 int verb_critical_chain(int argc
, char *argv
[], void *userdata
) {
200 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
201 _cleanup_(unit_times_free_arrayp
) UnitTimes
*times
= NULL
;
204 r
= acquire_bus(&bus
, NULL
);
206 return bus_log_connect_error(r
, arg_transport
);
208 n
= acquire_time_data(bus
, /* require_finished = */ true, ×
);
212 for (UnitTimes
*u
= times
; u
->has_data
; u
++) {
213 r
= hashmap_ensure_put(&unit_times_hashmap
, &string_hash_ops
, u
->name
, u
);
215 return log_error_errno(r
, "Failed to add entry to hashmap: %m");
218 pager_open(arg_pager_flags
);
220 puts("The time when unit became active or started is printed after the \"@\" character.\n"
221 "The time the unit took to start is printed after the \"+\" character.\n");
224 STRV_FOREACH(name
, strv_skip(argv
, 1))
225 list_dependencies(bus
, *name
);
227 list_dependencies(bus
, SPECIAL_DEFAULT_TARGET
);