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