1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010-2013 Lennart Poettering
7 Copyright 2013 Simon Peeters
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.
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.
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/>.
27 #include <sys/utsname.h>
32 #include "dbus-common.h"
39 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
42 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
44 #define svg(...) printf(__VA_ARGS__)
46 #define svg_bar(class, x1, x2, y) \
47 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
49 SCALE_X * (x1), SCALE_Y * (y), \
50 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
52 #define svg_text(b, x, y, format, ...) \
54 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
55 svg(format, ## __VA_ARGS__); \
59 static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
65 static char** arg_dot_from_patterns = NULL;
66 static char** arg_dot_to_patterns = NULL;
72 usec_t kernel_done_time;
74 usec_t userspace_time;
86 static int bus_get_uint64_property(DBusConnection *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
87 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
88 DBusMessageIter iter, sub;
91 r = bus_method_call_with_reply(
93 "org.freedesktop.systemd1",
95 "org.freedesktop.DBus.Properties",
99 DBUS_TYPE_STRING, &interface,
100 DBUS_TYPE_STRING, &property,
105 if (!dbus_message_iter_init(reply, &iter) ||
106 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
107 log_error("Failed to parse reply.");
111 dbus_message_iter_recurse(&iter, &sub);
113 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64) {
114 log_error("Failed to parse reply.");
118 dbus_message_iter_get_basic(&sub, val);
123 static int compare_unit_time(const void *a, const void *b) {
124 return compare(((struct unit_times *)b)->time,
125 ((struct unit_times *)a)->time);
128 static int compare_unit_start(const void *a, const void *b) {
129 return compare(((struct unit_times *)a)->ixt,
130 ((struct unit_times *)b)->ixt);
133 static int get_os_name(char **_n) {
137 r = parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &n, NULL);
148 static void free_unit_times(struct unit_times *t, unsigned n) {
149 struct unit_times *p;
151 for (p = t; p < t + n; p++)
157 static int acquire_time_data(DBusConnection *bus, struct unit_times **out) {
158 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
159 DBusMessageIter iter, sub;
160 int r, c = 0, n_units = 0;
161 struct unit_times *unit_times = NULL;
163 r = bus_method_call_with_reply(
165 "org.freedesktop.systemd1",
166 "/org/freedesktop/systemd1",
167 "org.freedesktop.systemd1.Manager",
175 if (!dbus_message_iter_init(reply, &iter) ||
176 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
177 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
178 log_error("Failed to parse reply.");
183 for (dbus_message_iter_recurse(&iter, &sub);
184 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
185 dbus_message_iter_next(&sub)) {
187 struct unit_times *t;
189 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
190 log_error("Failed to parse reply.");
196 struct unit_times *w;
198 n_units = MAX(2*c, 16);
199 w = realloc(unit_times, sizeof(struct unit_times) * n_units);
211 r = bus_parse_unit_info(&sub, &u);
215 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
217 if (bus_get_uint64_property(bus, u.unit_path,
218 "org.freedesktop.systemd1.Unit",
219 "InactiveExitTimestampMonotonic",
221 bus_get_uint64_property(bus, u.unit_path,
222 "org.freedesktop.systemd1.Unit",
223 "ActiveEnterTimestampMonotonic",
225 bus_get_uint64_property(bus, u.unit_path,
226 "org.freedesktop.systemd1.Unit",
227 "ActiveExitTimestampMonotonic",
229 bus_get_uint64_property(bus, u.unit_path,
230 "org.freedesktop.systemd1.Unit",
231 "InactiveEnterTimestampMonotonic",
237 if (t->aet >= t->ixt)
238 t->time = t->aet - t->ixt;
239 else if (t->iet >= t->ixt)
240 t->time = t->iet - t->ixt;
247 t->name = strdup(u.id);
248 if (t->name == NULL) {
259 free_unit_times(unit_times, (unsigned) c);
263 static int acquire_boot_times(DBusConnection *bus, struct boot_times **bt) {
264 static struct boot_times times;
265 static bool cached = false;
270 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
272 if (bus_get_uint64_property(bus,
273 "/org/freedesktop/systemd1",
274 "org.freedesktop.systemd1.Manager",
275 "FirmwareTimestampMonotonic",
276 ×.firmware_time) < 0 ||
277 bus_get_uint64_property(bus,
278 "/org/freedesktop/systemd1",
279 "org.freedesktop.systemd1.Manager",
280 "LoaderTimestampMonotonic",
281 ×.loader_time) < 0 ||
282 bus_get_uint64_property(bus,
283 "/org/freedesktop/systemd1",
284 "org.freedesktop.systemd1.Manager",
286 ×.kernel_time) < 0 ||
287 bus_get_uint64_property(bus,
288 "/org/freedesktop/systemd1",
289 "org.freedesktop.systemd1.Manager",
290 "InitRDTimestampMonotonic",
291 ×.initrd_time) < 0 ||
292 bus_get_uint64_property(bus,
293 "/org/freedesktop/systemd1",
294 "org.freedesktop.systemd1.Manager",
295 "UserspaceTimestampMonotonic",
296 ×.userspace_time) < 0 ||
297 bus_get_uint64_property(bus,
298 "/org/freedesktop/systemd1",
299 "org.freedesktop.systemd1.Manager",
300 "FinishTimestampMonotonic",
301 ×.finish_time) < 0)
304 if (times.finish_time <= 0) {
305 log_error("Bootup is not yet finished. Please try again later.");
309 if (times.initrd_time)
310 times.kernel_done_time = times.initrd_time;
312 times.kernel_done_time = times.userspace_time;
321 static int pretty_boot_time(DBusConnection *bus, char **_buf) {
322 char ts[FORMAT_TIMESPAN_MAX];
323 struct boot_times *t;
324 static char buf[4096];
329 r = acquire_boot_times(bus, &t);
336 size = strpcpyf(&ptr, size, "Startup finished in ");
337 if (t->firmware_time)
338 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
340 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
342 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
343 if (t->initrd_time > 0)
344 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
346 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
347 if (t->kernel_time > 0)
348 size = strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
350 size = strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
360 static void svg_graph_box(double height, double begin, double end) {
363 /* outside box, fill */
364 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
365 SCALE_X * (end - begin), SCALE_Y * height);
367 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
368 /* lines for each second */
369 if (i % 5000000 == 0)
370 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
371 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
372 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
373 else if (i % 1000000 == 0)
374 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
375 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
376 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
378 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
379 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
383 static int analyze_plot(DBusConnection *bus) {
384 struct unit_times *times;
385 struct boot_times *boot;
389 _cleanup_free_ char *pretty_times = NULL, *osname = NULL;
390 struct unit_times *u;
392 n = acquire_boot_times(bus, &boot);
396 n = pretty_boot_time(bus, &pretty_times);
400 get_os_name(&osname);
401 assert_se(uname(&name) >= 0);
403 n = acquire_time_data(bus, ×);
407 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
409 width = SCALE_X * (boot->firmware_time + boot->finish_time);
413 if (boot->firmware_time > boot->loader_time)
415 if (boot->loader_time) {
420 if (boot->initrd_time)
422 if (boot->kernel_time)
425 for (u = times; u < times + n; u++) {
428 if (u->ixt < boot->userspace_time ||
429 u->ixt > boot->finish_time) {
434 len = ((boot->firmware_time + u->ixt) * SCALE_X)
435 + (10.0 * strlen(u->name));
439 if (u->iet > u->ixt && u->iet <= boot->finish_time
440 && u->aet == 0 && u->axt == 0)
441 u->aet = u->axt = u->iet;
442 if (u->aet < u->ixt || u->aet > boot->finish_time)
443 u->aet = boot->finish_time;
444 if (u->axt < u->aet || u->aet > boot->finish_time)
445 u->axt = boot->finish_time;
446 if (u->iet < u->axt || u->iet > boot->finish_time)
447 u->iet = boot->finish_time;
451 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
452 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
453 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
455 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
456 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
457 80.0 + width, 150.0 + (m * SCALE_Y));
459 /* write some basic info as a comment, including some help */
460 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
461 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
462 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
463 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
464 "<!-- point your browser to this file. -->\n\n"
465 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
468 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
469 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
470 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
471 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
472 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
473 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
474 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
475 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
476 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
477 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
478 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
479 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
481 " line.sec5 { stroke-width: 2; }\n"
482 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
483 " text { font-family: Verdana, Helvetica; font-size: 10; }\n"
484 " text.left { font-family: Verdana, Helvetica; font-size: 10; text-anchor: start; }\n"
485 " text.right { font-family: Verdana, Helvetica; font-size: 10; text-anchor: end; }\n"
486 " text.sec { font-size: 8; }\n"
487 " ]]>\n </style>\n</defs>\n\n");
489 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
490 svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
491 isempty(osname) ? "Linux" : osname,
492 name.nodename, name.release, name.version, name.machine);
493 svg("<text x=\"20\" y=\"%.0f\">Legend: Red = Activating; Pink = Active; Dark Pink = Deactivating</text>",
494 120.0 + (m *SCALE_Y));
496 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
497 svg_graph_box(m, -boot->firmware_time, boot->finish_time);
499 if (boot->firmware_time) {
500 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
501 svg_text(true, -(double) boot->firmware_time, y, "firmware");
504 if (boot->loader_time) {
505 svg_bar("loader", -(double) boot->loader_time, 0, y);
506 svg_text(true, -(double) boot->loader_time, y, "loader");
509 if (boot->kernel_time) {
510 svg_bar("kernel", 0, boot->kernel_done_time, y);
511 svg_text(true, 0, y, "kernel");
514 if (boot->initrd_time) {
515 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
516 svg_text(true, boot->initrd_time, y, "initrd");
519 svg_bar("userspace", boot->userspace_time, boot->finish_time, y);
520 svg_text("left", boot->userspace_time, y, "userspace");
523 for (u = times; u < times + n; u++) {
524 char ts[FORMAT_TIMESPAN_MAX];
530 svg_bar("activating", u->ixt, u->aet, y);
531 svg_bar("active", u->aet, u->axt, y);
532 svg_bar("deactivating", u->axt, u->iet, y);
534 b = u->ixt * SCALE_X > width * 2 / 3;
536 svg_text(b, u->ixt, y, "%s (%s)",
537 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
539 svg_text(b, u->ixt, y, "%s", u->name);
546 free_unit_times(times, (unsigned) n);
551 static int analyze_blame(DBusConnection *bus) {
552 struct unit_times *times;
556 n = acquire_time_data(bus, ×);
560 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
562 for (i = 0; i < (unsigned) n; i++) {
563 char ts[FORMAT_TIMESPAN_MAX];
565 if (times[i].time > 0)
566 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
569 free_unit_times(times, (unsigned) n);
573 static int analyze_time(DBusConnection *bus) {
574 _cleanup_free_ char *buf = NULL;
577 r = pretty_boot_time(bus, &buf);
585 static int graph_one_property(const char *name, const char *prop, DBusMessageIter *iter, char* patterns[]) {
587 static const char * const colors[] = {
588 "Requires", "[color=\"black\"]",
589 "RequiresOverridable", "[color=\"black\"]",
590 "Requisite", "[color=\"darkblue\"]",
591 "RequisiteOverridable", "[color=\"darkblue\"]",
592 "Wants", "[color=\"grey66\"]",
593 "Conflicts", "[color=\"red\"]",
594 "ConflictedBy", "[color=\"red\"]",
595 "After", "[color=\"green\"]"
598 const char *c = NULL;
605 for (i = 0; i < ELEMENTSOF(colors); i += 2)
606 if (streq(colors[i], prop)) {
614 if (arg_dot != DEP_ALL)
615 if ((arg_dot == DEP_ORDER) != streq(prop, "After"))
618 if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY &&
619 dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
622 dbus_message_iter_recurse(iter, &sub);
624 for (dbus_message_iter_recurse(iter, &sub);
625 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
626 dbus_message_iter_next(&sub)) {
631 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
632 dbus_message_iter_get_basic(&sub, &s);
634 if (!strv_isempty(arg_dot_from_patterns)) {
637 STRV_FOREACH(p, arg_dot_from_patterns)
638 if (fnmatch(*p, name, 0) == 0) {
647 if (!strv_isempty(arg_dot_to_patterns)) {
650 STRV_FOREACH(p, arg_dot_to_patterns)
651 if (fnmatch(*p, s, 0) == 0) {
660 if (!strv_isempty(patterns)) {
663 STRV_FOREACH(p, patterns)
664 if (fnmatch(*p, name, 0) == 0 || fnmatch(*p, s, 0) == 0) {
672 printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
679 static int graph_one(DBusConnection *bus, const struct unit_info *u, char *patterns[]) {
680 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
681 const char *interface = "org.freedesktop.systemd1.Unit";
683 DBusMessageIter iter, sub, sub2, sub3;
688 r = bus_method_call_with_reply(
690 "org.freedesktop.systemd1",
692 "org.freedesktop.DBus.Properties",
696 DBUS_TYPE_STRING, &interface,
701 if (!dbus_message_iter_init(reply, &iter) ||
702 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
703 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
704 log_error("Failed to parse reply.");
708 for (dbus_message_iter_recurse(&iter, &sub);
709 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
710 dbus_message_iter_next(&sub)) {
713 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
714 dbus_message_iter_recurse(&sub, &sub2);
716 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0 ||
717 dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
718 log_error("Failed to parse reply.");
722 dbus_message_iter_recurse(&sub2, &sub3);
723 r = graph_one_property(u->id, prop, &sub3, patterns);
731 static int dot(DBusConnection *bus, char* patterns[]) {
732 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
733 DBusMessageIter iter, sub;
736 r = bus_method_call_with_reply(
738 "org.freedesktop.systemd1",
739 "/org/freedesktop/systemd1",
740 "org.freedesktop.systemd1.Manager",
748 if (!dbus_message_iter_init(reply, &iter) ||
749 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
750 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
751 log_error("Failed to parse reply.");
755 printf("digraph systemd {\n");
757 for (dbus_message_iter_recurse(&iter, &sub);
758 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
759 dbus_message_iter_next(&sub)) {
762 r = bus_parse_unit_info(&sub, &u);
766 r = graph_one(bus, &u, patterns);
773 log_info(" Color legend: black = Requires\n"
774 " dark blue = Requisite\n"
775 " dark grey = Wants\n"
780 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
781 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
786 static void analyze_help(void)
788 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
789 "Process systemd profiling information\n\n"
790 " -h --help Show this help\n"
791 " --version Show package version\n"
792 " --system Connect to system manager\n"
793 " --user Connect to user service manager\n"
794 " --order When generating a dependency graph, show only order\n"
795 " --require When generating a dependency graph, show only requirement\n"
796 " --from-pattern=GLOB, --to-pattern=GLOB\n"
797 " When generating a dependency graph, filter only origins\n"
798 " or destinations, respectively\n\n"
800 " time Print time spent in the kernel before reaching userspace\n"
801 " blame Print list of running units ordered by time to init\n"
802 " plot Output SVG graphic showing service initialization\n"
803 " dot Dump dependency graph (in dot(1) format)\n\n",
804 program_invocation_short_name);
807 static int parse_argv(int argc, char *argv[])
815 ARG_DOT_FROM_PATTERN,
819 static const struct option options[] = {
820 { "help", no_argument, NULL, 'h' },
821 { "version", no_argument, NULL, ARG_VERSION },
822 { "order", no_argument, NULL, ARG_ORDER },
823 { "require", no_argument, NULL, ARG_REQUIRE },
824 { "user", no_argument, NULL, ARG_USER },
825 { "system", no_argument, NULL, ARG_SYSTEM },
826 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN},
827 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
835 switch (getopt_long(argc, argv, "h", options, NULL)) {
842 puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES);
846 arg_scope = UNIT_FILE_USER;
850 arg_scope = UNIT_FILE_SYSTEM;
858 arg_dot = DEP_REQUIRE;
861 case ARG_DOT_FROM_PATTERN:
862 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
867 case ARG_DOT_TO_PATTERN:
868 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
880 assert_not_reached("Unhandled option");
885 int main(int argc, char *argv[]) {
887 DBusConnection *bus = NULL;
889 setlocale(LC_ALL, "");
890 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
891 log_parse_environment();
894 r = parse_argv(argc, argv);
900 bus = dbus_bus_get(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, NULL);
904 if (!argv[optind] || streq(argv[optind], "time"))
905 r = analyze_time(bus);
906 else if (streq(argv[optind], "blame"))
907 r = analyze_blame(bus);
908 else if (streq(argv[optind], "plot"))
909 r = analyze_plot(bus);
910 else if (streq(argv[optind], "dot"))
911 r = dot(bus, argv+optind+1);
913 log_error("Unknown operation '%s'.", argv[optind]);
915 strv_free(arg_dot_from_patterns);
916 strv_free(arg_dot_to_patterns);
917 dbus_connection_unref(bus);
919 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;