1:255.16-alt1
[systemd_ALT.git] / src / analyze / analyze-critical-chain.c
blob4a7f452eaf5f375da440b06e8a5baf539610b142
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "analyze-critical-chain.h"
4 #include "analyze-time-data.h"
5 #include "analyze.h"
6 #include "bus-error.h"
7 #include "copy.h"
8 #include "path-util.h"
9 #include "sort-util.h"
10 #include "special.h"
11 #include "static-destruct.h"
12 #include "strv.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(
19 const char *name,
20 unsigned level,
21 unsigned branches,
22 bool last,
23 UnitTimes *times,
24 BootTimes *boot) {
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));
31 if (times) {
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));
38 else
39 printf("%s", name);
40 } else
41 printf("%s", name);
42 printf("\n");
44 return 0;
47 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
48 _cleanup_free_ char *path = NULL;
50 assert(bus);
51 assert(name);
52 assert(deps);
54 path = unit_dbus_path_from_name(name);
55 if (!path)
56 return -ENOMEM;
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;
63 UnitTimes *times;
65 times = hashmap_get(unit_times_hashmap, *a);
66 if (times)
67 usa = times->activated;
68 times = hashmap_get(unit_times_hashmap, *b);
69 if (times)
70 usb = times->activated;
72 return CMP(usb, usa);
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;
81 int r;
82 usec_t service_longest = 0;
83 int to_print = 0;
84 UnitTimes *times;
85 BootTimes *boot;
87 if (strv_extend(units, name))
88 return log_oom();
90 r = list_dependencies_get_dependencies(bus, name, &deps);
91 if (r < 0)
92 return r;
94 typesafe_qsort(deps, strv_length(deps), list_dependencies_compare);
96 r = acquire_boot_times(bus, /* require_finished = */ true, &boot);
97 if (r < 0)
98 return r;
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)
107 return r;
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)
112 to_print++;
115 if (!to_print)
116 return r;
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)
121 continue;
123 to_print--;
125 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
126 if (r < 0)
127 return r;
129 if (strv_contains(*units, *c)) {
130 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
131 true, NULL, boot);
132 if (r < 0)
133 return r;
134 continue;
137 r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (to_print ? 1 : 0));
138 if (r < 0)
139 return r;
141 if (to_print == 0)
142 break;
144 return 0;
147 static int list_dependencies(sd_bus *bus, const char *name) {
148 _cleanup_strv_free_ char **units = NULL;
149 UnitTimes *times;
150 int r;
151 const char *id;
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;
155 BootTimes *boot;
157 assert(bus);
159 path = unit_dbus_path_from_name(name);
160 if (!path)
161 return -ENOMEM;
163 r = sd_bus_get_property(
164 bus,
165 "org.freedesktop.systemd1",
166 path,
167 "org.freedesktop.systemd1.Unit",
168 "Id",
169 &error,
170 &reply,
171 "s");
172 if (r < 0)
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);
176 if (r < 0)
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);
182 if (r < 0)
183 return r;
185 if (times) {
186 if (times->time)
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));
192 else
193 printf("%s\n", id);
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;
202 int n, r;
204 r = acquire_bus(&bus, NULL);
205 if (r < 0)
206 return bus_log_connect_error(r, arg_transport);
208 n = acquire_time_data(bus, /* require_finished = */ true, &times);
209 if (n <= 0)
210 return n;
212 for (UnitTimes *u = times; u->has_data; u++) {
213 r = hashmap_ensure_put(&unit_times_hashmap, &string_hash_ops, u->name, u);
214 if (r < 0)
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");
223 if (argc > 1)
224 STRV_FOREACH(name, strv_skip(argv, 1))
225 list_dependencies(bus, *name);
226 else
227 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
229 return EXIT_SUCCESS;