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