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