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