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