]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/analyze/systemd-analyze.c
systemctl: move "dump" command from systemctl to systemd-analyze
[thirdparty/systemd.git] / src / analyze / systemd-analyze.c
CommitLineData
2265fbf7
SP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
ffaa0e25
LP
6 Copyright 2010-2013 Lennart Poettering
7 Copyright 2013 Simon Peeters
2265fbf7
SP
8
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21***/
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <getopt.h>
26#include <locale.h>
27#include <sys/utsname.h>
e55933db 28#include <fnmatch.h>
2265fbf7
SP
29
30#include "install.h"
31#include "log.h"
32#include "dbus-common.h"
33#include "build.h"
34#include "util.h"
35#include "strxcpyx.h"
a5c32cff 36#include "fileio.h"
e55933db 37#include "strv.h"
bb150966
HH
38#include "unit-name.h"
39#include "special.h"
40#include "hashmap.h"
9ea9d4cf 41#include "pager.h"
2265fbf7 42
2f6eb835
LP
43#define SCALE_X (0.1 / 1000.0) /* pixels per us */
44#define SCALE_Y 20.0
45
2265fbf7 46#define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
c170f3a4 47
2265fbf7 48#define svg(...) printf(__VA_ARGS__)
c170f3a4
LP
49
50#define svg_bar(class, x1, x2, y) \
2265fbf7 51 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
c170f3a4 52 (class), \
2f6eb835
LP
53 SCALE_X * (x1), SCALE_Y * (y), \
54 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
c170f3a4
LP
55
56#define svg_text(b, x, y, format, ...) \
57 do { \
2f6eb835 58 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
c170f3a4
LP
59 svg(format, ## __VA_ARGS__); \
60 svg("</text>\n"); \
2265fbf7
SP
61 } while(false)
62
63static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
1700761b
SP
64static enum dot {
65 DEP_ALL,
66 DEP_ORDER,
67 DEP_REQUIRE
68} arg_dot = DEP_ALL;
e55933db
ŁS
69static char** arg_dot_from_patterns = NULL;
70static char** arg_dot_to_patterns = NULL;
9ea9d4cf
LP
71static usec_t arg_fuzz = 0;
72static bool arg_no_pager = false;
bb150966 73
2265fbf7 74struct boot_times {
c170f3a4
LP
75 usec_t firmware_time;
76 usec_t loader_time;
77 usec_t kernel_time;
78 usec_t kernel_done_time;
79 usec_t initrd_time;
80 usec_t userspace_time;
81 usec_t finish_time;
518d10e9
UTL
82 usec_t generators_start_time;
83 usec_t generators_finish_time;
d9acfb71
TA
84 usec_t unitsload_start_time;
85 usec_t unitsload_finish_time;
2265fbf7 86};
9ea9d4cf 87
2265fbf7
SP
88struct unit_times {
89 char *name;
c170f3a4
LP
90 usec_t ixt;
91 usec_t iet;
92 usec_t axt;
93 usec_t aet;
94 usec_t time;
2265fbf7
SP
95};
96
9ea9d4cf
LP
97static void pager_open_if_enabled(void) {
98
99 if (arg_no_pager)
100 return;
101
102 pager_open(false);
103}
104
c170f3a4 105static int bus_get_uint64_property(DBusConnection *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
2265fbf7 106 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
2265fbf7 107 DBusMessageIter iter, sub;
c170f3a4 108 int r;
2265fbf7 109
c170f3a4 110 r = bus_method_call_with_reply(
2265fbf7
SP
111 bus,
112 "org.freedesktop.systemd1",
113 path,
114 "org.freedesktop.DBus.Properties",
115 "Get",
116 &reply,
117 NULL,
118 DBUS_TYPE_STRING, &interface,
119 DBUS_TYPE_STRING, &property,
120 DBUS_TYPE_INVALID);
121 if (r < 0)
122 return r;
123
124 if (!dbus_message_iter_init(reply, &iter) ||
125 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
126 log_error("Failed to parse reply.");
127 return -EIO;
128 }
129
130 dbus_message_iter_recurse(&iter, &sub);
131
132 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64) {
133 log_error("Failed to parse reply.");
134 return -EIO;
135 }
136
137 dbus_message_iter_get_basic(&sub, val);
138
139 return 0;
140}
141
c170f3a4 142static int compare_unit_time(const void *a, const void *b) {
2265fbf7
SP
143 return compare(((struct unit_times *)b)->time,
144 ((struct unit_times *)a)->time);
145}
146
c170f3a4 147static int compare_unit_start(const void *a, const void *b) {
2265fbf7
SP
148 return compare(((struct unit_times *)a)->ixt,
149 ((struct unit_times *)b)->ixt);
150}
151
c170f3a4 152static int get_os_name(char **_n) {
2265fbf7 153 char *n = NULL;
c170f3a4
LP
154 int r;
155
156 r = parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &n, NULL);
157 if (r < 0)
158 return r;
159
160 if (!n)
161 return -ENOENT;
2265fbf7 162
c170f3a4
LP
163 *_n = n;
164 return 0;
2265fbf7
SP
165}
166
c170f3a4
LP
167static void free_unit_times(struct unit_times *t, unsigned n) {
168 struct unit_times *p;
169
170 for (p = t; p < t + n; p++)
171 free(p->name);
172
173 free(t);
174}
175
176static int acquire_time_data(DBusConnection *bus, struct unit_times **out) {
2265fbf7
SP
177 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
178 DBusMessageIter iter, sub;
179 int r, c = 0, n_units = 0;
180 struct unit_times *unit_times = NULL;
181
c170f3a4 182 r = bus_method_call_with_reply(
2265fbf7
SP
183 bus,
184 "org.freedesktop.systemd1",
185 "/org/freedesktop/systemd1",
186 "org.freedesktop.systemd1.Manager",
187 "ListUnits",
188 &reply,
189 NULL,
190 DBUS_TYPE_INVALID);
c170f3a4 191 if (r < 0)
2265fbf7
SP
192 goto fail;
193
194 if (!dbus_message_iter_init(reply, &iter) ||
195 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
196 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
197 log_error("Failed to parse reply.");
198 r = -EIO;
199 goto fail;
200 }
201
202 for (dbus_message_iter_recurse(&iter, &sub);
203 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
204 dbus_message_iter_next(&sub)) {
205 struct unit_info u;
206 struct unit_times *t;
207
208 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
209 log_error("Failed to parse reply.");
210 r = -EIO;
211 goto fail;
212 }
213
214 if (c >= n_units) {
215 struct unit_times *w;
216
217 n_units = MAX(2*c, 16);
218 w = realloc(unit_times, sizeof(struct unit_times) * n_units);
219
220 if (!w) {
221 r = log_oom();
222 goto fail;
223 }
224
225 unit_times = w;
226 }
227 t = unit_times+c;
228 t->name = NULL;
229
230 r = bus_parse_unit_info(&sub, &u);
231 if (r < 0)
232 goto fail;
233
c170f3a4
LP
234 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
235
2265fbf7
SP
236 if (bus_get_uint64_property(bus, u.unit_path,
237 "org.freedesktop.systemd1.Unit",
238 "InactiveExitTimestampMonotonic",
239 &t->ixt) < 0 ||
240 bus_get_uint64_property(bus, u.unit_path,
241 "org.freedesktop.systemd1.Unit",
242 "ActiveEnterTimestampMonotonic",
243 &t->aet) < 0 ||
244 bus_get_uint64_property(bus, u.unit_path,
245 "org.freedesktop.systemd1.Unit",
246 "ActiveExitTimestampMonotonic",
247 &t->axt) < 0 ||
248 bus_get_uint64_property(bus, u.unit_path,
249 "org.freedesktop.systemd1.Unit",
250 "InactiveEnterTimestampMonotonic",
251 &t->iet) < 0) {
252 r = -EIO;
253 goto fail;
254 }
255
2265fbf7
SP
256 if (t->aet >= t->ixt)
257 t->time = t->aet - t->ixt;
258 else if (t->iet >= t->ixt)
259 t->time = t->iet - t->ixt;
260 else
261 t->time = 0;
262
263 if (t->ixt == 0)
264 continue;
265
266 t->name = strdup(u.id);
267 if (t->name == NULL) {
268 r = log_oom();
269 goto fail;
270 }
271 c++;
272 }
273
274 *out = unit_times;
275 return c;
c170f3a4 276
2265fbf7 277fail:
c170f3a4 278 free_unit_times(unit_times, (unsigned) c);
2265fbf7
SP
279 return r;
280}
281
c170f3a4 282static int acquire_boot_times(DBusConnection *bus, struct boot_times **bt) {
2265fbf7
SP
283 static struct boot_times times;
284 static bool cached = false;
c170f3a4 285
2265fbf7 286 if (cached)
c170f3a4
LP
287 goto finish;
288
289 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
2265fbf7
SP
290
291 if (bus_get_uint64_property(bus,
292 "/org/freedesktop/systemd1",
293 "org.freedesktop.systemd1.Manager",
294 "FirmwareTimestampMonotonic",
295 &times.firmware_time) < 0 ||
296 bus_get_uint64_property(bus,
297 "/org/freedesktop/systemd1",
298 "org.freedesktop.systemd1.Manager",
299 "LoaderTimestampMonotonic",
300 &times.loader_time) < 0 ||
301 bus_get_uint64_property(bus,
302 "/org/freedesktop/systemd1",
303 "org.freedesktop.systemd1.Manager",
304 "KernelTimestamp",
305 &times.kernel_time) < 0 ||
306 bus_get_uint64_property(bus,
307 "/org/freedesktop/systemd1",
308 "org.freedesktop.systemd1.Manager",
309 "InitRDTimestampMonotonic",
310 &times.initrd_time) < 0 ||
311 bus_get_uint64_property(bus,
312 "/org/freedesktop/systemd1",
313 "org.freedesktop.systemd1.Manager",
314 "UserspaceTimestampMonotonic",
315 &times.userspace_time) < 0 ||
316 bus_get_uint64_property(bus,
317 "/org/freedesktop/systemd1",
318 "org.freedesktop.systemd1.Manager",
319 "FinishTimestampMonotonic",
518d10e9
UTL
320 &times.finish_time) < 0 ||
321 bus_get_uint64_property(bus,
322 "/org/freedesktop/systemd1",
323 "org.freedesktop.systemd1.Manager",
324 "GeneratorsStartTimestampMonotonic",
325 &times.generators_start_time) < 0 ||
326 bus_get_uint64_property(bus,
327 "/org/freedesktop/systemd1",
328 "org.freedesktop.systemd1.Manager",
329 "GeneratorsFinishTimestampMonotonic",
d9acfb71
TA
330 &times.generators_finish_time) < 0 ||
331 bus_get_uint64_property(bus,
332 "/org/freedesktop/systemd1",
333 "org.freedesktop.systemd1.Manager",
334 "UnitsLoadStartTimestampMonotonic",
335 &times.unitsload_start_time) < 0 ||
336 bus_get_uint64_property(bus,
337 "/org/freedesktop/systemd1",
338 "org.freedesktop.systemd1.Manager",
339 "UnitsLoadFinishTimestampMonotonic",
340 &times.unitsload_finish_time) < 0)
c170f3a4 341 return -EIO;
2265fbf7 342
c170f3a4 343 if (times.finish_time <= 0) {
2265fbf7 344 log_error("Bootup is not yet finished. Please try again later.");
c170f3a4 345 return -EAGAIN;
2265fbf7
SP
346 }
347
2265fbf7
SP
348 if (times.initrd_time)
349 times.kernel_done_time = times.initrd_time;
350 else
351 times.kernel_done_time = times.userspace_time;
352
353 cached = true;
c170f3a4
LP
354
355finish:
356 *bt = &times;
357 return 0;
2265fbf7
SP
358}
359
c170f3a4
LP
360static int pretty_boot_time(DBusConnection *bus, char **_buf) {
361 char ts[FORMAT_TIMESPAN_MAX];
2265fbf7 362 struct boot_times *t;
2265fbf7 363 static char buf[4096];
c170f3a4
LP
364 size_t size;
365 char *ptr;
366 int r;
367
368 r = acquire_boot_times(bus, &t);
369 if (r < 0)
370 return r;
2265fbf7 371
c170f3a4
LP
372 ptr = buf;
373 size = sizeof(buf);
2265fbf7
SP
374
375 size = strpcpyf(&ptr, size, "Startup finished in ");
376 if (t->firmware_time)
2fa4092c 377 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
2265fbf7 378 if (t->loader_time)
2fa4092c 379 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
2265fbf7 380 if (t->kernel_time)
2fa4092c 381 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
2265fbf7 382 if (t->initrd_time > 0)
2fa4092c 383 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
2265fbf7 384
2fa4092c 385 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
2265fbf7 386 if (t->kernel_time > 0)
2fa4092c 387 size = strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
2265fbf7 388 else
2fa4092c 389 size = strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
2265fbf7 390
c170f3a4
LP
391 ptr = strdup(buf);
392 if (!ptr)
393 return log_oom();
394
395 *_buf = ptr;
396 return 0;
2265fbf7
SP
397}
398
c170f3a4
LP
399static void svg_graph_box(double height, double begin, double end) {
400 long long i;
401
2265fbf7
SP
402 /* outside box, fill */
403 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
2f6eb835 404 SCALE_X * (end - begin), SCALE_Y * height);
2265fbf7 405
c170f3a4 406 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
2265fbf7 407 /* lines for each second */
c170f3a4 408 if (i % 5000000 == 0)
2265fbf7
SP
409 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
410 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
2f6eb835 411 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
c170f3a4 412 else if (i % 1000000 == 0)
2265fbf7
SP
413 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
414 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
2f6eb835 415 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
2265fbf7
SP
416 else
417 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
2f6eb835 418 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
2265fbf7
SP
419 }
420}
421
c170f3a4 422static int analyze_plot(DBusConnection *bus) {
2265fbf7
SP
423 struct unit_times *times;
424 struct boot_times *boot;
425 struct utsname name;
426 int n, m = 1, y=0;
427 double width;
c170f3a4
LP
428 _cleanup_free_ char *pretty_times = NULL, *osname = NULL;
429 struct unit_times *u;
2265fbf7 430
c170f3a4
LP
431 n = acquire_boot_times(bus, &boot);
432 if (n < 0)
433 return n;
2265fbf7 434
c170f3a4
LP
435 n = pretty_boot_time(bus, &pretty_times);
436 if (n < 0)
437 return n;
2265fbf7 438
c170f3a4
LP
439 get_os_name(&osname);
440 assert_se(uname(&name) >= 0);
2265fbf7
SP
441
442 n = acquire_time_data(bus, &times);
c170f3a4 443 if (n <= 0)
2265fbf7
SP
444 return n;
445
446 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
447
2f6eb835 448 width = SCALE_X * (boot->firmware_time + boot->finish_time);
2265fbf7
SP
449 if (width < 800.0)
450 width = 800.0;
451
452 if (boot->firmware_time > boot->loader_time)
453 m++;
454 if (boot->loader_time) {
455 m++;
456 if (width < 1000.0)
457 width = 1000.0;
458 }
459 if (boot->initrd_time)
460 m++;
461 if (boot->kernel_time)
462 m++;
463
c170f3a4 464 for (u = times; u < times + n; u++) {
2265fbf7 465 double len;
c170f3a4 466
2265fbf7
SP
467 if (u->ixt < boot->userspace_time ||
468 u->ixt > boot->finish_time) {
469 free(u->name);
470 u->name = NULL;
471 continue;
472 }
2f6eb835 473 len = ((boot->firmware_time + u->ixt) * SCALE_X)
2265fbf7
SP
474 + (10.0 * strlen(u->name));
475 if (len > width)
476 width = len;
477
478 if (u->iet > u->ixt && u->iet <= boot->finish_time
479 && u->aet == 0 && u->axt == 0)
480 u->aet = u->axt = u->iet;
481 if (u->aet < u->ixt || u->aet > boot->finish_time)
482 u->aet = boot->finish_time;
483 if (u->axt < u->aet || u->aet > boot->finish_time)
484 u->axt = boot->finish_time;
485 if (u->iet < u->axt || u->iet > boot->finish_time)
486 u->iet = boot->finish_time;
487 m++;
488 }
489
490 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
491 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
492 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
493
494 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
495 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
518d10e9 496 80.0 + width, 150.0 + (m * SCALE_Y) +
d9acfb71 497 5 * SCALE_Y /* legend */);
2265fbf7
SP
498
499 /* write some basic info as a comment, including some help */
500 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
501 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
502 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
503 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
504 "<!-- point your browser to this file. -->\n\n"
505 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
506
507 /* style sheet */
508 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
509 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
510 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
511 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
512 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
513 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
514 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
515 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
516 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
517 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
518d10e9 518 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
d9acfb71 519 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
2265fbf7
SP
520 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
521 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
522 "// line.sec1 { }\n"
523 " line.sec5 { stroke-width: 2; }\n"
524 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
2b7d6965
TA
525 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
526 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
527 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
528 " text.sec { font-size: 10px; }\n"
2265fbf7
SP
529 " ]]>\n </style>\n</defs>\n\n");
530
531 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
532 svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
c170f3a4 533 isempty(osname) ? "Linux" : osname,
2265fbf7 534 name.nodename, name.release, name.version, name.machine);
2265fbf7 535
2f6eb835 536 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
2265fbf7
SP
537 svg_graph_box(m, -boot->firmware_time, boot->finish_time);
538
539 if (boot->firmware_time) {
c170f3a4
LP
540 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
541 svg_text(true, -(double) boot->firmware_time, y, "firmware");
2265fbf7
SP
542 y++;
543 }
544 if (boot->loader_time) {
c170f3a4
LP
545 svg_bar("loader", -(double) boot->loader_time, 0, y);
546 svg_text(true, -(double) boot->loader_time, y, "loader");
2265fbf7
SP
547 y++;
548 }
549 if (boot->kernel_time) {
550 svg_bar("kernel", 0, boot->kernel_done_time, y);
c170f3a4 551 svg_text(true, 0, y, "kernel");
2265fbf7
SP
552 y++;
553 }
554 if (boot->initrd_time) {
555 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
c170f3a4 556 svg_text(true, boot->initrd_time, y, "initrd");
2265fbf7
SP
557 y++;
558 }
518d10e9
UTL
559 svg_bar("active", boot->userspace_time, boot->finish_time, y);
560 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
d9acfb71 561 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
518d10e9 562 svg_text("left", boot->userspace_time, y, "systemd");
2265fbf7
SP
563 y++;
564
c170f3a4
LP
565 for (u = times; u < times + n; u++) {
566 char ts[FORMAT_TIMESPAN_MAX];
fd4a5ec6 567 bool b;
c170f3a4 568
2265fbf7
SP
569 if (!u->name)
570 continue;
c170f3a4 571
2265fbf7
SP
572 svg_bar("activating", u->ixt, u->aet, y);
573 svg_bar("active", u->aet, u->axt, y);
574 svg_bar("deactivating", u->axt, u->iet, y);
c170f3a4 575
fd4a5ec6
ZJS
576 b = u->ixt * SCALE_X > width * 2 / 3;
577 if (u->time)
578 svg_text(b, u->ixt, y, "%s (%s)",
2fa4092c 579 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
c170f3a4 580 else
fd4a5ec6 581 svg_text(b, u->ixt, y, "%s", u->name);
2265fbf7
SP
582 y++;
583 }
518d10e9
UTL
584
585 /* Legend */
586 y++;
587 svg_bar("activating", 0, 300000, y);
588 svg_text("right", 400000, y, "Activating");
589 y++;
590 svg_bar("active", 0, 300000, y);
591 svg_text("right", 400000, y, "Active");
592 y++;
593 svg_bar("deactivating", 0, 300000, y);
594 svg_text("right", 400000, y, "Deactivating");
595 y++;
596 svg_bar("generators", 0, 300000, y);
597 svg_text("right", 400000, y, "Generators");
598 y++;
d9acfb71
TA
599 svg_bar("unitsload", 0, 300000, y);
600 svg_text("right", 400000, y, "Loading unit files");
601 y++;
518d10e9 602
2265fbf7
SP
603 svg("</g>\n\n");
604
605 svg("</svg>");
c170f3a4
LP
606
607 free_unit_times(times, (unsigned) n);
608
2265fbf7
SP
609 return 0;
610}
611
bb150966
HH
612static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
613 bool last, struct unit_times *times, struct boot_times *boot) {
614 unsigned int i;
615 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
616
617 for (i = level; i != 0; i--)
618 printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));
619
620 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
621
622 if (times) {
623 if (times->time)
624 printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
625 format_timespan(ts, sizeof(ts), times->ixt - boot->userspace_time, USEC_PER_MSEC),
626 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
627 else if (times->aet > boot->userspace_time)
628 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->aet - boot->userspace_time, USEC_PER_MSEC));
629 else
630 printf("%s", name);
631 } else printf("%s", name);
632 printf("\n");
633
634 return 0;
635}
636
637static int list_dependencies_get_dependencies(DBusConnection *bus, const char *name, char ***deps) {
638 static const char dependencies[] =
639 "After\0";
640
641 _cleanup_free_ char *path;
642 const char *interface = "org.freedesktop.systemd1.Unit";
643
644 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
645 DBusMessageIter iter, sub, sub2, sub3;
646
647 int r = 0;
648 char **ret = NULL;
649
650 assert(bus);
651 assert(name);
652 assert(deps);
653
654 path = unit_dbus_path_from_name(name);
655 if (path == NULL) {
656 r = -EINVAL;
657 goto finish;
658 }
659
660 r = bus_method_call_with_reply(
661 bus,
662 "org.freedesktop.systemd1",
663 path,
664 "org.freedesktop.DBus.Properties",
665 "GetAll",
666 &reply,
667 NULL,
668 DBUS_TYPE_STRING, &interface,
669 DBUS_TYPE_INVALID);
670 if (r < 0)
671 goto finish;
672
673 if (!dbus_message_iter_init(reply, &iter) ||
674 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
675 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
676 log_error("Failed to parse reply.");
677 r = -EIO;
678 goto finish;
679 }
680
681 dbus_message_iter_recurse(&iter, &sub);
682
683 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
684 const char *prop;
685
686 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
687 dbus_message_iter_recurse(&sub, &sub2);
688
689 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) {
690 log_error("Failed to parse reply.");
691 r = -EIO;
692 goto finish;
693 }
694
695 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
696 log_error("Failed to parse reply.");
697 r = -EIO;
698 goto finish;
699 }
700
701 dbus_message_iter_recurse(&sub2, &sub3);
702 dbus_message_iter_next(&sub);
703
704 if (!nulstr_contains(dependencies, prop))
705 continue;
706
707 if (dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_ARRAY) {
708 if (dbus_message_iter_get_element_type(&sub3) == DBUS_TYPE_STRING) {
709 DBusMessageIter sub4;
710 dbus_message_iter_recurse(&sub3, &sub4);
711
712 while (dbus_message_iter_get_arg_type(&sub4) != DBUS_TYPE_INVALID) {
713 const char *s;
714
715 assert(dbus_message_iter_get_arg_type(&sub4) == DBUS_TYPE_STRING);
716 dbus_message_iter_get_basic(&sub4, &s);
717
718 r = strv_extend(&ret, s);
719 if (r < 0) {
720 log_oom();
721 goto finish;
722 }
723
724 dbus_message_iter_next(&sub4);
725 }
726 }
727 }
728 }
729finish:
730 if (r < 0)
731 strv_free(ret);
732 else
733 *deps = ret;
734 return r;
735}
736
737static Hashmap *unit_times_hashmap;
738
739static int list_dependencies_compare(const void *_a, const void *_b) {
740 const char **a = (const char**) _a, **b = (const char**) _b;
741 usec_t usa = 0, usb = 0;
742 struct unit_times *times;
743
744 times = hashmap_get(unit_times_hashmap, *a);
745 if (times)
746 usa = times->aet;
747 times = hashmap_get(unit_times_hashmap, *b);
748 if (times)
749 usb = times->aet;
750
751 return usb - usa;
752}
753
754static int list_dependencies_one(DBusConnection *bus, const char *name, unsigned int level, char ***units,
755 unsigned int branches) {
756 _cleanup_strv_free_ char **deps = NULL;
757 char **c;
758 int r = 0;
759 usec_t service_longest = 0;
760 int to_print = 0;
761 struct unit_times *times;
762 struct boot_times *boot;
763
764 if(strv_extend(units, name))
765 return log_oom();
766
767 r = list_dependencies_get_dependencies(bus, name, &deps);
768 if (r < 0)
769 return r;
770
771 qsort(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
772
773 r = acquire_boot_times(bus, &boot);
774 if (r < 0)
775 return r;
776
777 STRV_FOREACH(c, deps) {
778 times = hashmap_get(unit_times_hashmap, *c);
779 if (times
780 && times->aet
781 && times->aet <= boot->finish_time
782 && (times->aet >= service_longest
783 || service_longest == 0)) {
784 service_longest = times->aet;
785 break;
786 }
787 }
788
789 if (service_longest == 0 )
790 return r;
791
792 STRV_FOREACH(c, deps) {
793 times = hashmap_get(unit_times_hashmap, *c);
794 if (times && times->aet
795 && times->aet <= boot->finish_time
796 && (service_longest - times->aet) <= arg_fuzz) {
797 to_print++;
798 }
799 }
800
801 if(!to_print)
802 return r;
803
804 STRV_FOREACH(c, deps) {
805 times = hashmap_get(unit_times_hashmap, *c);
806 if (!times
807 || !times->aet
808 || times->aet > boot->finish_time
809 || service_longest - times->aet > arg_fuzz)
810 continue;
811
812 to_print--;
813
814 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
815 if (r < 0)
816 return r;
817
818 if (strv_contains(*units, *c)) {
819 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
820 true, NULL, boot);
821 continue;
822 }
823
824 r = list_dependencies_one(bus, *c, level + 1, units,
825 (branches << 1) | (to_print ? 1 : 0));
826 if(r < 0)
827 return r;
828
829
830 if(!to_print)
831 break;
832
833 }
834 return 0;
835}
836
805bf39c 837static int list_dependencies(DBusConnection *bus, const char *name) {
bb150966
HH
838 _cleanup_strv_free_ char **units = NULL;
839 char ts[FORMAT_TIMESPAN_MAX];
840 struct unit_times *times;
841 int r;
842 const char
843 *path, *id,
844 *interface = "org.freedesktop.systemd1.Unit",
845 *property = "Id";
846 DBusMessageIter iter, sub;
847 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
848 struct boot_times *boot;
849
850 assert(bus);
851
805bf39c 852 path = unit_dbus_path_from_name(name);
bb150966
HH
853 if (path == NULL)
854 return -EINVAL;
855
856 r = bus_method_call_with_reply (
857 bus,
858 "org.freedesktop.systemd1",
859 path,
860 "org.freedesktop.DBus.Properties",
861 "Get",
862 &reply,
863 NULL,
864 DBUS_TYPE_STRING, &interface,
865 DBUS_TYPE_STRING, &property,
866 DBUS_TYPE_INVALID);
867 if (r < 0)
868 return r;
869
870 if (!dbus_message_iter_init(reply, &iter) ||
871 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
872 log_error("Failed to parse reply.");
873 return -EIO;
874 }
875
876 dbus_message_iter_recurse(&iter, &sub);
877
878 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
879 log_error("Failed to parse reply.");
880 return -EIO;
881 }
882
883 dbus_message_iter_get_basic(&sub, &id);
884
885 times = hashmap_get(unit_times_hashmap, id);
886
887 r = acquire_boot_times(bus, &boot);
888 if (r < 0)
889 return r;
890
891 if (times) {
892 if (times->time)
893 printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
894 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
895 else if (times->aet > boot->userspace_time)
896 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->aet - boot->userspace_time, USEC_PER_MSEC));
897 else
898 printf("%s\n", id);
899 }
900
805bf39c 901 return list_dependencies_one(bus, name, 0, &units, 0);
bb150966
HH
902}
903
805bf39c 904static int analyze_critical_chain(DBusConnection *bus, char *names[]) {
bb150966
HH
905 struct unit_times *times;
906 int n, r;
907 unsigned int i;
908 Hashmap *h;
909
910 n = acquire_time_data(bus, &times);
911 if (n <= 0)
912 return n;
913
914 h = hashmap_new(string_hash_func, string_compare_func);
915 if (!h)
916 return -ENOMEM;
917
918 for (i = 0; i < (unsigned)n; i++) {
919 r = hashmap_put(h, times[i].name, &times[i]);
920 if (r < 0)
921 return r;
922 }
923 unit_times_hashmap = h;
924
9ea9d4cf
LP
925 pager_open_if_enabled();
926
bb150966
HH
927 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
928 "The time the unit takes to start is printed after the \"+\" character.\n");
929
805bf39c
GP
930 if (!strv_isempty(names)) {
931 char **name;
932 STRV_FOREACH(name, names)
933 list_dependencies(bus, *name);
9ea9d4cf 934 } else
805bf39c 935 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
bb150966
HH
936
937 hashmap_free(h);
938 free_unit_times(times, (unsigned) n);
939 return 0;
940}
941
c170f3a4 942static int analyze_blame(DBusConnection *bus) {
2265fbf7 943 struct unit_times *times;
c170f3a4
LP
944 unsigned i;
945 int n;
946
947 n = acquire_time_data(bus, &times);
948 if (n <= 0)
2265fbf7
SP
949 return n;
950
951 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
952
9ea9d4cf
LP
953 pager_open_if_enabled();
954
c170f3a4
LP
955 for (i = 0; i < (unsigned) n; i++) {
956 char ts[FORMAT_TIMESPAN_MAX];
957
958 if (times[i].time > 0)
2fa4092c 959 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
2265fbf7 960 }
c170f3a4
LP
961
962 free_unit_times(times, (unsigned) n);
2265fbf7
SP
963 return 0;
964}
965
c170f3a4
LP
966static int analyze_time(DBusConnection *bus) {
967 _cleanup_free_ char *buf = NULL;
968 int r;
969
970 r = pretty_boot_time(bus, &buf);
971 if (r < 0)
972 return r;
973
974 puts(buf);
2265fbf7
SP
975 return 0;
976}
977
e55933db 978static int graph_one_property(const char *name, const char *prop, DBusMessageIter *iter, char* patterns[]) {
1700761b
SP
979
980 static const char * const colors[] = {
981 "Requires", "[color=\"black\"]",
982 "RequiresOverridable", "[color=\"black\"]",
983 "Requisite", "[color=\"darkblue\"]",
984 "RequisiteOverridable", "[color=\"darkblue\"]",
985 "Wants", "[color=\"grey66\"]",
986 "Conflicts", "[color=\"red\"]",
987 "ConflictedBy", "[color=\"red\"]",
988 "After", "[color=\"green\"]"
989 };
990
991 const char *c = NULL;
992 unsigned i;
993
994 assert(name);
995 assert(prop);
996 assert(iter);
997
998 for (i = 0; i < ELEMENTSOF(colors); i += 2)
999 if (streq(colors[i], prop)) {
1000 c = colors[i+1];
1001 break;
1002 }
1003
1004 if (!c)
1005 return 0;
1006
1007 if (arg_dot != DEP_ALL)
1008 if ((arg_dot == DEP_ORDER) != streq(prop, "After"))
1009 return 0;
1010
1011 if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY &&
1012 dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
1013 DBusMessageIter sub;
1014
1015 dbus_message_iter_recurse(iter, &sub);
1016
1017 for (dbus_message_iter_recurse(iter, &sub);
1018 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
1019 dbus_message_iter_next(&sub)) {
1020 const char *s;
e55933db 1021 char **p;
816f25e8 1022 bool match_found;
1700761b
SP
1023
1024 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
1025 dbus_message_iter_get_basic(&sub, &s);
e55933db 1026
816f25e8 1027 if (!strv_isempty(arg_dot_from_patterns)) {
e55933db 1028 match_found = false;
816f25e8
LP
1029
1030 STRV_FOREACH(p, arg_dot_from_patterns)
1031 if (fnmatch(*p, name, 0) == 0) {
1032 match_found = true;
1033 break;
1034 }
1035
1036 if (!match_found)
1037 continue;
e55933db 1038 }
e55933db 1039
816f25e8 1040 if (!strv_isempty(arg_dot_to_patterns)) {
e55933db 1041 match_found = false;
816f25e8
LP
1042
1043 STRV_FOREACH(p, arg_dot_to_patterns)
1044 if (fnmatch(*p, s, 0) == 0) {
1045 match_found = true;
1046 break;
1047 }
1048
1049 if (!match_found)
1050 continue;
e55933db 1051 }
e55933db 1052
816f25e8 1053 if (!strv_isempty(patterns)) {
e55933db 1054 match_found = false;
816f25e8
LP
1055
1056 STRV_FOREACH(p, patterns)
1057 if (fnmatch(*p, name, 0) == 0 || fnmatch(*p, s, 0) == 0) {
1058 match_found = true;
1059 break;
1060 }
1061 if (!match_found)
1062 continue;
e55933db 1063 }
e55933db 1064
1700761b
SP
1065 printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
1066 }
1067 }
1068
1069 return 0;
1070}
1071
e55933db 1072static int graph_one(DBusConnection *bus, const struct unit_info *u, char *patterns[]) {
1700761b
SP
1073 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1074 const char *interface = "org.freedesktop.systemd1.Unit";
1075 int r;
1076 DBusMessageIter iter, sub, sub2, sub3;
1077
1078 assert(bus);
1079 assert(u);
1080
1081 r = bus_method_call_with_reply(
1082 bus,
1083 "org.freedesktop.systemd1",
1084 u->unit_path,
1085 "org.freedesktop.DBus.Properties",
1086 "GetAll",
1087 &reply,
1088 NULL,
1089 DBUS_TYPE_STRING, &interface,
1090 DBUS_TYPE_INVALID);
1091 if (r < 0)
1092 return r;
1093
1094 if (!dbus_message_iter_init(reply, &iter) ||
1095 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
1096 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
1097 log_error("Failed to parse reply.");
1098 return -EIO;
1099 }
1100
1101 for (dbus_message_iter_recurse(&iter, &sub);
1102 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
1103 dbus_message_iter_next(&sub)) {
1104 const char *prop;
1105
1106 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
1107 dbus_message_iter_recurse(&sub, &sub2);
1108
1109 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0 ||
1110 dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
1111 log_error("Failed to parse reply.");
1112 return -EIO;
1113 }
1114
1115 dbus_message_iter_recurse(&sub2, &sub3);
e55933db 1116 r = graph_one_property(u->id, prop, &sub3, patterns);
1700761b
SP
1117 if (r < 0)
1118 return r;
1119 }
1120
1121 return 0;
1122}
1123
e55933db 1124static int dot(DBusConnection *bus, char* patterns[]) {
1700761b
SP
1125 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1126 DBusMessageIter iter, sub;
1127 int r;
1128
1129 r = bus_method_call_with_reply(
1130 bus,
1131 "org.freedesktop.systemd1",
1132 "/org/freedesktop/systemd1",
1133 "org.freedesktop.systemd1.Manager",
1134 "ListUnits",
1135 &reply,
1136 NULL,
1137 DBUS_TYPE_INVALID);
1138 if (r < 0)
1139 return r;
1140
1141 if (!dbus_message_iter_init(reply, &iter) ||
1142 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
1143 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
1144 log_error("Failed to parse reply.");
1145 return -EIO;
1146 }
1147
1148 printf("digraph systemd {\n");
1149
1150 for (dbus_message_iter_recurse(&iter, &sub);
1151 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
1152 dbus_message_iter_next(&sub)) {
1153 struct unit_info u;
1154
1155 r = bus_parse_unit_info(&sub, &u);
1156 if (r < 0)
1157 return -EIO;
1158
e55933db 1159 r = graph_one(bus, &u, patterns);
1700761b
SP
1160 if (r < 0)
1161 return r;
1162 }
1163
1164 printf("}\n");
1165
1166 log_info(" Color legend: black = Requires\n"
1167 " dark blue = Requisite\n"
1168 " dark grey = Wants\n"
1169 " red = Conflicts\n"
1170 " green = After\n");
1171
1172 if (on_tty())
1173 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1174 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1175
1176 return 0;
1177}
1178
9ea9d4cf
LP
1179static int dump(DBusConnection *bus, char **args) {
1180 _cleanup_free_ DBusMessage *reply = NULL;
1181 DBusError error;
1182 int r;
1183 const char *text;
1184
1185 dbus_error_init(&error);
1186
1187 pager_open_if_enabled();
1188
1189 r = bus_method_call_with_reply(
1190 bus,
1191 "org.freedesktop.systemd1",
1192 "/org/freedesktop/systemd1",
1193 "org.freedesktop.systemd1.Manager",
1194 "Dump",
1195 &reply,
1196 NULL,
1197 DBUS_TYPE_INVALID);
1198 if (r < 0)
1199 return r;
1200
1201 if (!dbus_message_get_args(reply, &error,
1202 DBUS_TYPE_STRING, &text,
1203 DBUS_TYPE_INVALID)) {
1204 log_error("Failed to parse reply: %s", bus_error_message(&error));
1205 dbus_error_free(&error);
1206 return -EIO;
1207 }
1208
1209 fputs(text, stdout);
1210 return 0;
1211}
1212
1213static void analyze_help(void) {
1214
1215 pager_open_if_enabled();
1216
2265fbf7
SP
1217 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1218 "Process systemd profiling information\n\n"
1219 " -h --help Show this help\n"
1220 " --version Show package version\n"
1221 " --system Connect to system manager\n"
1700761b
SP
1222 " --user Connect to user service manager\n"
1223 " --order When generating a dependency graph, show only order\n"
f5b7a3fb
LP
1224 " --require When generating a dependency graph, show only requirement\n"
1225 " --from-pattern=GLOB, --to-pattern=GLOB\n"
1226 " When generating a dependency graph, filter only origins\n"
bb150966
HH
1227 " or destinations, respectively\n"
1228 " --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
1229 " services, which finished TIMESPAN earlier, than the\n"
1230 " latest in the branch. The unit of TIMESPAN is seconds\n"
9ea9d4cf
LP
1231 " unless specified with a different unit, i.e. 50ms\n"
1232 " --no-pager Do not pipe output into a pager\n\n"
2265fbf7 1233 "Commands:\n"
1700761b
SP
1234 " time Print time spent in the kernel before reaching userspace\n"
1235 " blame Print list of running units ordered by time to init\n"
bb150966 1236 " critical-chain Print a tree of the time critical chain of units\n"
1700761b 1237 " plot Output SVG graphic showing service initialization\n"
9ea9d4cf
LP
1238 " dot Output dependency graph in dot(1) format\n"
1239 " dump Output state serialization of service manager\n",
2265fbf7 1240 program_invocation_short_name);
96de7c04
ZJS
1241
1242 /* When updating this list, including descriptions, apply
1243 * changes to shell-completion/bash/systemd and
1244 * shell-completion/systemd-zsh-completion.zsh too. */
2265fbf7
SP
1245}
1246
9ea9d4cf 1247static int parse_argv(int argc, char *argv[]) {
bb150966
HH
1248 int r;
1249
2265fbf7
SP
1250 enum {
1251 ARG_VERSION = 0x100,
1700761b
SP
1252 ARG_ORDER,
1253 ARG_REQUIRE,
2265fbf7 1254 ARG_USER,
e55933db
ŁS
1255 ARG_SYSTEM,
1256 ARG_DOT_FROM_PATTERN,
bb150966 1257 ARG_DOT_TO_PATTERN,
9ea9d4cf
LP
1258 ARG_FUZZ,
1259 ARG_NO_PAGER
2265fbf7
SP
1260 };
1261
1262 static const struct option options[] = {
9ea9d4cf
LP
1263 { "help", no_argument, NULL, 'h' },
1264 { "version", no_argument, NULL, ARG_VERSION },
1265 { "order", no_argument, NULL, ARG_ORDER },
1266 { "require", no_argument, NULL, ARG_REQUIRE },
1267 { "user", no_argument, NULL, ARG_USER },
1268 { "system", no_argument, NULL, ARG_SYSTEM },
1269 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1270 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1271 { "fuzz", required_argument, NULL, ARG_FUZZ },
1272 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1273 { NULL, 0, NULL, 0 }
2265fbf7
SP
1274 };
1275
1276 assert(argc >= 0);
1277 assert(argv);
1278
c170f3a4 1279 for (;;) {
2265fbf7 1280 switch (getopt_long(argc, argv, "h", options, NULL)) {
c170f3a4
LP
1281
1282 case 'h':
1283 analyze_help();
1284 return 0;
1285
1286 case ARG_VERSION:
1287 puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES);
1288 return 0;
1289
1290 case ARG_USER:
1291 arg_scope = UNIT_FILE_USER;
1292 break;
1293
1294 case ARG_SYSTEM:
1295 arg_scope = UNIT_FILE_SYSTEM;
1296 break;
1297
1298 case ARG_ORDER:
1299 arg_dot = DEP_ORDER;
1300 break;
1301
1302 case ARG_REQUIRE:
1303 arg_dot = DEP_REQUIRE;
1304 break;
1305
e55933db 1306 case ARG_DOT_FROM_PATTERN:
903a0b07
LP
1307 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1308 return log_oom();
1309
e55933db
ŁS
1310 break;
1311
1312 case ARG_DOT_TO_PATTERN:
903a0b07
LP
1313 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1314 return log_oom();
1315
e55933db
ŁS
1316 break;
1317
bb150966
HH
1318 case ARG_FUZZ:
1319 r = parse_sec(optarg, &arg_fuzz);
1320 if (r < 0)
1321 return r;
1322 break;
1323
9ea9d4cf
LP
1324 case ARG_NO_PAGER:
1325 arg_no_pager = true;
1326 break;
1327
c170f3a4
LP
1328 case -1:
1329 return 1;
1330
1331 case '?':
1332 return -EINVAL;
1333
1334 default:
1335 assert_not_reached("Unhandled option");
2265fbf7
SP
1336 }
1337 }
1338}
1339
1340int main(int argc, char *argv[]) {
1341 int r;
1342 DBusConnection *bus = NULL;
1343
1344 setlocale(LC_ALL, "");
c170f3a4 1345 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
2265fbf7
SP
1346 log_parse_environment();
1347 log_open();
1348
1349 r = parse_argv(argc, argv);
9ea9d4cf
LP
1350 if (r <= 0)
1351 goto finish;
2265fbf7
SP
1352
1353 bus = dbus_bus_get(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, NULL);
9ea9d4cf
LP
1354 if (!bus) {
1355 r = -EIO;
1356 goto finish;
1357 }
2265fbf7
SP
1358
1359 if (!argv[optind] || streq(argv[optind], "time"))
1360 r = analyze_time(bus);
1361 else if (streq(argv[optind], "blame"))
1362 r = analyze_blame(bus);
bb150966 1363 else if (streq(argv[optind], "critical-chain"))
805bf39c 1364 r = analyze_critical_chain(bus, argv+optind+1);
2265fbf7
SP
1365 else if (streq(argv[optind], "plot"))
1366 r = analyze_plot(bus);
1700761b 1367 else if (streq(argv[optind], "dot"))
e55933db 1368 r = dot(bus, argv+optind+1);
9ea9d4cf
LP
1369 else if (streq(argv[optind], "dump"))
1370 r = dump(bus, argv+optind+1);
2265fbf7
SP
1371 else
1372 log_error("Unknown operation '%s'.", argv[optind]);
1373
9ea9d4cf
LP
1374 dbus_connection_unref(bus);
1375
1376finish:
1377 pager_close();
1378
e55933db
ŁS
1379 strv_free(arg_dot_from_patterns);
1380 strv_free(arg_dot_to_patterns);
c170f3a4
LP
1381
1382 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2265fbf7 1383}