]>
Commit | Line | Data |
---|---|---|
ef215fa7 LP |
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | ||
ef215fa7 LP |
3 | #include "analyze-critical-chain.h" |
4 | #include "analyze-time-data.h" | |
e2d99934 ZJS |
5 | #include "analyze.h" |
6 | #include "bus-error.h" | |
ef215fa7 LP |
7 | #include "copy.h" |
8 | #include "path-util.h" | |
ef215fa7 LP |
9 | #include "sort-util.h" |
10 | #include "special.h" | |
d5dcd00b | 11 | #include "static-destruct.h" |
e2d99934 ZJS |
12 | #include "strv.h" |
13 | #include "terminal-util.h" | |
ef215fa7 | 14 | |
d5dcd00b ZJS |
15 | static Hashmap *unit_times_hashmap = NULL; |
16 | STATIC_DESTRUCTOR_REGISTER(unit_times_hashmap, hashmap_freep); | |
17 | ||
ef215fa7 LP |
18 | static int list_dependencies_print( |
19 | const char *name, | |
20 | unsigned level, | |
21 | unsigned branches, | |
22 | bool last, | |
23 | UnitTimes *times, | |
24 | BootTimes *boot) { | |
25 | ||
1d640a04 | 26 | for (unsigned i = level; i > 0; i--) |
ef215fa7 LP |
27 | printf("%s", special_glyph(branches & (1 << (i-1)) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE)); |
28 | ||
29 | printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH)); | |
30 | ||
1d640a04 | 31 | if (times && times->activating >= boot->userspace_time) { |
7bbfcf64 | 32 | if (timestamp_is_set(times->time)) |
1d640a04 | 33 | printf("%s%s @%s +%s%s\n", ansi_highlight_red(), name, |
ef215fa7 LP |
34 | FORMAT_TIMESPAN(times->activating - boot->userspace_time, USEC_PER_MSEC), |
35 | FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal()); | |
ef215fa7 | 36 | else |
1d640a04 | 37 | printf("%s @%s\n", name, FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC)); |
ef215fa7 | 38 | } else |
1d640a04 | 39 | printf("%s\n", name); |
ef215fa7 LP |
40 | |
41 | return 0; | |
42 | } | |
43 | ||
44 | static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) { | |
45 | _cleanup_free_ char *path = NULL; | |
46 | ||
47 | assert(bus); | |
48 | assert(name); | |
49 | assert(deps); | |
50 | ||
51 | path = unit_dbus_path_from_name(name); | |
52 | if (!path) | |
53 | return -ENOMEM; | |
54 | ||
55 | return bus_get_unit_property_strv(bus, path, "After", deps); | |
56 | } | |
57 | ||
ef215fa7 LP |
58 | static int list_dependencies_compare(char *const *a, char *const *b) { |
59 | usec_t usa = 0, usb = 0; | |
60 | UnitTimes *times; | |
61 | ||
62 | times = hashmap_get(unit_times_hashmap, *a); | |
63 | if (times) | |
64 | usa = times->activated; | |
65 | times = hashmap_get(unit_times_hashmap, *b); | |
66 | if (times) | |
67 | usb = times->activated; | |
68 | ||
69 | return CMP(usb, usa); | |
70 | } | |
71 | ||
72 | static bool times_in_range(const UnitTimes *times, const BootTimes *boot) { | |
73 | return times && times->activated > 0 && times->activated <= boot->finish_time; | |
74 | } | |
75 | ||
76 | static int list_dependencies_one(sd_bus *bus, const char *name, unsigned level, char ***units, unsigned branches) { | |
77 | _cleanup_strv_free_ char **deps = NULL; | |
ef215fa7 LP |
78 | int r; |
79 | usec_t service_longest = 0; | |
80 | int to_print = 0; | |
81 | UnitTimes *times; | |
82 | BootTimes *boot; | |
83 | ||
84 | if (strv_extend(units, name)) | |
85 | return log_oom(); | |
86 | ||
87 | r = list_dependencies_get_dependencies(bus, name, &deps); | |
88 | if (r < 0) | |
89 | return r; | |
90 | ||
91 | typesafe_qsort(deps, strv_length(deps), list_dependencies_compare); | |
92 | ||
63d7632d | 93 | r = acquire_boot_times(bus, /* require_finished = */ true, &boot); |
ef215fa7 LP |
94 | if (r < 0) |
95 | return r; | |
96 | ||
97 | STRV_FOREACH(c, deps) { | |
f37508d5 | 98 | times = hashmap_get(unit_times_hashmap, *c); |
ef215fa7 LP |
99 | if (times_in_range(times, boot) && times->activated >= service_longest) |
100 | service_longest = times->activated; | |
101 | } | |
102 | ||
103 | if (service_longest == 0) | |
104 | return r; | |
105 | ||
106 | STRV_FOREACH(c, deps) { | |
f37508d5 | 107 | times = hashmap_get(unit_times_hashmap, *c); |
ef215fa7 LP |
108 | if (times_in_range(times, boot) && service_longest - times->activated <= arg_fuzz) |
109 | to_print++; | |
110 | } | |
111 | ||
112 | if (!to_print) | |
113 | return r; | |
114 | ||
115 | STRV_FOREACH(c, deps) { | |
f37508d5 | 116 | times = hashmap_get(unit_times_hashmap, *c); |
ef215fa7 LP |
117 | if (!times_in_range(times, boot) || service_longest - times->activated > arg_fuzz) |
118 | continue; | |
119 | ||
120 | to_print--; | |
121 | ||
122 | r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot); | |
123 | if (r < 0) | |
124 | return r; | |
125 | ||
126 | if (strv_contains(*units, *c)) { | |
127 | r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0), | |
128 | true, NULL, boot); | |
129 | if (r < 0) | |
130 | return r; | |
131 | continue; | |
132 | } | |
133 | ||
134 | r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (to_print ? 1 : 0)); | |
135 | if (r < 0) | |
136 | return r; | |
137 | ||
138 | if (to_print == 0) | |
139 | break; | |
140 | } | |
141 | return 0; | |
142 | } | |
143 | ||
144 | static int list_dependencies(sd_bus *bus, const char *name) { | |
145 | _cleanup_strv_free_ char **units = NULL; | |
146 | UnitTimes *times; | |
147 | int r; | |
148 | const char *id; | |
149 | _cleanup_free_ char *path = NULL; | |
150 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
151 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
152 | BootTimes *boot; | |
153 | ||
154 | assert(bus); | |
155 | ||
156 | path = unit_dbus_path_from_name(name); | |
157 | if (!path) | |
158 | return -ENOMEM; | |
159 | ||
160 | r = sd_bus_get_property( | |
161 | bus, | |
162 | "org.freedesktop.systemd1", | |
163 | path, | |
164 | "org.freedesktop.systemd1.Unit", | |
165 | "Id", | |
166 | &error, | |
167 | &reply, | |
168 | "s"); | |
169 | if (r < 0) | |
170 | return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); | |
171 | ||
172 | r = sd_bus_message_read(reply, "s", &id); | |
173 | if (r < 0) | |
174 | return bus_log_parse_error(r); | |
175 | ||
176 | times = hashmap_get(unit_times_hashmap, id); | |
177 | ||
63d7632d | 178 | r = acquire_boot_times(bus, /* require_finished = */ true, &boot); |
ef215fa7 LP |
179 | if (r < 0) |
180 | return r; | |
181 | ||
182 | if (times) { | |
183 | if (times->time) | |
184 | printf("%s%s +%s%s\n", ansi_highlight_red(), id, | |
185 | FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal()); | |
186 | else if (times->activated > boot->userspace_time) | |
187 | printf("%s @%s\n", id, | |
188 | FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC)); | |
189 | else | |
190 | printf("%s\n", id); | |
191 | } | |
192 | ||
193 | return list_dependencies_one(bus, name, 0, &units, 0); | |
194 | } | |
195 | ||
ef38bedb | 196 | int verb_critical_chain(int argc, char *argv[], void *userdata) { |
ef215fa7 LP |
197 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; |
198 | _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL; | |
ef215fa7 LP |
199 | int n, r; |
200 | ||
201 | r = acquire_bus(&bus, NULL); | |
202 | if (r < 0) | |
203 | return bus_log_connect_error(r, arg_transport); | |
204 | ||
63d7632d | 205 | n = acquire_time_data(bus, /* require_finished = */ true, ×); |
ef215fa7 LP |
206 | if (n <= 0) |
207 | return n; | |
208 | ||
ef215fa7 | 209 | for (UnitTimes *u = times; u->has_data; u++) { |
d5dcd00b | 210 | r = hashmap_ensure_put(&unit_times_hashmap, &string_hash_ops, u->name, u); |
ef215fa7 LP |
211 | if (r < 0) |
212 | return log_error_errno(r, "Failed to add entry to hashmap: %m"); | |
213 | } | |
ef215fa7 LP |
214 | |
215 | pager_open(arg_pager_flags); | |
216 | ||
217 | puts("The time when unit became active or started is printed after the \"@\" character.\n" | |
218 | "The time the unit took to start is printed after the \"+\" character.\n"); | |
219 | ||
de010b0b | 220 | if (argc > 1) |
ef215fa7 LP |
221 | STRV_FOREACH(name, strv_skip(argv, 1)) |
222 | list_dependencies(bus, *name); | |
de010b0b | 223 | else |
ef215fa7 LP |
224 | list_dependencies(bus, SPECIAL_DEFAULT_TARGET); |
225 | ||
fddad5f4 | 226 | return EXIT_SUCCESS; |
ef215fa7 | 227 | } |