analyze.c revision 07d0eaa0171d6e30b85a4b84b3287509406f9451
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering/***
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering This file is part of systemd.
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering Copyright 2010-2013 Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering Copyright 2013 Simon Peeters
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering systemd is free software; you can redistribute it and/or modify it
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering under the terms of the GNU Lesser General Public License as published by
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering (at your option) any later version.
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering systemd is distributed in the hope that it will be useful, but
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering Lesser General Public License for more details.
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering You should have received a copy of the GNU Lesser General Public License
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering***/
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include <stdio.h>
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include <stdlib.h>
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include <getopt.h>
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include <locale.h>
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include <sys/utsname.h>
24882e06c135584f16f31ba8a00fecde8b7f6fadLennart Poettering#include <fnmatch.h>
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "sd-bus.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "bus-util.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "bus-error.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "install.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "log.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "build.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "util.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "strxcpyx.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "fileio.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "strv.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "unit-name.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "special.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "hashmap.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#include "pager.h"
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#define SCALE_X (0.1 / 1000.0) /* pixels per us */
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#define SCALE_Y 20.0
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#define svg(...) printf(__VA_ARGS__)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#define svg_bar(class, x1, x2, y) \
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering (class), \
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering SCALE_X * (x1), SCALE_Y * (y), \
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering#define svg_text(b, x, y, format, ...) \
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering do { \
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering svg(format, ## __VA_ARGS__); \
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering svg("</text>\n"); \
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering } while(false)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic enum dot {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering DEP_ALL,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering DEP_ORDER,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering DEP_REQUIRE
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering} arg_dot = DEP_ALL;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic char** arg_dot_from_patterns = NULL;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic char** arg_dot_to_patterns = NULL;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic usec_t arg_fuzz = 0;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic bool arg_no_pager = false;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstruct boot_times {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t firmware_time;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t loader_time;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t kernel_time;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t kernel_done_time;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t initrd_time;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t userspace_time;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t finish_time;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t generators_start_time;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t generators_finish_time;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t unitsload_start_time;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t unitsload_finish_time;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering};
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstruct unit_info {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering const char *id;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering const char *description;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering const char *load_state;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering const char *active_state;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering const char *sub_state;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering const char *following;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering const char *unit_path;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering uint32_t job_id;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering const char *job_type;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering const char *job_path;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering};
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstruct unit_times {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering char *name;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t activating;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t activated;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t deactivated;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t deactivating;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering usec_t time;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering};
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic void pager_open_if_enabled(void) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (arg_no_pager)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering pager_open(false);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering}
72c0a2c255b172ebbb2a2b7dab7c9aec4c9582d9Lennart Poettering
72c0a2c255b172ebbb2a2b7dab7c9aec4c9582d9Lennart Poetteringstatic int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
72c0a2c255b172ebbb2a2b7dab7c9aec4c9582d9Lennart Poettering _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering int r;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering r = sd_bus_get_property_trivial(
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering bus,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "org.freedesktop.systemd1",
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering path,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering interface,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering property,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &error,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering 't', val);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (r < 0) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return r;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering }
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return 0;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering}
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic int compare_unit_time(const void *a, const void *b) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return compare(((struct unit_times *)b)->time,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering ((struct unit_times *)a)->time);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering}
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic int compare_unit_start(const void *a, const void *b) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return compare(((struct unit_times *)a)->activating,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering ((struct unit_times *)b)->activating);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering}
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic int get_os_name(char **_n) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering char *n = NULL;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering int r;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering r = parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &n, NULL);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (r < 0)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return r;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (!n)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return -ENOENT;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering *_n = n;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return 0;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering}
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic void free_unit_times(struct unit_times *t, unsigned n) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering struct unit_times *p;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering for (p = t; p < t + n; p++)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering free(p->name);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering free(t);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering}
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic int bus_parse_unit_info(sd_bus_message *message, struct unit_info *u) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering int r = 0;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering assert(message);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering assert(u);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering r = sd_bus_message_read(message, "(ssssssouso)", &u->id,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &u->description,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &u->load_state,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &u->active_state,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &u->sub_state,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &u->following,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &u->unit_path,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &u->job_id,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &u->job_type,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &u->job_path);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (r < 0) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering log_error("Failed to parse message as unit_info.");
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return -EIO;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering }
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return r;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering}
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic int bus_get_unit_property_strv(sd_bus *bus, const char *unit_path, const char *prop, char ***strv) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering int r;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering const char *s;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering r = sd_bus_get_property(
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering bus,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "org.freedesktop.systemd1",
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering unit_path,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "org.freedesktop.systemd1.Unit",
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering prop,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &error,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &reply,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "as");
72c0a2c255b172ebbb2a2b7dab7c9aec4c9582d9Lennart Poettering if (r < 0) {
72c0a2c255b172ebbb2a2b7dab7c9aec4c9582d9Lennart Poettering log_error("Failed to get unit property: %s %s", prop, bus_error_message(&error, -r));
72c0a2c255b172ebbb2a2b7dab7c9aec4c9582d9Lennart Poettering return r;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering }
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s");
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (r < 0)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return r;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering while ((r = sd_bus_message_read(reply, "s", &s)) > 0) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering r = strv_extend(strv, s);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (r < 0) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering log_oom();
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return r;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering }
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering }
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return r;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering}
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic int acquire_time_data(sd_bus *bus, struct unit_times **out) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering int r, c = 0, n_units = 0;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering struct unit_times *unit_times = NULL;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering struct unit_info u;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering r = sd_bus_call_method(
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering bus,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "org.freedesktop.systemd1",
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering "/org/freedesktop/systemd1",
587fec427c80b6c34dcf1d7570f891fcb652a7c5Lennart Poettering "org.freedesktop.systemd1.Manager",
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "ListUnits",
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &error,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &reply,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "");
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (r < 0) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering goto fail;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering }
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (r < 0)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering goto fail;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering while ((r = bus_parse_unit_info(reply, &u)) > 0) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering struct unit_times *t;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (r < 0)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering goto fail;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (c >= n_units) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering struct unit_times *w;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering n_units = MAX(2*c, 16);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering w = realloc(unit_times, sizeof(struct unit_times) * n_units);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (!w) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering r = log_oom();
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering goto fail;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering }
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering unit_times = w;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering }
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering t = unit_times+c;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering t->name = NULL;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering assert_cc(sizeof(usec_t) == sizeof(uint64_t));
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (bus_get_uint64_property(bus, u.unit_path,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "org.freedesktop.systemd1.Unit",
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "InactiveExitTimestampMonotonic",
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &t->activating) < 0 ||
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering bus_get_uint64_property(bus, u.unit_path,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "org.freedesktop.systemd1.Unit",
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "ActiveEnterTimestampMonotonic",
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &t->activated) < 0 ||
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering bus_get_uint64_property(bus, u.unit_path,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "org.freedesktop.systemd1.Unit",
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "ActiveExitTimestampMonotonic",
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &t->deactivating) < 0 ||
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering bus_get_uint64_property(bus, u.unit_path,
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "org.freedesktop.systemd1.Unit",
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering "InactiveEnterTimestampMonotonic",
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering &t->deactivated) < 0) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering r = -EIO;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering goto fail;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering }
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (t->activated >= t->activating)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering t->time = t->activated - t->activating;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering else if (t->deactivated >= t->activating)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering t->time = t->deactivated - t->activating;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering else
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering t->time = 0;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (t->activating == 0)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering continue;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering t->name = strdup(u.id);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (t->name == NULL) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering r = log_oom();
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering goto fail;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering }
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering c++;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering }
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering *out = unit_times;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return c;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringfail:
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering free_unit_times(unit_times, (unsigned) c);
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering return r;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering}
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poetteringstatic int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering static struct boot_times times;
ce30c8dcb41dfe9264f79f30c7f51c0e74576638Lennart Poettering static bool cached = false;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering if (cached)
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering goto finish;
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering assert_cc(sizeof(usec_t) == sizeof(uint64_t));
b6e676ce41508e2aeea22202fc8f234126177f52Lennart Poettering
if (bus_get_uint64_property(bus,
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"FirmwareTimestampMonotonic",
&times.firmware_time) < 0 ||
bus_get_uint64_property(bus,
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"LoaderTimestampMonotonic",
&times.loader_time) < 0 ||
bus_get_uint64_property(bus,
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"KernelTimestamp",
&times.kernel_time) < 0 ||
bus_get_uint64_property(bus,
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"InitRDTimestampMonotonic",
&times.initrd_time) < 0 ||
bus_get_uint64_property(bus,
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"UserspaceTimestampMonotonic",
&times.userspace_time) < 0 ||
bus_get_uint64_property(bus,
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"FinishTimestampMonotonic",
&times.finish_time) < 0 ||
bus_get_uint64_property(bus,
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"GeneratorsStartTimestampMonotonic",
&times.generators_start_time) < 0 ||
bus_get_uint64_property(bus,
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"GeneratorsFinishTimestampMonotonic",
&times.generators_finish_time) < 0 ||
bus_get_uint64_property(bus,
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"UnitsLoadStartTimestampMonotonic",
&times.unitsload_start_time) < 0 ||
bus_get_uint64_property(bus,
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"UnitsLoadFinishTimestampMonotonic",
&times.unitsload_finish_time) < 0)
return -EIO;
if (times.finish_time <= 0) {
log_error("Bootup is not yet finished. Please try again later.");
return -EAGAIN;
}
if (times.initrd_time)
times.kernel_done_time = times.initrd_time;
else
times.kernel_done_time = times.userspace_time;
cached = true;
finish:
*bt = &times;
return 0;
}
static int pretty_boot_time(sd_bus *bus, char **_buf) {
char ts[FORMAT_TIMESPAN_MAX];
struct boot_times *t;
static char buf[4096];
size_t size;
char *ptr;
int r;
r = acquire_boot_times(bus, &t);
if (r < 0)
return r;
ptr = buf;
size = sizeof(buf);
size = strpcpyf(&ptr, size, "Startup finished in ");
if (t->firmware_time)
size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
if (t->loader_time)
size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
if (t->kernel_time)
size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
if (t->initrd_time > 0)
size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
if (t->kernel_time > 0)
strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
else
strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
ptr = strdup(buf);
if (!ptr)
return log_oom();
*_buf = ptr;
return 0;
}
static void svg_graph_box(double height, double begin, double end) {
long long i;
/* outside box, fill */
svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
SCALE_X * (end - begin), SCALE_Y * height);
for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
/* lines for each second */
if (i % 5000000 == 0)
svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
else if (i % 1000000 == 0)
svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
else
svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
SCALE_X * i, SCALE_X * i, SCALE_Y * height);
}
}
static int analyze_plot(sd_bus *bus) {
struct unit_times *times;
struct boot_times *boot;
struct utsname name;
int n, m = 1, y=0;
double width;
_cleanup_free_ char *pretty_times = NULL, *osname = NULL;
struct unit_times *u;
n = acquire_boot_times(bus, &boot);
if (n < 0)
return n;
n = pretty_boot_time(bus, &pretty_times);
if (n < 0)
return n;
get_os_name(&osname);
assert_se(uname(&name) >= 0);
n = acquire_time_data(bus, &times);
if (n <= 0)
return n;
qsort(times, n, sizeof(struct unit_times), compare_unit_start);
width = SCALE_X * (boot->firmware_time + boot->finish_time);
if (width < 800.0)
width = 800.0;
if (boot->firmware_time > boot->loader_time)
m++;
if (boot->loader_time) {
m++;
if (width < 1000.0)
width = 1000.0;
}
if (boot->initrd_time)
m++;
if (boot->kernel_time)
m++;
for (u = times; u < times + n; u++) {
double text_start, text_width;
if (u->activating < boot->userspace_time ||
u->activating > boot->finish_time) {
free(u->name);
u->name = NULL;
continue;
}
/* If the text cannot fit on the left side then
* increase the svg width so it fits on the right.
* TODO: calculate the text width more accurately */
text_width = 8.0 * strlen(u->name);
text_start = (boot->firmware_time + u->activating) * SCALE_X;
if (text_width > text_start && text_width + text_start > width)
width = text_width + text_start;
if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
&& u->activated == 0 && u->deactivating == 0)
u->activated = u->deactivating = u->deactivated;
if (u->activated < u->activating || u->activated > boot->finish_time)
u->activated = boot->finish_time;
if (u->deactivating < u->activated || u->activated > boot->finish_time)
u->deactivating = boot->finish_time;
if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
u->deactivated = boot->finish_time;
m++;
}
svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
"\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
"xmlns=\"http://www.w3.org/2000/svg\">\n\n",
80.0 + width, 150.0 + (m * SCALE_Y) +
5 * SCALE_Y /* legend */);
/* write some basic info as a comment, including some help */
svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
"<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
"<!-- that render these files properly but much slower are ImageMagick, -->\n"
"<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
"<!-- point your browser to this file. -->\n\n"
"<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
/* style sheet */
svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
" rect { stroke-width: 1; stroke-opacity: 0; }\n"
" rect.background { fill: rgb(255,255,255); }\n"
" rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
" rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
" rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
" rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
" rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
" rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
" rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
" rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
" rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
" rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
" line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
"// line.sec1 { }\n"
" line.sec5 { stroke-width: 2; }\n"
" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
" text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
" text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
" text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
" text.sec { font-size: 10px; }\n"
" ]]>\n </style>\n</defs>\n\n");
svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
isempty(osname) ? "Linux" : osname,
name.nodename, name.release, name.version, name.machine);
svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
svg_graph_box(m, -boot->firmware_time, boot->finish_time);
if (boot->firmware_time) {
svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
svg_text(true, -(double) boot->firmware_time, y, "firmware");
y++;
}
if (boot->loader_time) {
svg_bar("loader", -(double) boot->loader_time, 0, y);
svg_text(true, -(double) boot->loader_time, y, "loader");
y++;
}
if (boot->kernel_time) {
svg_bar("kernel", 0, boot->kernel_done_time, y);
svg_text(true, 0, y, "kernel");
y++;
}
if (boot->initrd_time) {
svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
svg_text(true, boot->initrd_time, y, "initrd");
y++;
}
svg_bar("active", boot->userspace_time, boot->finish_time, y);
svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
svg_text(true, boot->userspace_time, y, "systemd");
y++;
for (u = times; u < times + n; u++) {
char ts[FORMAT_TIMESPAN_MAX];
bool b;
if (!u->name)
continue;
svg_bar("activating", u->activating, u->activated, y);
svg_bar("active", u->activated, u->deactivating, y);
svg_bar("deactivating", u->deactivating, u->deactivated, y);
/* place the text on the left if we have passed the half of the svg width */
b = u->activating * SCALE_X < width / 2;
if (u->time)
svg_text(b, u->activating, y, "%s (%s)",
u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
else
svg_text(b, u->activating, y, "%s", u->name);
y++;
}
/* Legend */
y++;
svg_bar("activating", 0, 300000, y);
svg_text(true, 400000, y, "Activating");
y++;
svg_bar("active", 0, 300000, y);
svg_text(true, 400000, y, "Active");
y++;
svg_bar("deactivating", 0, 300000, y);
svg_text(true, 400000, y, "Deactivating");
y++;
svg_bar("generators", 0, 300000, y);
svg_text(true, 400000, y, "Generators");
y++;
svg_bar("unitsload", 0, 300000, y);
svg_text(true, 400000, y, "Loading unit files");
y++;
svg("</g>\n\n");
svg("</svg>");
free_unit_times(times, (unsigned) n);
return 0;
}
static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
bool last, struct unit_times *times, struct boot_times *boot) {
unsigned int i;
char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
for (i = level; i != 0; i--)
printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));
printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
if (times) {
if (times->time)
printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name,
format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
else if (times->activated > boot->userspace_time)
printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
else
printf("%s", name);
} else printf("%s", name);
printf("\n");
return 0;
}
static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
_cleanup_free_ char *path;
int r = 0;
char **ret = NULL;
assert(bus);
assert(name);
assert(deps);
path = unit_dbus_path_from_name(name);
if (path == NULL)
return -EINVAL;
r = bus_get_unit_property_strv(bus, path, "After", &ret);
if (r < 0)
strv_free(ret);
else
*deps = ret;
return r;
}
static Hashmap *unit_times_hashmap;
static int list_dependencies_compare(const void *_a, const void *_b) {
const char **a = (const char**) _a, **b = (const char**) _b;
usec_t usa = 0, usb = 0;
struct unit_times *times;
times = hashmap_get(unit_times_hashmap, *a);
if (times)
usa = times->activated;
times = hashmap_get(unit_times_hashmap, *b);
if (times)
usb = times->activated;
return usb - usa;
}
static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
unsigned int branches) {
_cleanup_strv_free_ char **deps = NULL;
char **c;
int r = 0;
usec_t service_longest = 0;
int to_print = 0;
struct unit_times *times;
struct boot_times *boot;
if(strv_extend(units, name))
return log_oom();
r = list_dependencies_get_dependencies(bus, name, &deps);
if (r < 0)
return r;
qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
r = acquire_boot_times(bus, &boot);
if (r < 0)
return r;
STRV_FOREACH(c, deps) {
times = hashmap_get(unit_times_hashmap, *c);
if (times
&& times->activated
&& times->activated <= boot->finish_time
&& (times->activated >= service_longest
|| service_longest == 0)) {
service_longest = times->activated;
break;
}
}
if (service_longest == 0 )
return r;
STRV_FOREACH(c, deps) {
times = hashmap_get(unit_times_hashmap, *c);
if (times && times->activated
&& times->activated <= boot->finish_time
&& (service_longest - times->activated) <= arg_fuzz) {
to_print++;
}
}
if(!to_print)
return r;
STRV_FOREACH(c, deps) {
times = hashmap_get(unit_times_hashmap, *c);
if (!times
|| !times->activated
|| times->activated > boot->finish_time
|| service_longest - times->activated > arg_fuzz)
continue;
to_print--;
r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
if (r < 0)
return r;
if (strv_contains(*units, *c)) {
r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
true, NULL, boot);
if (r < 0)
return r;
continue;
}
r = list_dependencies_one(bus, *c, level + 1, units,
(branches << 1) | (to_print ? 1 : 0));
if (r < 0)
return r;
if (!to_print)
break;
}
return 0;
}
static int list_dependencies(sd_bus *bus, const char *name) {
_cleanup_strv_free_ char **units = NULL;
char ts[FORMAT_TIMESPAN_MAX];
struct unit_times *times;
int r;
const char *path, *id;
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
struct boot_times *boot;
assert(bus);
path = unit_dbus_path_from_name(name);
if (path == NULL)
return -EINVAL;
r = sd_bus_get_property(
bus,
"org.freedesktop.systemd1",
path,
"org.freedesktop.systemd1.Unit",
"Id",
&error,
&reply,
"s");
if (r < 0) {
log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
return r;
}
r = sd_bus_message_read(reply, "s", &id);
if (r < 0) {
log_error("Failed to parse reply.");
return r;
}
times = hashmap_get(unit_times_hashmap, id);
r = acquire_boot_times(bus, &boot);
if (r < 0)
return r;
if (times) {
if (times->time)
printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED_ON, id,
format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
else if (times->activated > boot->userspace_time)
printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
else
printf("%s\n", id);
}
return list_dependencies_one(bus, name, 0, &units, 0);
}
static int analyze_critical_chain(sd_bus *bus, char *names[]) {
struct unit_times *times;
int n, r;
unsigned int i;
Hashmap *h;
n = acquire_time_data(bus, &times);
if (n <= 0)
return n;
h = hashmap_new(string_hash_func, string_compare_func);
if (!h)
return -ENOMEM;
for (i = 0; i < (unsigned)n; i++) {
r = hashmap_put(h, times[i].name, &times[i]);
if (r < 0)
return r;
}
unit_times_hashmap = h;
pager_open_if_enabled();
puts("The time after the unit is active or started is printed after the \"@\" character.\n"
"The time the unit takes to start is printed after the \"+\" character.\n");
if (!strv_isempty(names)) {
char **name;
STRV_FOREACH(name, names)
list_dependencies(bus, *name);
} else
list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
hashmap_free(h);
free_unit_times(times, (unsigned) n);
return 0;
}
static int analyze_blame(sd_bus *bus) {
struct unit_times *times;
unsigned i;
int n;
n = acquire_time_data(bus, &times);
if (n <= 0)
return n;
qsort(times, n, sizeof(struct unit_times), compare_unit_time);
pager_open_if_enabled();
for (i = 0; i < (unsigned) n; i++) {
char ts[FORMAT_TIMESPAN_MAX];
if (times[i].time > 0)
printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
}
free_unit_times(times, (unsigned) n);
return 0;
}
static int analyze_time(sd_bus *bus) {
_cleanup_free_ char *buf = NULL;
int r;
r = pretty_boot_time(bus, &buf);
if (r < 0)
return r;
puts(buf);
return 0;
}
static int graph_one_property(sd_bus *bus, const struct unit_info *u, const char* prop, const char *color, char* patterns[]) {
_cleanup_strv_free_ char **units = NULL;
char **unit;
int r;
assert(u);
assert(prop);
assert(color);
r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
if (r < 0)
return -r;
STRV_FOREACH(unit, units) {
char **p;
bool match_found;
if (!strv_isempty(arg_dot_from_patterns)) {
match_found = false;
STRV_FOREACH(p, arg_dot_from_patterns)
if (fnmatch(*p, u->id, 0) == 0) {
match_found = true;
break;
}
if (!match_found)
continue;
}
if (!strv_isempty(arg_dot_to_patterns)) {
match_found = false;
STRV_FOREACH(p, arg_dot_to_patterns)
if (fnmatch(*p, *unit, 0) == 0) {
match_found = true;
break;
}
if (!match_found)
continue;
}
if (!strv_isempty(patterns)) {
match_found = false;
STRV_FOREACH(p, patterns)
if (fnmatch(*p, u->id, 0) == 0 || fnmatch(*p, *unit, 0) == 0) {
match_found = true;
break;
}
if (!match_found)
continue;
}
printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
}
return 0;
}
static int graph_one(sd_bus *bus, const struct unit_info *u, char *patterns[]) {
int r;
assert(bus);
assert(u);
if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
r = graph_one_property(bus, u, "After", "green", patterns);
if (r < 0)
return r;
}
if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
r = graph_one_property(bus, u, "Requires", "black", patterns);
if (r < 0)
return r;
r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns);
if (r < 0)
return r;
r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns);
if (r < 0)
return r;
r = graph_one_property(bus, u, "Wants", "grey66", patterns);
if (r < 0)
return r;
r = graph_one_property(bus, u, "Conflicts", "red", patterns);
if (r < 0)
return r;
r = graph_one_property(bus, u, "ConflictedBy", "red", patterns);
if (r < 0)
return r;
}
return 0;
}
static int dot(sd_bus *bus, char* patterns[]) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
struct unit_info u;
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"ListUnits",
&error,
&reply,
"");
if (r < 0) {
log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
return r;
}
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
if (r < 0)
return r;
printf("digraph systemd {\n");
while ((r = bus_parse_unit_info(reply, &u)) > 0) {
r = graph_one(bus, &u, patterns);
if (r < 0)
return r;
}
printf("}\n");
log_info(" Color legend: black = Requires\n"
" dark blue = Requisite\n"
" dark grey = Wants\n"
" red = Conflicts\n"
" green = After\n");
if (on_tty())
log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
"-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
return 0;
}
static int dump(sd_bus *bus, char **args) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
const char *text = NULL;
int r;
if (!strv_isempty(args)) {
log_error("Too many arguments.");
return -E2BIG;
}
pager_open_if_enabled();
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"Dump",
&error,
&reply,
"");
if (r < 0) {
log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
return r;
}
r = sd_bus_message_read(reply, "s", &text);
if (r < 0) {
log_error("Failed to parse reply");
return r;
}
fputs(text, stdout);
return 0;
}
static int set_log_level(sd_bus *bus, char **args) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
const char* value;
assert(bus);
assert(args);
if (strv_length(args) != 1) {
log_error("This command expects one argument only.");
return -E2BIG;
}
value = args[0];
r = sd_bus_set_property(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"LogLevel",
&error,
"s",
value);
if (r < 0) {
log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
return -EIO;
}
return 0;
}
static void analyze_help(void) {
pager_open_if_enabled();
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Process systemd profiling information\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --system Connect to system manager\n"
" --user Connect to user service manager\n"
" --order When generating a dependency graph, show only order\n"
" --require When generating a dependency graph, show only requirement\n"
" --from-pattern=GLOB, --to-pattern=GLOB\n"
" When generating a dependency graph, filter only origins\n"
" or destinations, respectively\n"
" --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
" services, which finished TIMESPAN earlier, than the\n"
" latest in the branch. The unit of TIMESPAN is seconds\n"
" unless specified with a different unit, i.e. 50ms\n"
" --no-pager Do not pipe output into a pager\n\n"
"Commands:\n"
" time Print time spent in the kernel before reaching userspace\n"
" blame Print list of running units ordered by time to init\n"
" critical-chain Print a tree of the time critical chain of units\n"
" plot Output SVG graphic showing service initialization\n"
" dot Output dependency graph in dot(1) format\n"
" set-log-level LEVEL Set logging threshold for systemd\n"
" dump Output state serialization of service manager\n",
program_invocation_short_name);
/* When updating this list, including descriptions, apply
* changes to shell-completion/bash/systemd and
* shell-completion/systemd-zsh-completion.zsh too. */
}
static int parse_argv(int argc, char *argv[]) {
int r;
enum {
ARG_VERSION = 0x100,
ARG_ORDER,
ARG_REQUIRE,
ARG_USER,
ARG_SYSTEM,
ARG_DOT_FROM_PATTERN,
ARG_DOT_TO_PATTERN,
ARG_FUZZ,
ARG_NO_PAGER
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "order", no_argument, NULL, ARG_ORDER },
{ "require", no_argument, NULL, ARG_REQUIRE },
{ "user", no_argument, NULL, ARG_USER },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
{ "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
{ "fuzz", required_argument, NULL, ARG_FUZZ },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ NULL, 0, NULL, 0 }
};
assert(argc >= 0);
assert(argv);
for (;;) {
switch (getopt_long(argc, argv, "h", options, NULL)) {
case 'h':
analyze_help();
return 0;
case ARG_VERSION:
puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES);
return 0;
case ARG_USER:
arg_scope = UNIT_FILE_USER;
break;
case ARG_SYSTEM:
arg_scope = UNIT_FILE_SYSTEM;
break;
case ARG_ORDER:
arg_dot = DEP_ORDER;
break;
case ARG_REQUIRE:
arg_dot = DEP_REQUIRE;
break;
case ARG_DOT_FROM_PATTERN:
if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
return log_oom();
break;
case ARG_DOT_TO_PATTERN:
if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
return log_oom();
break;
case ARG_FUZZ:
r = parse_sec(optarg, &arg_fuzz);
if (r < 0)
return r;
break;
case ARG_NO_PAGER:
arg_no_pager = true;
break;
case -1:
return 1;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
}
}
int main(int argc, char *argv[]) {
_cleanup_bus_unref_ sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
if (arg_scope == UNIT_FILE_SYSTEM)
r = sd_bus_open_system(&bus);
else
r = sd_bus_open_user(&bus);
if (r < 0) {
log_error("Failed to connect to bus: %s", strerror(-r));
goto finish;
}
if (!argv[optind] || streq(argv[optind], "time"))
r = analyze_time(bus);
else if (streq(argv[optind], "blame"))
r = analyze_blame(bus);
else if (streq(argv[optind], "critical-chain"))
r = analyze_critical_chain(bus, argv+optind+1);
else if (streq(argv[optind], "plot"))
r = analyze_plot(bus);
else if (streq(argv[optind], "dot"))
r = dot(bus, argv+optind+1);
else if (streq(argv[optind], "dump"))
r = dump(bus, argv+optind+1);
else if (streq(argv[optind], "set-log-level"))
r = set_log_level(bus, argv+optind+1);
else
log_error("Unknown operation '%s'.", argv[optind]);
finish:
pager_close();
strv_free(arg_dot_from_patterns);
strv_free(arg_dot_to_patterns);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}