diff options
Diffstat (limited to 'src/analyze/analyze.c')
-rw-r--r-- | src/analyze/analyze.c | 339 |
1 files changed, 220 insertions, 119 deletions
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index c0863e4167..3657ef50f1 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -88,6 +88,18 @@ struct boot_times { usec_t generators_finish_time; usec_t unitsload_start_time; usec_t unitsload_finish_time; + + /* + * If we're analyzing the user instance, all timestamps will be offset + * by its own start-up timestamp, which may be arbitrarily big. + * With "plot", this causes arbitrarily wide output SVG files which almost + * completely consist of empty space. Thus we cancel out this offset. + * + * This offset is subtracted from times above by acquire_boot_times(), + * but it still needs to be subtracted from unit-specific timestamps + * (so it is stored here for reference). + */ + usec_t reverse_offset; }; struct unit_times { @@ -188,95 +200,13 @@ static void free_unit_times(struct unit_times *t, unsigned n) { free(t); } -static int acquire_time_data(sd_bus *bus, struct unit_times **out) { - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - int r, c = 0; - struct unit_times *unit_times = NULL; - size_t size = 0; - UnitInfo u; - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnits", - &error, &reply, - NULL); - if (r < 0) { - log_error("Failed to list units: %s", bus_error_message(&error, -r)); - goto fail; - } - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); - if (r < 0) { - bus_log_parse_error(r); - goto fail; - } - - while ((r = bus_parse_unit_info(reply, &u)) > 0) { - struct unit_times *t; - - if (!GREEDY_REALLOC(unit_times, size, c+1)) { - r = log_oom(); - goto fail; - } - - t = unit_times+c; - t->name = NULL; - - assert_cc(sizeof(usec_t) == sizeof(uint64_t)); - - if (bus_get_uint64_property(bus, u.unit_path, - "org.freedesktop.systemd1.Unit", - "InactiveExitTimestampMonotonic", - &t->activating) < 0 || - bus_get_uint64_property(bus, u.unit_path, - "org.freedesktop.systemd1.Unit", - "ActiveEnterTimestampMonotonic", - &t->activated) < 0 || - bus_get_uint64_property(bus, u.unit_path, - "org.freedesktop.systemd1.Unit", - "ActiveExitTimestampMonotonic", - &t->deactivating) < 0 || - bus_get_uint64_property(bus, u.unit_path, - "org.freedesktop.systemd1.Unit", - "InactiveEnterTimestampMonotonic", - &t->deactivated) < 0) { - r = -EIO; - goto fail; - } - - if (t->activated >= t->activating) - t->time = t->activated - t->activating; - else if (t->deactivated >= t->activating) - t->time = t->deactivated - t->activating; - else - t->time = 0; +static void subtract_timestamp(usec_t *a, usec_t b) { + assert(a); - if (t->activating == 0) - continue; - - t->name = strdup(u.id); - if (t->name == NULL) { - r = log_oom(); - goto fail; - } - c++; - } - if (r < 0) { - bus_log_parse_error(r); - goto fail; + if (*a > 0) { + assert(*a >= b); + *a -= b; } - - *out = unit_times; - return c; - -fail: - if (unit_times) - free_unit_times(unit_times, (unsigned) c); - return r; } static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) { @@ -355,10 +285,30 @@ static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) { return -EINPROGRESS; } - if (times.initrd_time) - times.kernel_done_time = times.initrd_time; - else - times.kernel_done_time = times.userspace_time; + if (arg_user) { + /* + * User-instance-specific timestamps processing + * (see comment to reverse_offset in struct boot_times). + */ + times.reverse_offset = times.userspace_time; + + times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0; + subtract_timestamp(×.finish_time, times.reverse_offset); + + subtract_timestamp(×.security_start_time, times.reverse_offset); + subtract_timestamp(×.security_finish_time, times.reverse_offset); + + subtract_timestamp(×.generators_start_time, times.reverse_offset); + subtract_timestamp(×.generators_finish_time, times.reverse_offset); + + subtract_timestamp(×.unitsload_start_time, times.reverse_offset); + subtract_timestamp(×.unitsload_finish_time, times.reverse_offset); + } else { + if (times.initrd_time) + times.kernel_done_time = times.initrd_time; + else + times.kernel_done_time = times.userspace_time; + } cached = true; @@ -378,6 +328,107 @@ static void free_host_info(struct host_info *hi) { free(hi); } +static int acquire_time_data(sd_bus *bus, struct unit_times **out) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + int r, c = 0; + struct boot_times *boot_times = NULL; + struct unit_times *unit_times = NULL; + size_t size = 0; + UnitInfo u; + + r = acquire_boot_times(bus, &boot_times); + if (r < 0) + goto fail; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnits", + &error, &reply, + NULL); + if (r < 0) { + log_error("Failed to list units: %s", bus_error_message(&error, -r)); + goto fail; + } + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); + if (r < 0) { + bus_log_parse_error(r); + goto fail; + } + + while ((r = bus_parse_unit_info(reply, &u)) > 0) { + struct unit_times *t; + + if (!GREEDY_REALLOC(unit_times, size, c+1)) { + r = log_oom(); + goto fail; + } + + t = unit_times+c; + t->name = NULL; + + assert_cc(sizeof(usec_t) == sizeof(uint64_t)); + + if (bus_get_uint64_property(bus, u.unit_path, + "org.freedesktop.systemd1.Unit", + "InactiveExitTimestampMonotonic", + &t->activating) < 0 || + bus_get_uint64_property(bus, u.unit_path, + "org.freedesktop.systemd1.Unit", + "ActiveEnterTimestampMonotonic", + &t->activated) < 0 || + bus_get_uint64_property(bus, u.unit_path, + "org.freedesktop.systemd1.Unit", + "ActiveExitTimestampMonotonic", + &t->deactivating) < 0 || + bus_get_uint64_property(bus, u.unit_path, + "org.freedesktop.systemd1.Unit", + "InactiveEnterTimestampMonotonic", + &t->deactivated) < 0) { + r = -EIO; + goto fail; + } + + subtract_timestamp(&t->activating, boot_times->reverse_offset); + subtract_timestamp(&t->activated, boot_times->reverse_offset); + subtract_timestamp(&t->deactivating, boot_times->reverse_offset); + subtract_timestamp(&t->deactivated, boot_times->reverse_offset); + + if (t->activated >= t->activating) + t->time = t->activated - t->activating; + else if (t->deactivated >= t->activating) + t->time = t->deactivated - t->activating; + else + t->time = 0; + + if (t->activating == 0) + continue; + + t->name = strdup(u.id); + if (t->name == NULL) { + r = log_oom(); + goto fail; + } + c++; + } + if (r < 0) { + bus_log_parse_error(r); + goto fail; + } + + *out = unit_times; + return c; + +fail: + if (unit_times) + free_unit_times(unit_times, (unsigned) c); + return r; +} + static int acquire_host_info(sd_bus *bus, struct host_info **hi) { int r; struct host_info *host; @@ -450,10 +501,7 @@ static int pretty_boot_time(sd_bus *bus, char **_buf) { 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)); + strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC)); ptr = strdup(buf); if (!ptr) @@ -534,8 +582,7 @@ static int analyze_plot(sd_bus *bus) { if (u->activating < boot->userspace_time || u->activating > boot->finish_time) { - free(u->name); - u->name = NULL; + u->name = mfree(u->name); continue; } @@ -712,9 +759,9 @@ static int list_dependencies_print(const char *name, unsigned int level, unsigne if (times) { if (times->time) - printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED_ON, name, + printf("%s%s @%s +%s%s", ANSI_HIGHLIGHT_RED, 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); + format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ANSI_NORMAL); 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 @@ -797,11 +844,8 @@ static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int lev 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) { + if (times && times->activated && times->activated <= boot->finish_time && (service_longest - times->activated) <= arg_fuzz) to_print++; - } } if (!to_print) @@ -883,8 +927,8 @@ static int list_dependencies(sd_bus *bus, const char *name) { 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); + printf("%s%s +%s%s\n", ANSI_HIGHLIGHT_RED, id, + format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_NORMAL); 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 @@ -968,7 +1012,7 @@ static int analyze_time(sd_bus *bus) { return 0; } -static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[]) { +static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[], char* from_patterns[], char* to_patterns[]) { _cleanup_strv_free_ char **units = NULL; char **unit; int r; @@ -980,9 +1024,9 @@ static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, match_patterns = strv_fnmatch(patterns, u->id, 0); - if (!strv_isempty(arg_dot_from_patterns) && + if (!strv_isempty(from_patterns) && !match_patterns && - !strv_fnmatch(arg_dot_from_patterns, u->id, 0)) + !strv_fnmatch(from_patterns, u->id, 0)) return 0; r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units); @@ -994,9 +1038,9 @@ static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, match_patterns2 = strv_fnmatch(patterns, *unit, 0); - if (!strv_isempty(arg_dot_to_patterns) && + if (!strv_isempty(to_patterns) && !match_patterns2 && - !strv_fnmatch(arg_dot_to_patterns, *unit, 0)) + !strv_fnmatch(to_patterns, *unit, 0)) continue; if (!strv_isempty(patterns) && !match_patterns && !match_patterns2) @@ -1008,35 +1052,35 @@ static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, return 0; } -static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[]) { +static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_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); + r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns); if (r < 0) return r; } if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) { - r = graph_one_property(bus, u, "Requires", "black", patterns); + r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns); if (r < 0) return r; - r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns); + r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns, from_patterns, to_patterns); if (r < 0) return r; - r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns); + r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns, from_patterns, to_patterns); if (r < 0) return r; - r = graph_one_property(bus, u, "Wants", "grey66", patterns); + r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns); if (r < 0) return r; - r = graph_one_property(bus, u, "Conflicts", "red", patterns); + r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns); if (r < 0) return r; - r = graph_one_property(bus, u, "ConflictedBy", "red", patterns); + r = graph_one_property(bus, u, "ConflictedBy", "red", patterns, from_patterns, to_patterns); if (r < 0) return r; } @@ -1044,12 +1088,69 @@ static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[]) { return 0; } +static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { + _cleanup_strv_free_ char **expanded_patterns = NULL; + char **pattern; + int r; + + STRV_FOREACH(pattern, patterns) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *unit = NULL, *unit_id = NULL; + + if (strv_extend(&expanded_patterns, *pattern) < 0) + return log_oom(); + + if (string_is_glob(*pattern)) + continue; + + unit = unit_dbus_path_from_name(*pattern); + if (!unit) + return log_oom(); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + unit, + "org.freedesktop.systemd1.Unit", + "Id", + &error, + &unit_id); + if (r < 0) + return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); + + if (!streq(*pattern, unit_id)) { + if (strv_extend(&expanded_patterns, unit_id) < 0) + return log_oom(); + } + } + + *ret = expanded_patterns; + expanded_patterns = NULL; /* do not free */ + + 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; + _cleanup_strv_free_ char **expanded_patterns = NULL; + _cleanup_strv_free_ char **expanded_from_patterns = NULL; + _cleanup_strv_free_ char **expanded_to_patterns = NULL; int r; UnitInfo u; + r = expand_patterns(bus, patterns, &expanded_patterns); + if (r < 0) + return r; + + r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns); + if (r < 0) + return r; + + r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns); + if (r < 0) + return r; + r = sd_bus_call_method( bus, "org.freedesktop.systemd1", @@ -1072,7 +1173,7 @@ static int dot(sd_bus *bus, char* patterns[]) { while ((r = bus_parse_unit_info(reply, &u)) > 0) { - r = graph_one(bus, &u, patterns); + r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns); if (r < 0) return r; } |