]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/analyze/analyze-critical-chain.c
test: use SYSLOG_IDENTIFIER= filter instead of "journalctl -u"
[thirdparty/systemd.git] / src / analyze / analyze-critical-chain.c
CommitLineData
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
15static Hashmap *unit_times_hashmap = NULL;
16STATIC_DESTRUCTOR_REGISTER(unit_times_hashmap, hashmap_freep);
17
ef215fa7
LP
18static 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
44static 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
58static 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
72static bool times_in_range(const UnitTimes *times, const BootTimes *boot) {
73 return times && times->activated > 0 && times->activated <= boot->finish_time;
74}
75
76static 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
144static 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 196int 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, &times);
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}