1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include "analyze-critical-chain.h"
5 #include "analyze-time-data.h"
9 #include "terminal-util.h"
10 #include "sort-util.h"
12 #include "bus-error.h"
14 static int list_dependencies_print(
22 for (unsigned i
= level
; i
!= 0; i
--)
23 printf("%s", special_glyph(branches
& (1 << (i
-1)) ? SPECIAL_GLYPH_TREE_VERTICAL
: SPECIAL_GLYPH_TREE_SPACE
));
25 printf("%s", special_glyph(last
? SPECIAL_GLYPH_TREE_RIGHT
: SPECIAL_GLYPH_TREE_BRANCH
));
29 printf("%s%s @%s +%s%s", ansi_highlight_red(), name
,
30 FORMAT_TIMESPAN(times
->activating
- boot
->userspace_time
, USEC_PER_MSEC
),
31 FORMAT_TIMESPAN(times
->time
, USEC_PER_MSEC
), ansi_normal());
32 else if (times
->activated
> boot
->userspace_time
)
33 printf("%s @%s", name
, FORMAT_TIMESPAN(times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
43 static int list_dependencies_get_dependencies(sd_bus
*bus
, const char *name
, char ***deps
) {
44 _cleanup_free_
char *path
= NULL
;
50 path
= unit_dbus_path_from_name(name
);
54 return bus_get_unit_property_strv(bus
, path
, "After", deps
);
57 static Hashmap
*unit_times_hashmap
;
59 static int list_dependencies_compare(char *const *a
, char *const *b
) {
60 usec_t usa
= 0, usb
= 0;
63 times
= hashmap_get(unit_times_hashmap
, *a
);
65 usa
= times
->activated
;
66 times
= hashmap_get(unit_times_hashmap
, *b
);
68 usb
= times
->activated
;
73 static bool times_in_range(const UnitTimes
*times
, const BootTimes
*boot
) {
74 return times
&& times
->activated
> 0 && times
->activated
<= boot
->finish_time
;
77 static int list_dependencies_one(sd_bus
*bus
, const char *name
, unsigned level
, char ***units
, unsigned branches
) {
78 _cleanup_strv_free_
char **deps
= NULL
;
81 usec_t service_longest
= 0;
86 if (strv_extend(units
, name
))
89 r
= list_dependencies_get_dependencies(bus
, name
, &deps
);
93 typesafe_qsort(deps
, strv_length(deps
), list_dependencies_compare
);
95 r
= acquire_boot_times(bus
, &boot
);
99 STRV_FOREACH(c
, deps
) {
100 times
= hashmap_get(unit_times_hashmap
, *c
); /* lgtm [cpp/inconsistent-null-check] */
101 if (times_in_range(times
, boot
) && times
->activated
>= service_longest
)
102 service_longest
= times
->activated
;
105 if (service_longest
== 0)
108 STRV_FOREACH(c
, deps
) {
109 times
= hashmap_get(unit_times_hashmap
, *c
); /* lgtm [cpp/inconsistent-null-check] */
110 if (times_in_range(times
, boot
) && service_longest
- times
->activated
<= arg_fuzz
)
117 STRV_FOREACH(c
, deps
) {
118 times
= hashmap_get(unit_times_hashmap
, *c
); /* lgtm [cpp/inconsistent-null-check] */
119 if (!times_in_range(times
, boot
) || service_longest
- times
->activated
> arg_fuzz
)
124 r
= list_dependencies_print(*c
, level
, branches
, to_print
== 0, times
, boot
);
128 if (strv_contains(*units
, *c
)) {
129 r
= list_dependencies_print("...", level
+ 1, (branches
<< 1) | (to_print
? 1 : 0),
136 r
= list_dependencies_one(bus
, *c
, level
+ 1, units
, (branches
<< 1) | (to_print
? 1 : 0));
146 static int list_dependencies(sd_bus
*bus
, const char *name
) {
147 _cleanup_strv_free_
char **units
= NULL
;
151 _cleanup_free_
char *path
= NULL
;
152 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
153 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
158 path
= unit_dbus_path_from_name(name
);
162 r
= sd_bus_get_property(
164 "org.freedesktop.systemd1",
166 "org.freedesktop.systemd1.Unit",
172 return log_error_errno(r
, "Failed to get ID: %s", bus_error_message(&error
, r
));
174 r
= sd_bus_message_read(reply
, "s", &id
);
176 return bus_log_parse_error(r
);
178 times
= hashmap_get(unit_times_hashmap
, id
);
180 r
= acquire_boot_times(bus
, &boot
);
186 printf("%s%s +%s%s\n", ansi_highlight_red(), id
,
187 FORMAT_TIMESPAN(times
->time
, USEC_PER_MSEC
), ansi_normal());
188 else if (times
->activated
> boot
->userspace_time
)
189 printf("%s @%s\n", id
,
190 FORMAT_TIMESPAN(times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
195 return list_dependencies_one(bus
, name
, 0, &units
, 0);
198 int verb_critical_chain(int argc
, char *argv
[], void *userdata
) {
199 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
200 _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
, ×
);
212 h
= hashmap_new(&string_hash_ops
);
216 for (UnitTimes
*u
= times
; u
->has_data
; u
++) {
217 r
= hashmap_put(h
, u
->name
, u
);
219 return log_error_errno(r
, "Failed to add entry to hashmap: %m");
221 unit_times_hashmap
= h
;
223 pager_open(arg_pager_flags
);
225 puts("The time when unit became active or started is printed after the \"@\" character.\n"
226 "The time the unit took to start is printed after the \"+\" character.\n");
230 STRV_FOREACH(name
, strv_skip(argv
, 1))
231 list_dependencies(bus
, *name
);
233 list_dependencies(bus
, SPECIAL_DEFAULT_TARGET
);