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