]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/analyze/systemd-analyze.c
core: remove unnecessary goto in setup_namespace
[thirdparty/systemd.git] / src / analyze / systemd-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>
28
29#include "install.h"
30#include "log.h"
31#include "dbus-common.h"
32#include "build.h"
33#include "util.h"
34#include "strxcpyx.h"
a5c32cff 35#include "fileio.h"
2265fbf7 36
2f6eb835
LP
37#define SCALE_X (0.1 / 1000.0) /* pixels per us */
38#define SCALE_Y 20.0
39
2265fbf7 40#define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
c170f3a4 41
2265fbf7 42#define svg(...) printf(__VA_ARGS__)
c170f3a4
LP
43
44#define svg_bar(class, x1, x2, y) \
2265fbf7 45 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
c170f3a4 46 (class), \
2f6eb835
LP
47 SCALE_X * (x1), SCALE_Y * (y), \
48 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
c170f3a4
LP
49
50#define svg_text(b, x, y, format, ...) \
51 do { \
2f6eb835 52 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
53 svg(format, ## __VA_ARGS__); \
54 svg("</text>\n"); \
2265fbf7
SP
55 } while(false)
56
57static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
1700761b
SP
58static enum dot {
59 DEP_ALL,
60 DEP_ORDER,
61 DEP_REQUIRE
62} arg_dot = DEP_ALL;
2265fbf7 63
2265fbf7 64struct boot_times {
c170f3a4
LP
65 usec_t firmware_time;
66 usec_t loader_time;
67 usec_t kernel_time;
68 usec_t kernel_done_time;
69 usec_t initrd_time;
70 usec_t userspace_time;
71 usec_t finish_time;
2265fbf7
SP
72};
73struct unit_times {
74 char *name;
c170f3a4
LP
75 usec_t ixt;
76 usec_t iet;
77 usec_t axt;
78 usec_t aet;
79 usec_t time;
2265fbf7
SP
80};
81
c170f3a4 82static int bus_get_uint64_property(DBusConnection *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
2265fbf7 83 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
2265fbf7 84 DBusMessageIter iter, sub;
c170f3a4 85 int r;
2265fbf7 86
c170f3a4 87 r = bus_method_call_with_reply(
2265fbf7
SP
88 bus,
89 "org.freedesktop.systemd1",
90 path,
91 "org.freedesktop.DBus.Properties",
92 "Get",
93 &reply,
94 NULL,
95 DBUS_TYPE_STRING, &interface,
96 DBUS_TYPE_STRING, &property,
97 DBUS_TYPE_INVALID);
98 if (r < 0)
99 return r;
100
101 if (!dbus_message_iter_init(reply, &iter) ||
102 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
103 log_error("Failed to parse reply.");
104 return -EIO;
105 }
106
107 dbus_message_iter_recurse(&iter, &sub);
108
109 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64) {
110 log_error("Failed to parse reply.");
111 return -EIO;
112 }
113
114 dbus_message_iter_get_basic(&sub, val);
115
116 return 0;
117}
118
c170f3a4 119static int compare_unit_time(const void *a, const void *b) {
2265fbf7
SP
120 return compare(((struct unit_times *)b)->time,
121 ((struct unit_times *)a)->time);
122}
123
c170f3a4 124static int compare_unit_start(const void *a, const void *b) {
2265fbf7
SP
125 return compare(((struct unit_times *)a)->ixt,
126 ((struct unit_times *)b)->ixt);
127}
128
c170f3a4 129static int get_os_name(char **_n) {
2265fbf7 130 char *n = NULL;
c170f3a4
LP
131 int r;
132
133 r = parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &n, NULL);
134 if (r < 0)
135 return r;
136
137 if (!n)
138 return -ENOENT;
2265fbf7 139
c170f3a4
LP
140 *_n = n;
141 return 0;
2265fbf7
SP
142}
143
c170f3a4
LP
144static void free_unit_times(struct unit_times *t, unsigned n) {
145 struct unit_times *p;
146
147 for (p = t; p < t + n; p++)
148 free(p->name);
149
150 free(t);
151}
152
153static int acquire_time_data(DBusConnection *bus, struct unit_times **out) {
2265fbf7
SP
154 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
155 DBusMessageIter iter, sub;
156 int r, c = 0, n_units = 0;
157 struct unit_times *unit_times = NULL;
158
c170f3a4 159 r = bus_method_call_with_reply(
2265fbf7
SP
160 bus,
161 "org.freedesktop.systemd1",
162 "/org/freedesktop/systemd1",
163 "org.freedesktop.systemd1.Manager",
164 "ListUnits",
165 &reply,
166 NULL,
167 DBUS_TYPE_INVALID);
c170f3a4 168 if (r < 0)
2265fbf7
SP
169 goto fail;
170
171 if (!dbus_message_iter_init(reply, &iter) ||
172 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
173 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
174 log_error("Failed to parse reply.");
175 r = -EIO;
176 goto fail;
177 }
178
179 for (dbus_message_iter_recurse(&iter, &sub);
180 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
181 dbus_message_iter_next(&sub)) {
182 struct unit_info u;
183 struct unit_times *t;
184
185 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
186 log_error("Failed to parse reply.");
187 r = -EIO;
188 goto fail;
189 }
190
191 if (c >= n_units) {
192 struct unit_times *w;
193
194 n_units = MAX(2*c, 16);
195 w = realloc(unit_times, sizeof(struct unit_times) * n_units);
196
197 if (!w) {
198 r = log_oom();
199 goto fail;
200 }
201
202 unit_times = w;
203 }
204 t = unit_times+c;
205 t->name = NULL;
206
207 r = bus_parse_unit_info(&sub, &u);
208 if (r < 0)
209 goto fail;
210
c170f3a4
LP
211 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
212
2265fbf7
SP
213 if (bus_get_uint64_property(bus, u.unit_path,
214 "org.freedesktop.systemd1.Unit",
215 "InactiveExitTimestampMonotonic",
216 &t->ixt) < 0 ||
217 bus_get_uint64_property(bus, u.unit_path,
218 "org.freedesktop.systemd1.Unit",
219 "ActiveEnterTimestampMonotonic",
220 &t->aet) < 0 ||
221 bus_get_uint64_property(bus, u.unit_path,
222 "org.freedesktop.systemd1.Unit",
223 "ActiveExitTimestampMonotonic",
224 &t->axt) < 0 ||
225 bus_get_uint64_property(bus, u.unit_path,
226 "org.freedesktop.systemd1.Unit",
227 "InactiveEnterTimestampMonotonic",
228 &t->iet) < 0) {
229 r = -EIO;
230 goto fail;
231 }
232
2265fbf7
SP
233 if (t->aet >= t->ixt)
234 t->time = t->aet - t->ixt;
235 else if (t->iet >= t->ixt)
236 t->time = t->iet - t->ixt;
237 else
238 t->time = 0;
239
240 if (t->ixt == 0)
241 continue;
242
243 t->name = strdup(u.id);
244 if (t->name == NULL) {
245 r = log_oom();
246 goto fail;
247 }
248 c++;
249 }
250
251 *out = unit_times;
252 return c;
c170f3a4 253
2265fbf7 254fail:
c170f3a4 255 free_unit_times(unit_times, (unsigned) c);
2265fbf7
SP
256 return r;
257}
258
c170f3a4 259static int acquire_boot_times(DBusConnection *bus, struct boot_times **bt) {
2265fbf7
SP
260 static struct boot_times times;
261 static bool cached = false;
c170f3a4 262
2265fbf7 263 if (cached)
c170f3a4
LP
264 goto finish;
265
266 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
2265fbf7
SP
267
268 if (bus_get_uint64_property(bus,
269 "/org/freedesktop/systemd1",
270 "org.freedesktop.systemd1.Manager",
271 "FirmwareTimestampMonotonic",
272 &times.firmware_time) < 0 ||
273 bus_get_uint64_property(bus,
274 "/org/freedesktop/systemd1",
275 "org.freedesktop.systemd1.Manager",
276 "LoaderTimestampMonotonic",
277 &times.loader_time) < 0 ||
278 bus_get_uint64_property(bus,
279 "/org/freedesktop/systemd1",
280 "org.freedesktop.systemd1.Manager",
281 "KernelTimestamp",
282 &times.kernel_time) < 0 ||
283 bus_get_uint64_property(bus,
284 "/org/freedesktop/systemd1",
285 "org.freedesktop.systemd1.Manager",
286 "InitRDTimestampMonotonic",
287 &times.initrd_time) < 0 ||
288 bus_get_uint64_property(bus,
289 "/org/freedesktop/systemd1",
290 "org.freedesktop.systemd1.Manager",
291 "UserspaceTimestampMonotonic",
292 &times.userspace_time) < 0 ||
293 bus_get_uint64_property(bus,
294 "/org/freedesktop/systemd1",
295 "org.freedesktop.systemd1.Manager",
296 "FinishTimestampMonotonic",
297 &times.finish_time) < 0)
c170f3a4 298 return -EIO;
2265fbf7 299
c170f3a4 300 if (times.finish_time <= 0) {
2265fbf7 301 log_error("Bootup is not yet finished. Please try again later.");
c170f3a4 302 return -EAGAIN;
2265fbf7
SP
303 }
304
2265fbf7
SP
305 if (times.initrd_time)
306 times.kernel_done_time = times.initrd_time;
307 else
308 times.kernel_done_time = times.userspace_time;
309
310 cached = true;
c170f3a4
LP
311
312finish:
313 *bt = &times;
314 return 0;
2265fbf7
SP
315}
316
c170f3a4
LP
317static int pretty_boot_time(DBusConnection *bus, char **_buf) {
318 char ts[FORMAT_TIMESPAN_MAX];
2265fbf7 319 struct boot_times *t;
2265fbf7 320 static char buf[4096];
c170f3a4
LP
321 size_t size;
322 char *ptr;
323 int r;
324
325 r = acquire_boot_times(bus, &t);
326 if (r < 0)
327 return r;
2265fbf7 328
c170f3a4
LP
329 ptr = buf;
330 size = sizeof(buf);
2265fbf7
SP
331
332 size = strpcpyf(&ptr, size, "Startup finished in ");
333 if (t->firmware_time)
c170f3a4 334 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time));
2265fbf7 335 if (t->loader_time)
c170f3a4 336 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time));
2265fbf7 337 if (t->kernel_time)
c170f3a4 338 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time));
2265fbf7 339 if (t->initrd_time > 0)
c170f3a4 340 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time));
2265fbf7 341
c170f3a4 342 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time));
2265fbf7 343 if (t->kernel_time > 0)
c170f3a4 344 size = strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time));
2265fbf7 345 else
c170f3a4 346 size = strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time));
2265fbf7 347
c170f3a4
LP
348 ptr = strdup(buf);
349 if (!ptr)
350 return log_oom();
351
352 *_buf = ptr;
353 return 0;
2265fbf7
SP
354}
355
c170f3a4
LP
356static void svg_graph_box(double height, double begin, double end) {
357 long long i;
358
2265fbf7
SP
359 /* outside box, fill */
360 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
2f6eb835 361 SCALE_X * (end - begin), SCALE_Y * height);
2265fbf7 362
c170f3a4 363 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
2265fbf7 364 /* lines for each second */
c170f3a4 365 if (i % 5000000 == 0)
2265fbf7
SP
366 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
367 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
2f6eb835 368 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
c170f3a4 369 else if (i % 1000000 == 0)
2265fbf7
SP
370 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
371 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
2f6eb835 372 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
2265fbf7
SP
373 else
374 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
2f6eb835 375 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
2265fbf7
SP
376 }
377}
378
c170f3a4 379static int analyze_plot(DBusConnection *bus) {
2265fbf7
SP
380 struct unit_times *times;
381 struct boot_times *boot;
382 struct utsname name;
383 int n, m = 1, y=0;
384 double width;
c170f3a4
LP
385 _cleanup_free_ char *pretty_times = NULL, *osname = NULL;
386 struct unit_times *u;
2265fbf7 387
c170f3a4
LP
388 n = acquire_boot_times(bus, &boot);
389 if (n < 0)
390 return n;
2265fbf7 391
c170f3a4
LP
392 n = pretty_boot_time(bus, &pretty_times);
393 if (n < 0)
394 return n;
2265fbf7 395
c170f3a4
LP
396 get_os_name(&osname);
397 assert_se(uname(&name) >= 0);
2265fbf7
SP
398
399 n = acquire_time_data(bus, &times);
c170f3a4 400 if (n <= 0)
2265fbf7
SP
401 return n;
402
403 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
404
2f6eb835 405 width = SCALE_X * (boot->firmware_time + boot->finish_time);
2265fbf7
SP
406 if (width < 800.0)
407 width = 800.0;
408
409 if (boot->firmware_time > boot->loader_time)
410 m++;
411 if (boot->loader_time) {
412 m++;
413 if (width < 1000.0)
414 width = 1000.0;
415 }
416 if (boot->initrd_time)
417 m++;
418 if (boot->kernel_time)
419 m++;
420
c170f3a4 421 for (u = times; u < times + n; u++) {
2265fbf7 422 double len;
c170f3a4 423
2265fbf7
SP
424 if (u->ixt < boot->userspace_time ||
425 u->ixt > boot->finish_time) {
426 free(u->name);
427 u->name = NULL;
428 continue;
429 }
2f6eb835 430 len = ((boot->firmware_time + u->ixt) * SCALE_X)
2265fbf7
SP
431 + (10.0 * strlen(u->name));
432 if (len > width)
433 width = len;
434
435 if (u->iet > u->ixt && u->iet <= boot->finish_time
436 && u->aet == 0 && u->axt == 0)
437 u->aet = u->axt = u->iet;
438 if (u->aet < u->ixt || u->aet > boot->finish_time)
439 u->aet = boot->finish_time;
440 if (u->axt < u->aet || u->aet > boot->finish_time)
441 u->axt = boot->finish_time;
442 if (u->iet < u->axt || u->iet > boot->finish_time)
443 u->iet = boot->finish_time;
444 m++;
445 }
446
447 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
448 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
449 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
450
451 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
452 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
2f6eb835 453 80.0 + width, 150.0 + (m * SCALE_Y));
2265fbf7
SP
454
455 /* write some basic info as a comment, including some help */
456 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
457 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
458 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
459 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
460 "<!-- point your browser to this file. -->\n\n"
461 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
462
463 /* style sheet */
464 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
465 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
466 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
467 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
468 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
469 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
470 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
471 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
472 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
473 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
474 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
475 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
476 "// line.sec1 { }\n"
477 " line.sec5 { stroke-width: 2; }\n"
478 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
479 " text { font-family: Verdana, Helvetica; font-size: 10; }\n"
c170f3a4
LP
480 " text.left { font-family: Verdana, Helvetica; font-size: 10; text-anchor: start; }\n"
481 " text.right { font-family: Verdana, Helvetica; font-size: 10; text-anchor: end; }\n"
2265fbf7
SP
482 " text.sec { font-size: 8; }\n"
483 " ]]>\n </style>\n</defs>\n\n");
484
485 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
486 svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
c170f3a4 487 isempty(osname) ? "Linux" : osname,
2265fbf7
SP
488 name.nodename, name.release, name.version, name.machine);
489 svg("<text x=\"20\" y=\"%.0f\">Legend: Red = Activating; Pink = Active; Dark Pink = Deactivating</text>",
2f6eb835 490 120.0 + (m *SCALE_Y));
2265fbf7 491
2f6eb835 492 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
2265fbf7
SP
493 svg_graph_box(m, -boot->firmware_time, boot->finish_time);
494
495 if (boot->firmware_time) {
c170f3a4
LP
496 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
497 svg_text(true, -(double) boot->firmware_time, y, "firmware");
2265fbf7
SP
498 y++;
499 }
500 if (boot->loader_time) {
c170f3a4
LP
501 svg_bar("loader", -(double) boot->loader_time, 0, y);
502 svg_text(true, -(double) boot->loader_time, y, "loader");
2265fbf7
SP
503 y++;
504 }
505 if (boot->kernel_time) {
506 svg_bar("kernel", 0, boot->kernel_done_time, y);
c170f3a4 507 svg_text(true, 0, y, "kernel");
2265fbf7
SP
508 y++;
509 }
510 if (boot->initrd_time) {
511 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
c170f3a4 512 svg_text(true, boot->initrd_time, y, "initrd");
2265fbf7
SP
513 y++;
514 }
515 svg_bar("userspace", boot->userspace_time, boot->finish_time, y);
c170f3a4 516 svg_text("left", boot->userspace_time, y, "userspace");
2265fbf7
SP
517 y++;
518
c170f3a4
LP
519 for (u = times; u < times + n; u++) {
520 char ts[FORMAT_TIMESPAN_MAX];
521
2265fbf7
SP
522 if (!u->name)
523 continue;
c170f3a4 524
2265fbf7
SP
525 svg_bar("activating", u->ixt, u->aet, y);
526 svg_bar("active", u->aet, u->axt, y);
527 svg_bar("deactivating", u->axt, u->iet, y);
c170f3a4 528
2f6eb835 529 if (u->ixt * SCALE_X > width * 2 / 3)
c170f3a4
LP
530 svg_text(false, u->ixt, y, u->time? "%s (%s)" : "%s", u->name, format_timespan(ts, sizeof(ts), u->time));
531 else
532 svg_text(true, u->ixt, y, u->time? "%s (%s)" : "%s", u->name, format_timespan(ts, sizeof(ts), u->time));
2265fbf7
SP
533 y++;
534 }
535 svg("</g>\n\n");
536
537 svg("</svg>");
c170f3a4
LP
538
539 free_unit_times(times, (unsigned) n);
540
2265fbf7
SP
541 return 0;
542}
543
c170f3a4 544static int analyze_blame(DBusConnection *bus) {
2265fbf7 545 struct unit_times *times;
c170f3a4
LP
546 unsigned i;
547 int n;
548
549 n = acquire_time_data(bus, &times);
550 if (n <= 0)
2265fbf7
SP
551 return n;
552
553 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
554
c170f3a4
LP
555 for (i = 0; i < (unsigned) n; i++) {
556 char ts[FORMAT_TIMESPAN_MAX];
557
558 if (times[i].time > 0)
559 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time), times[i].name);
2265fbf7 560 }
c170f3a4
LP
561
562 free_unit_times(times, (unsigned) n);
2265fbf7
SP
563 return 0;
564}
565
c170f3a4
LP
566static int analyze_time(DBusConnection *bus) {
567 _cleanup_free_ char *buf = NULL;
568 int r;
569
570 r = pretty_boot_time(bus, &buf);
571 if (r < 0)
572 return r;
573
574 puts(buf);
2265fbf7
SP
575 return 0;
576}
577
1700761b
SP
578static int graph_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
579
580 static const char * const colors[] = {
581 "Requires", "[color=\"black\"]",
582 "RequiresOverridable", "[color=\"black\"]",
583 "Requisite", "[color=\"darkblue\"]",
584 "RequisiteOverridable", "[color=\"darkblue\"]",
585 "Wants", "[color=\"grey66\"]",
586 "Conflicts", "[color=\"red\"]",
587 "ConflictedBy", "[color=\"red\"]",
588 "After", "[color=\"green\"]"
589 };
590
591 const char *c = NULL;
592 unsigned i;
593
594 assert(name);
595 assert(prop);
596 assert(iter);
597
598 for (i = 0; i < ELEMENTSOF(colors); i += 2)
599 if (streq(colors[i], prop)) {
600 c = colors[i+1];
601 break;
602 }
603
604 if (!c)
605 return 0;
606
607 if (arg_dot != DEP_ALL)
608 if ((arg_dot == DEP_ORDER) != streq(prop, "After"))
609 return 0;
610
611 if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY &&
612 dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
613 DBusMessageIter sub;
614
615 dbus_message_iter_recurse(iter, &sub);
616
617 for (dbus_message_iter_recurse(iter, &sub);
618 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
619 dbus_message_iter_next(&sub)) {
620 const char *s;
621
622 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
623 dbus_message_iter_get_basic(&sub, &s);
624 printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
625 }
626 }
627
628 return 0;
629}
630
631static int graph_one(DBusConnection *bus, const struct unit_info *u) {
632 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
633 const char *interface = "org.freedesktop.systemd1.Unit";
634 int r;
635 DBusMessageIter iter, sub, sub2, sub3;
636
637 assert(bus);
638 assert(u);
639
640 r = bus_method_call_with_reply(
641 bus,
642 "org.freedesktop.systemd1",
643 u->unit_path,
644 "org.freedesktop.DBus.Properties",
645 "GetAll",
646 &reply,
647 NULL,
648 DBUS_TYPE_STRING, &interface,
649 DBUS_TYPE_INVALID);
650 if (r < 0)
651 return r;
652
653 if (!dbus_message_iter_init(reply, &iter) ||
654 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
655 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
656 log_error("Failed to parse reply.");
657 return -EIO;
658 }
659
660 for (dbus_message_iter_recurse(&iter, &sub);
661 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
662 dbus_message_iter_next(&sub)) {
663 const char *prop;
664
665 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
666 dbus_message_iter_recurse(&sub, &sub2);
667
668 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0 ||
669 dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
670 log_error("Failed to parse reply.");
671 return -EIO;
672 }
673
674 dbus_message_iter_recurse(&sub2, &sub3);
675 r = graph_one_property(u->id, prop, &sub3);
676 if (r < 0)
677 return r;
678 }
679
680 return 0;
681}
682
683static int dot(DBusConnection *bus) {
684 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
685 DBusMessageIter iter, sub;
686 int r;
687
688 r = bus_method_call_with_reply(
689 bus,
690 "org.freedesktop.systemd1",
691 "/org/freedesktop/systemd1",
692 "org.freedesktop.systemd1.Manager",
693 "ListUnits",
694 &reply,
695 NULL,
696 DBUS_TYPE_INVALID);
697 if (r < 0)
698 return r;
699
700 if (!dbus_message_iter_init(reply, &iter) ||
701 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
702 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
703 log_error("Failed to parse reply.");
704 return -EIO;
705 }
706
707 printf("digraph systemd {\n");
708
709 for (dbus_message_iter_recurse(&iter, &sub);
710 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
711 dbus_message_iter_next(&sub)) {
712 struct unit_info u;
713
714 r = bus_parse_unit_info(&sub, &u);
715 if (r < 0)
716 return -EIO;
717
718 r = graph_one(bus, &u);
719 if (r < 0)
720 return r;
721 }
722
723 printf("}\n");
724
725 log_info(" Color legend: black = Requires\n"
726 " dark blue = Requisite\n"
727 " dark grey = Wants\n"
728 " red = Conflicts\n"
729 " green = After\n");
730
731 if (on_tty())
732 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
733 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
734
735 return 0;
736}
737
2265fbf7
SP
738static void analyze_help(void)
739{
740 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
741 "Process systemd profiling information\n\n"
742 " -h --help Show this help\n"
743 " --version Show package version\n"
744 " --system Connect to system manager\n"
1700761b
SP
745 " --user Connect to user service manager\n"
746 " --order When generating a dependency graph, show only order\n"
747 " --require When generating a dependency graph, show only requirement\n\n"
2265fbf7 748 "Commands:\n"
1700761b
SP
749 " time Print time spent in the kernel before reaching userspace\n"
750 " blame Print list of running units ordered by time to init\n"
751 " plot Output SVG graphic showing service initialization\n"
752 " dot Dump dependency graph (in dot(1) format)\n\n",
2265fbf7
SP
753 program_invocation_short_name);
754}
755
756static int parse_argv(int argc, char *argv[])
757{
758 enum {
759 ARG_VERSION = 0x100,
1700761b
SP
760 ARG_ORDER,
761 ARG_REQUIRE,
2265fbf7
SP
762 ARG_USER,
763 ARG_SYSTEM
764 };
765
766 static const struct option options[] = {
767 { "help", no_argument, NULL, 'h' },
768 { "version", no_argument, NULL, ARG_VERSION },
1700761b
SP
769 { "order", no_argument, NULL, ARG_ORDER },
770 { "require", no_argument, NULL, ARG_REQUIRE },
2265fbf7
SP
771 { "user", no_argument, NULL, ARG_USER },
772 { "system", no_argument, NULL, ARG_SYSTEM },
773 { NULL, 0, NULL, 0 }
774 };
775
776 assert(argc >= 0);
777 assert(argv);
778
c170f3a4 779 for (;;) {
2265fbf7 780 switch (getopt_long(argc, argv, "h", options, NULL)) {
c170f3a4
LP
781
782 case 'h':
783 analyze_help();
784 return 0;
785
786 case ARG_VERSION:
787 puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES);
788 return 0;
789
790 case ARG_USER:
791 arg_scope = UNIT_FILE_USER;
792 break;
793
794 case ARG_SYSTEM:
795 arg_scope = UNIT_FILE_SYSTEM;
796 break;
797
798 case ARG_ORDER:
799 arg_dot = DEP_ORDER;
800 break;
801
802 case ARG_REQUIRE:
803 arg_dot = DEP_REQUIRE;
804 break;
805
806 case -1:
807 return 1;
808
809 case '?':
810 return -EINVAL;
811
812 default:
813 assert_not_reached("Unhandled option");
2265fbf7
SP
814 }
815 }
816}
817
818int main(int argc, char *argv[]) {
819 int r;
820 DBusConnection *bus = NULL;
821
822 setlocale(LC_ALL, "");
c170f3a4 823 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
2265fbf7
SP
824 log_parse_environment();
825 log_open();
826
827 r = parse_argv(argc, argv);
2265fbf7 828 if (r < 0)
c170f3a4
LP
829 return EXIT_FAILURE;
830 else if (r <= 0)
831 return EXIT_SUCCESS;
2265fbf7
SP
832
833 bus = dbus_bus_get(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, NULL);
834 if (!bus)
c170f3a4 835 return EXIT_FAILURE;
2265fbf7
SP
836
837 if (!argv[optind] || streq(argv[optind], "time"))
838 r = analyze_time(bus);
839 else if (streq(argv[optind], "blame"))
840 r = analyze_blame(bus);
841 else if (streq(argv[optind], "plot"))
842 r = analyze_plot(bus);
1700761b
SP
843 else if (streq(argv[optind], "dot"))
844 r = dot(bus);
2265fbf7
SP
845 else
846 log_error("Unknown operation '%s'.", argv[optind]);
847
848 dbus_connection_unref(bus);
c170f3a4
LP
849
850 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2265fbf7 851}