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