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