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