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