]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/analyze/systemd-analyze.c
dbus-manager.c: log error, why switch-root is refused
[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>
e55933db 28#include <fnmatch.h>
2265fbf7
SP
29
30#include "install.h"
31#include "log.h"
32#include "dbus-common.h"
33#include "build.h"
34#include "util.h"
35#include "strxcpyx.h"
a5c32cff 36#include "fileio.h"
e55933db 37#include "strv.h"
2265fbf7 38
2f6eb835
LP
39#define SCALE_X (0.1 / 1000.0) /* pixels per us */
40#define SCALE_Y 20.0
41
2265fbf7 42#define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
c170f3a4 43
2265fbf7 44#define svg(...) printf(__VA_ARGS__)
c170f3a4
LP
45
46#define svg_bar(class, x1, x2, y) \
2265fbf7 47 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
c170f3a4 48 (class), \
2f6eb835
LP
49 SCALE_X * (x1), SCALE_Y * (y), \
50 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
c170f3a4
LP
51
52#define svg_text(b, x, y, format, ...) \
53 do { \
2f6eb835 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); \
c170f3a4
LP
55 svg(format, ## __VA_ARGS__); \
56 svg("</text>\n"); \
2265fbf7
SP
57 } while(false)
58
59static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
1700761b
SP
60static enum dot {
61 DEP_ALL,
62 DEP_ORDER,
63 DEP_REQUIRE
64} arg_dot = DEP_ALL;
e55933db
ŁS
65static char** arg_dot_from_patterns = NULL;
66static char** arg_dot_to_patterns = NULL;
2265fbf7 67
2265fbf7 68struct boot_times {
c170f3a4
LP
69 usec_t firmware_time;
70 usec_t loader_time;
71 usec_t kernel_time;
72 usec_t kernel_done_time;
73 usec_t initrd_time;
74 usec_t userspace_time;
75 usec_t finish_time;
2265fbf7
SP
76};
77struct unit_times {
78 char *name;
c170f3a4
LP
79 usec_t ixt;
80 usec_t iet;
81 usec_t axt;
82 usec_t aet;
83 usec_t time;
2265fbf7
SP
84};
85
c170f3a4 86static int bus_get_uint64_property(DBusConnection *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
2265fbf7 87 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
2265fbf7 88 DBusMessageIter iter, sub;
c170f3a4 89 int r;
2265fbf7 90
c170f3a4 91 r = bus_method_call_with_reply(
2265fbf7
SP
92 bus,
93 "org.freedesktop.systemd1",
94 path,
95 "org.freedesktop.DBus.Properties",
96 "Get",
97 &reply,
98 NULL,
99 DBUS_TYPE_STRING, &interface,
100 DBUS_TYPE_STRING, &property,
101 DBUS_TYPE_INVALID);
102 if (r < 0)
103 return r;
104
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.");
108 return -EIO;
109 }
110
111 dbus_message_iter_recurse(&iter, &sub);
112
113 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64) {
114 log_error("Failed to parse reply.");
115 return -EIO;
116 }
117
118 dbus_message_iter_get_basic(&sub, val);
119
120 return 0;
121}
122
c170f3a4 123static int compare_unit_time(const void *a, const void *b) {
2265fbf7
SP
124 return compare(((struct unit_times *)b)->time,
125 ((struct unit_times *)a)->time);
126}
127
c170f3a4 128static int compare_unit_start(const void *a, const void *b) {
2265fbf7
SP
129 return compare(((struct unit_times *)a)->ixt,
130 ((struct unit_times *)b)->ixt);
131}
132
c170f3a4 133static int get_os_name(char **_n) {
2265fbf7 134 char *n = NULL;
c170f3a4
LP
135 int r;
136
137 r = parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &n, NULL);
138 if (r < 0)
139 return r;
140
141 if (!n)
142 return -ENOENT;
2265fbf7 143
c170f3a4
LP
144 *_n = n;
145 return 0;
2265fbf7
SP
146}
147
c170f3a4
LP
148static void free_unit_times(struct unit_times *t, unsigned n) {
149 struct unit_times *p;
150
151 for (p = t; p < t + n; p++)
152 free(p->name);
153
154 free(t);
155}
156
157static int acquire_time_data(DBusConnection *bus, struct unit_times **out) {
2265fbf7
SP
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;
162
c170f3a4 163 r = bus_method_call_with_reply(
2265fbf7
SP
164 bus,
165 "org.freedesktop.systemd1",
166 "/org/freedesktop/systemd1",
167 "org.freedesktop.systemd1.Manager",
168 "ListUnits",
169 &reply,
170 NULL,
171 DBUS_TYPE_INVALID);
c170f3a4 172 if (r < 0)
2265fbf7
SP
173 goto fail;
174
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.");
179 r = -EIO;
180 goto fail;
181 }
182
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)) {
186 struct unit_info u;
187 struct unit_times *t;
188
189 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
190 log_error("Failed to parse reply.");
191 r = -EIO;
192 goto fail;
193 }
194
195 if (c >= n_units) {
196 struct unit_times *w;
197
198 n_units = MAX(2*c, 16);
199 w = realloc(unit_times, sizeof(struct unit_times) * n_units);
200
201 if (!w) {
202 r = log_oom();
203 goto fail;
204 }
205
206 unit_times = w;
207 }
208 t = unit_times+c;
209 t->name = NULL;
210
211 r = bus_parse_unit_info(&sub, &u);
212 if (r < 0)
213 goto fail;
214
c170f3a4
LP
215 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
216
2265fbf7
SP
217 if (bus_get_uint64_property(bus, u.unit_path,
218 "org.freedesktop.systemd1.Unit",
219 "InactiveExitTimestampMonotonic",
220 &t->ixt) < 0 ||
221 bus_get_uint64_property(bus, u.unit_path,
222 "org.freedesktop.systemd1.Unit",
223 "ActiveEnterTimestampMonotonic",
224 &t->aet) < 0 ||
225 bus_get_uint64_property(bus, u.unit_path,
226 "org.freedesktop.systemd1.Unit",
227 "ActiveExitTimestampMonotonic",
228 &t->axt) < 0 ||
229 bus_get_uint64_property(bus, u.unit_path,
230 "org.freedesktop.systemd1.Unit",
231 "InactiveEnterTimestampMonotonic",
232 &t->iet) < 0) {
233 r = -EIO;
234 goto fail;
235 }
236
2265fbf7
SP
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;
241 else
242 t->time = 0;
243
244 if (t->ixt == 0)
245 continue;
246
247 t->name = strdup(u.id);
248 if (t->name == NULL) {
249 r = log_oom();
250 goto fail;
251 }
252 c++;
253 }
254
255 *out = unit_times;
256 return c;
c170f3a4 257
2265fbf7 258fail:
c170f3a4 259 free_unit_times(unit_times, (unsigned) c);
2265fbf7
SP
260 return r;
261}
262
c170f3a4 263static int acquire_boot_times(DBusConnection *bus, struct boot_times **bt) {
2265fbf7
SP
264 static struct boot_times times;
265 static bool cached = false;
c170f3a4 266
2265fbf7 267 if (cached)
c170f3a4
LP
268 goto finish;
269
270 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
2265fbf7
SP
271
272 if (bus_get_uint64_property(bus,
273 "/org/freedesktop/systemd1",
274 "org.freedesktop.systemd1.Manager",
275 "FirmwareTimestampMonotonic",
276 &times.firmware_time) < 0 ||
277 bus_get_uint64_property(bus,
278 "/org/freedesktop/systemd1",
279 "org.freedesktop.systemd1.Manager",
280 "LoaderTimestampMonotonic",
281 &times.loader_time) < 0 ||
282 bus_get_uint64_property(bus,
283 "/org/freedesktop/systemd1",
284 "org.freedesktop.systemd1.Manager",
285 "KernelTimestamp",
286 &times.kernel_time) < 0 ||
287 bus_get_uint64_property(bus,
288 "/org/freedesktop/systemd1",
289 "org.freedesktop.systemd1.Manager",
290 "InitRDTimestampMonotonic",
291 &times.initrd_time) < 0 ||
292 bus_get_uint64_property(bus,
293 "/org/freedesktop/systemd1",
294 "org.freedesktop.systemd1.Manager",
295 "UserspaceTimestampMonotonic",
296 &times.userspace_time) < 0 ||
297 bus_get_uint64_property(bus,
298 "/org/freedesktop/systemd1",
299 "org.freedesktop.systemd1.Manager",
300 "FinishTimestampMonotonic",
301 &times.finish_time) < 0)
c170f3a4 302 return -EIO;
2265fbf7 303
c170f3a4 304 if (times.finish_time <= 0) {
2265fbf7 305 log_error("Bootup is not yet finished. Please try again later.");
c170f3a4 306 return -EAGAIN;
2265fbf7
SP
307 }
308
2265fbf7
SP
309 if (times.initrd_time)
310 times.kernel_done_time = times.initrd_time;
311 else
312 times.kernel_done_time = times.userspace_time;
313
314 cached = true;
c170f3a4
LP
315
316finish:
317 *bt = &times;
318 return 0;
2265fbf7
SP
319}
320
c170f3a4
LP
321static int pretty_boot_time(DBusConnection *bus, char **_buf) {
322 char ts[FORMAT_TIMESPAN_MAX];
2265fbf7 323 struct boot_times *t;
2265fbf7 324 static char buf[4096];
c170f3a4
LP
325 size_t size;
326 char *ptr;
327 int r;
328
329 r = acquire_boot_times(bus, &t);
330 if (r < 0)
331 return r;
2265fbf7 332
c170f3a4
LP
333 ptr = buf;
334 size = sizeof(buf);
2265fbf7
SP
335
336 size = strpcpyf(&ptr, size, "Startup finished in ");
337 if (t->firmware_time)
2fa4092c 338 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
2265fbf7 339 if (t->loader_time)
2fa4092c 340 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
2265fbf7 341 if (t->kernel_time)
2fa4092c 342 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
2265fbf7 343 if (t->initrd_time > 0)
2fa4092c 344 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
2265fbf7 345
2fa4092c 346 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
2265fbf7 347 if (t->kernel_time > 0)
2fa4092c 348 size = strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
2265fbf7 349 else
2fa4092c 350 size = strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
2265fbf7 351
c170f3a4
LP
352 ptr = strdup(buf);
353 if (!ptr)
354 return log_oom();
355
356 *_buf = ptr;
357 return 0;
2265fbf7
SP
358}
359
c170f3a4
LP
360static void svg_graph_box(double height, double begin, double end) {
361 long long i;
362
2265fbf7
SP
363 /* outside box, fill */
364 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
2f6eb835 365 SCALE_X * (end - begin), SCALE_Y * height);
2265fbf7 366
c170f3a4 367 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
2265fbf7 368 /* lines for each second */
c170f3a4 369 if (i % 5000000 == 0)
2265fbf7
SP
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",
2f6eb835 372 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
c170f3a4 373 else if (i % 1000000 == 0)
2265fbf7
SP
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",
2f6eb835 376 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
2265fbf7
SP
377 else
378 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
2f6eb835 379 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
2265fbf7
SP
380 }
381}
382
c170f3a4 383static int analyze_plot(DBusConnection *bus) {
2265fbf7
SP
384 struct unit_times *times;
385 struct boot_times *boot;
386 struct utsname name;
387 int n, m = 1, y=0;
388 double width;
c170f3a4
LP
389 _cleanup_free_ char *pretty_times = NULL, *osname = NULL;
390 struct unit_times *u;
2265fbf7 391
c170f3a4
LP
392 n = acquire_boot_times(bus, &boot);
393 if (n < 0)
394 return n;
2265fbf7 395
c170f3a4
LP
396 n = pretty_boot_time(bus, &pretty_times);
397 if (n < 0)
398 return n;
2265fbf7 399
c170f3a4
LP
400 get_os_name(&osname);
401 assert_se(uname(&name) >= 0);
2265fbf7
SP
402
403 n = acquire_time_data(bus, &times);
c170f3a4 404 if (n <= 0)
2265fbf7
SP
405 return n;
406
407 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
408
2f6eb835 409 width = SCALE_X * (boot->firmware_time + boot->finish_time);
2265fbf7
SP
410 if (width < 800.0)
411 width = 800.0;
412
413 if (boot->firmware_time > boot->loader_time)
414 m++;
415 if (boot->loader_time) {
416 m++;
417 if (width < 1000.0)
418 width = 1000.0;
419 }
420 if (boot->initrd_time)
421 m++;
422 if (boot->kernel_time)
423 m++;
424
c170f3a4 425 for (u = times; u < times + n; u++) {
2265fbf7 426 double len;
c170f3a4 427
2265fbf7
SP
428 if (u->ixt < boot->userspace_time ||
429 u->ixt > boot->finish_time) {
430 free(u->name);
431 u->name = NULL;
432 continue;
433 }
2f6eb835 434 len = ((boot->firmware_time + u->ixt) * SCALE_X)
2265fbf7
SP
435 + (10.0 * strlen(u->name));
436 if (len > width)
437 width = len;
438
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;
448 m++;
449 }
450
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");
454
455 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
456 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
2f6eb835 457 80.0 + width, 150.0 + (m * SCALE_Y));
2265fbf7
SP
458
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);
466
467 /* style sheet */
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"
480 "// line.sec1 { }\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"
c170f3a4
LP
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"
2265fbf7
SP
486 " text.sec { font-size: 8; }\n"
487 " ]]>\n </style>\n</defs>\n\n");
488
489 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
490 svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
c170f3a4 491 isempty(osname) ? "Linux" : osname,
2265fbf7
SP
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>",
2f6eb835 494 120.0 + (m *SCALE_Y));
2265fbf7 495
2f6eb835 496 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
2265fbf7
SP
497 svg_graph_box(m, -boot->firmware_time, boot->finish_time);
498
499 if (boot->firmware_time) {
c170f3a4
LP
500 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
501 svg_text(true, -(double) boot->firmware_time, y, "firmware");
2265fbf7
SP
502 y++;
503 }
504 if (boot->loader_time) {
c170f3a4
LP
505 svg_bar("loader", -(double) boot->loader_time, 0, y);
506 svg_text(true, -(double) boot->loader_time, y, "loader");
2265fbf7
SP
507 y++;
508 }
509 if (boot->kernel_time) {
510 svg_bar("kernel", 0, boot->kernel_done_time, y);
c170f3a4 511 svg_text(true, 0, y, "kernel");
2265fbf7
SP
512 y++;
513 }
514 if (boot->initrd_time) {
515 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
c170f3a4 516 svg_text(true, boot->initrd_time, y, "initrd");
2265fbf7
SP
517 y++;
518 }
519 svg_bar("userspace", boot->userspace_time, boot->finish_time, y);
c170f3a4 520 svg_text("left", boot->userspace_time, y, "userspace");
2265fbf7
SP
521 y++;
522
c170f3a4
LP
523 for (u = times; u < times + n; u++) {
524 char ts[FORMAT_TIMESPAN_MAX];
fd4a5ec6 525 bool b;
c170f3a4 526
2265fbf7
SP
527 if (!u->name)
528 continue;
c170f3a4 529
2265fbf7
SP
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);
c170f3a4 533
fd4a5ec6
ZJS
534 b = u->ixt * SCALE_X > width * 2 / 3;
535 if (u->time)
536 svg_text(b, u->ixt, y, "%s (%s)",
2fa4092c 537 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
c170f3a4 538 else
fd4a5ec6 539 svg_text(b, u->ixt, y, "%s", u->name);
2265fbf7
SP
540 y++;
541 }
542 svg("</g>\n\n");
543
544 svg("</svg>");
c170f3a4
LP
545
546 free_unit_times(times, (unsigned) n);
547
2265fbf7
SP
548 return 0;
549}
550
c170f3a4 551static int analyze_blame(DBusConnection *bus) {
2265fbf7 552 struct unit_times *times;
c170f3a4
LP
553 unsigned i;
554 int n;
555
556 n = acquire_time_data(bus, &times);
557 if (n <= 0)
2265fbf7
SP
558 return n;
559
560 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
561
c170f3a4
LP
562 for (i = 0; i < (unsigned) n; i++) {
563 char ts[FORMAT_TIMESPAN_MAX];
564
565 if (times[i].time > 0)
2fa4092c 566 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
2265fbf7 567 }
c170f3a4
LP
568
569 free_unit_times(times, (unsigned) n);
2265fbf7
SP
570 return 0;
571}
572
c170f3a4
LP
573static int analyze_time(DBusConnection *bus) {
574 _cleanup_free_ char *buf = NULL;
575 int r;
576
577 r = pretty_boot_time(bus, &buf);
578 if (r < 0)
579 return r;
580
581 puts(buf);
2265fbf7
SP
582 return 0;
583}
584
e55933db 585static int graph_one_property(const char *name, const char *prop, DBusMessageIter *iter, char* patterns[]) {
1700761b
SP
586
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\"]"
596 };
597
598 const char *c = NULL;
599 unsigned i;
600
601 assert(name);
602 assert(prop);
603 assert(iter);
604
605 for (i = 0; i < ELEMENTSOF(colors); i += 2)
606 if (streq(colors[i], prop)) {
607 c = colors[i+1];
608 break;
609 }
610
611 if (!c)
612 return 0;
613
614 if (arg_dot != DEP_ALL)
615 if ((arg_dot == DEP_ORDER) != streq(prop, "After"))
616 return 0;
617
618 if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY &&
619 dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
620 DBusMessageIter sub;
621
622 dbus_message_iter_recurse(iter, &sub);
623
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)) {
627 const char *s;
e55933db 628 char **p;
816f25e8 629 bool match_found;
1700761b
SP
630
631 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
632 dbus_message_iter_get_basic(&sub, &s);
e55933db 633
816f25e8 634 if (!strv_isempty(arg_dot_from_patterns)) {
e55933db 635 match_found = false;
816f25e8
LP
636
637 STRV_FOREACH(p, arg_dot_from_patterns)
638 if (fnmatch(*p, name, 0) == 0) {
639 match_found = true;
640 break;
641 }
642
643 if (!match_found)
644 continue;
e55933db 645 }
e55933db 646
816f25e8 647 if (!strv_isempty(arg_dot_to_patterns)) {
e55933db 648 match_found = false;
816f25e8
LP
649
650 STRV_FOREACH(p, arg_dot_to_patterns)
651 if (fnmatch(*p, s, 0) == 0) {
652 match_found = true;
653 break;
654 }
655
656 if (!match_found)
657 continue;
e55933db 658 }
e55933db 659
816f25e8 660 if (!strv_isempty(patterns)) {
e55933db 661 match_found = false;
816f25e8
LP
662
663 STRV_FOREACH(p, patterns)
664 if (fnmatch(*p, name, 0) == 0 || fnmatch(*p, s, 0) == 0) {
665 match_found = true;
666 break;
667 }
668 if (!match_found)
669 continue;
e55933db 670 }
e55933db 671
1700761b
SP
672 printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
673 }
674 }
675
676 return 0;
677}
678
e55933db 679static int graph_one(DBusConnection *bus, const struct unit_info *u, char *patterns[]) {
1700761b
SP
680 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
681 const char *interface = "org.freedesktop.systemd1.Unit";
682 int r;
683 DBusMessageIter iter, sub, sub2, sub3;
684
685 assert(bus);
686 assert(u);
687
688 r = bus_method_call_with_reply(
689 bus,
690 "org.freedesktop.systemd1",
691 u->unit_path,
692 "org.freedesktop.DBus.Properties",
693 "GetAll",
694 &reply,
695 NULL,
696 DBUS_TYPE_STRING, &interface,
697 DBUS_TYPE_INVALID);
698 if (r < 0)
699 return r;
700
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.");
705 return -EIO;
706 }
707
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)) {
711 const char *prop;
712
713 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
714 dbus_message_iter_recurse(&sub, &sub2);
715
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.");
719 return -EIO;
720 }
721
722 dbus_message_iter_recurse(&sub2, &sub3);
e55933db 723 r = graph_one_property(u->id, prop, &sub3, patterns);
1700761b
SP
724 if (r < 0)
725 return r;
726 }
727
728 return 0;
729}
730
e55933db 731static int dot(DBusConnection *bus, char* patterns[]) {
1700761b
SP
732 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
733 DBusMessageIter iter, sub;
734 int r;
735
736 r = bus_method_call_with_reply(
737 bus,
738 "org.freedesktop.systemd1",
739 "/org/freedesktop/systemd1",
740 "org.freedesktop.systemd1.Manager",
741 "ListUnits",
742 &reply,
743 NULL,
744 DBUS_TYPE_INVALID);
745 if (r < 0)
746 return r;
747
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.");
752 return -EIO;
753 }
754
755 printf("digraph systemd {\n");
756
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)) {
760 struct unit_info u;
761
762 r = bus_parse_unit_info(&sub, &u);
763 if (r < 0)
764 return -EIO;
765
e55933db 766 r = graph_one(bus, &u, patterns);
1700761b
SP
767 if (r < 0)
768 return r;
769 }
770
771 printf("}\n");
772
773 log_info(" Color legend: black = Requires\n"
774 " dark blue = Requisite\n"
775 " dark grey = Wants\n"
776 " red = Conflicts\n"
777 " green = After\n");
778
779 if (on_tty())
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");
782
783 return 0;
784}
785
2265fbf7
SP
786static void analyze_help(void)
787{
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"
1700761b
SP
793 " --user Connect to user service manager\n"
794 " --order When generating a dependency graph, show only order\n"
f5b7a3fb
LP
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"
2265fbf7 799 "Commands:\n"
1700761b
SP
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",
2265fbf7
SP
804 program_invocation_short_name);
805}
806
807static int parse_argv(int argc, char *argv[])
808{
809 enum {
810 ARG_VERSION = 0x100,
1700761b
SP
811 ARG_ORDER,
812 ARG_REQUIRE,
2265fbf7 813 ARG_USER,
e55933db
ŁS
814 ARG_SYSTEM,
815 ARG_DOT_FROM_PATTERN,
816 ARG_DOT_TO_PATTERN
2265fbf7
SP
817 };
818
819 static const struct option options[] = {
820 { "help", no_argument, NULL, 'h' },
821 { "version", no_argument, NULL, ARG_VERSION },
1700761b
SP
822 { "order", no_argument, NULL, ARG_ORDER },
823 { "require", no_argument, NULL, ARG_REQUIRE },
2265fbf7
SP
824 { "user", no_argument, NULL, ARG_USER },
825 { "system", no_argument, NULL, ARG_SYSTEM },
e55933db
ŁS
826 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN},
827 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
2265fbf7
SP
828 { NULL, 0, NULL, 0 }
829 };
830
831 assert(argc >= 0);
832 assert(argv);
833
c170f3a4 834 for (;;) {
2265fbf7 835 switch (getopt_long(argc, argv, "h", options, NULL)) {
c170f3a4
LP
836
837 case 'h':
838 analyze_help();
839 return 0;
840
841 case ARG_VERSION:
842 puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES);
843 return 0;
844
845 case ARG_USER:
846 arg_scope = UNIT_FILE_USER;
847 break;
848
849 case ARG_SYSTEM:
850 arg_scope = UNIT_FILE_SYSTEM;
851 break;
852
853 case ARG_ORDER:
854 arg_dot = DEP_ORDER;
855 break;
856
857 case ARG_REQUIRE:
858 arg_dot = DEP_REQUIRE;
859 break;
860
e55933db 861 case ARG_DOT_FROM_PATTERN:
903a0b07
LP
862 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
863 return log_oom();
864
e55933db
ŁS
865 break;
866
867 case ARG_DOT_TO_PATTERN:
903a0b07
LP
868 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
869 return log_oom();
870
e55933db
ŁS
871 break;
872
c170f3a4
LP
873 case -1:
874 return 1;
875
876 case '?':
877 return -EINVAL;
878
879 default:
880 assert_not_reached("Unhandled option");
2265fbf7
SP
881 }
882 }
883}
884
885int main(int argc, char *argv[]) {
886 int r;
887 DBusConnection *bus = NULL;
888
889 setlocale(LC_ALL, "");
c170f3a4 890 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
2265fbf7
SP
891 log_parse_environment();
892 log_open();
893
894 r = parse_argv(argc, argv);
2265fbf7 895 if (r < 0)
c170f3a4
LP
896 return EXIT_FAILURE;
897 else if (r <= 0)
898 return EXIT_SUCCESS;
2265fbf7
SP
899
900 bus = dbus_bus_get(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, NULL);
901 if (!bus)
c170f3a4 902 return EXIT_FAILURE;
2265fbf7
SP
903
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);
1700761b 910 else if (streq(argv[optind], "dot"))
e55933db 911 r = dot(bus, argv+optind+1);
2265fbf7
SP
912 else
913 log_error("Unknown operation '%s'.", argv[optind]);
914
e55933db
ŁS
915 strv_free(arg_dot_from_patterns);
916 strv_free(arg_dot_to_patterns);
2265fbf7 917 dbus_connection_unref(bus);
c170f3a4
LP
918
919 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2265fbf7 920}