From: Lennart Poettering Date: Mon, 21 Feb 2022 12:56:59 +0000 (+0100) Subject: analyze: split out critical chain X-Git-Tag: v251-rc1~249^2~7 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ef215fa7523e00d424ee17f7092c1d7702868e53;p=thirdparty%2Fsystemd.git analyze: split out critical chain --- diff --git a/src/analyze/analyze-critical-chain.c b/src/analyze/analyze-critical-chain.c new file mode 100644 index 00000000000..3e2c5e7c98c --- /dev/null +++ b/src/analyze/analyze-critical-chain.c @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-critical-chain.h" +#include "analyze-time-data.h" +#include "strv.h" +#include "copy.h" +#include "path-util.h" +#include "terminal-util.h" +#include "sort-util.h" +#include "special.h" +#include "bus-error.h" + +static int list_dependencies_print( + const char *name, + unsigned level, + unsigned branches, + bool last, + UnitTimes *times, + BootTimes *boot) { + + for (unsigned i = level; i != 0; i--) + printf("%s", special_glyph(branches & (1 << (i-1)) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE)); + + printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH)); + + if (times) { + if (times->time > 0) + printf("%s%s @%s +%s%s", ansi_highlight_red(), name, + FORMAT_TIMESPAN(times->activating - boot->userspace_time, USEC_PER_MSEC), + FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal()); + else if (times->activated > boot->userspace_time) + printf("%s @%s", name, FORMAT_TIMESPAN(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 = NULL; + + assert(bus); + assert(name); + assert(deps); + + path = unit_dbus_path_from_name(name); + if (!path) + return -ENOMEM; + + return bus_get_unit_property_strv(bus, path, "After", deps); +} + +static Hashmap *unit_times_hashmap; + +static int list_dependencies_compare(char *const *a, char *const *b) { + usec_t usa = 0, usb = 0; + UnitTimes *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 CMP(usb, usa); +} + +static bool times_in_range(const UnitTimes *times, const BootTimes *boot) { + return times && times->activated > 0 && times->activated <= boot->finish_time; +} + +static int list_dependencies_one(sd_bus *bus, const char *name, unsigned level, char ***units, unsigned branches) { + _cleanup_strv_free_ char **deps = NULL; + char **c; + int r; + usec_t service_longest = 0; + int to_print = 0; + UnitTimes *times; + BootTimes *boot; + + if (strv_extend(units, name)) + return log_oom(); + + r = list_dependencies_get_dependencies(bus, name, &deps); + if (r < 0) + return r; + + typesafe_qsort(deps, strv_length(deps), 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); /* lgtm [cpp/inconsistent-null-check] */ + if (times_in_range(times, boot) && times->activated >= service_longest) + service_longest = times->activated; + } + + if (service_longest == 0) + return r; + + STRV_FOREACH(c, deps) { + times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */ + if (times_in_range(times, boot) && service_longest - times->activated <= arg_fuzz) + to_print++; + } + + if (!to_print) + return r; + + STRV_FOREACH(c, deps) { + times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */ + if (!times_in_range(times, boot) || 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 == 0) + break; + } + return 0; +} + +static int list_dependencies(sd_bus *bus, const char *name) { + _cleanup_strv_free_ char **units = NULL; + UnitTimes *times; + int r; + const char *id; + _cleanup_free_ char *path = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + BootTimes *boot; + + assert(bus); + + path = unit_dbus_path_from_name(name); + if (!path) + return -ENOMEM; + + r = sd_bus_get_property( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "Id", + &error, + &reply, + "s"); + if (r < 0) + return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); + + r = sd_bus_message_read(reply, "s", &id); + if (r < 0) + return bus_log_parse_error(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(), id, + FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal()); + else if (times->activated > boot->userspace_time) + printf("%s @%s\n", id, + FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC)); + else + printf("%s\n", id); + } + + return list_dependencies_one(bus, name, 0, &units, 0); +} + +int analyze_critical_chain(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL; + Hashmap *h; + int n, r; + + r = acquire_bus(&bus, NULL); + if (r < 0) + return bus_log_connect_error(r, arg_transport); + + n = acquire_time_data(bus, ×); + if (n <= 0) + return n; + + h = hashmap_new(&string_hash_ops); + if (!h) + return log_oom(); + + for (UnitTimes *u = times; u->has_data; u++) { + r = hashmap_put(h, u->name, u); + if (r < 0) + return log_error_errno(r, "Failed to add entry to hashmap: %m"); + } + unit_times_hashmap = h; + + pager_open(arg_pager_flags); + + puts("The time when unit became active or started is printed after the \"@\" character.\n" + "The time the unit took to start is printed after the \"+\" character.\n"); + + if (argc > 1) { + char **name; + STRV_FOREACH(name, strv_skip(argv, 1)) + list_dependencies(bus, *name); + } else + list_dependencies(bus, SPECIAL_DEFAULT_TARGET); + + h = hashmap_free(h); + return 0; +} diff --git a/src/analyze/analyze-critical-chain.h b/src/analyze/analyze-critical-chain.h new file mode 100644 index 00000000000..a5a8e2d76f1 --- /dev/null +++ b/src/analyze/analyze-critical-chain.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int analyze_critical_chain(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 5d6ae15038e..465f7c7c092 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -18,6 +18,7 @@ #include "analyze-capability.h" #include "analyze-cat-config.h" #include "analyze-condition.h" +#include "analyze-critical-chain.h" #include "analyze-dot.h" #include "analyze-dump.h" #include "analyze-elf.h" @@ -83,7 +84,7 @@ DotMode arg_dot = DEP_ALL; char **arg_dot_from_patterns = NULL, **arg_dot_to_patterns = NULL; -static usec_t arg_fuzz = 0; +usec_t arg_fuzz = 0; PagerFlags arg_pager_flags = 0; BusTransport arg_transport = BUS_TRANSPORT_LOCAL; const char *arg_host = NULL; @@ -196,231 +197,6 @@ static int process_aliases(char *argv[], char *tempdir, char ***ret) { return 0; } -static int list_dependencies_print( - const char *name, - unsigned level, - unsigned branches, - bool last, - UnitTimes *times, - BootTimes *boot) { - - for (unsigned i = level; i != 0; i--) - printf("%s", special_glyph(branches & (1 << (i-1)) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE)); - - printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH)); - - if (times) { - if (times->time > 0) - printf("%s%s @%s +%s%s", ansi_highlight_red(), name, - FORMAT_TIMESPAN(times->activating - boot->userspace_time, USEC_PER_MSEC), - FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal()); - else if (times->activated > boot->userspace_time) - printf("%s @%s", name, FORMAT_TIMESPAN(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 = NULL; - - assert(bus); - assert(name); - assert(deps); - - path = unit_dbus_path_from_name(name); - if (!path) - return -ENOMEM; - - return bus_get_unit_property_strv(bus, path, "After", deps); -} - -static Hashmap *unit_times_hashmap; - -static int list_dependencies_compare(char *const *a, char *const *b) { - usec_t usa = 0, usb = 0; - UnitTimes *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 CMP(usb, usa); -} - -static bool times_in_range(const UnitTimes *times, const BootTimes *boot) { - return times && times->activated > 0 && times->activated <= boot->finish_time; -} - -static int list_dependencies_one(sd_bus *bus, const char *name, unsigned level, char ***units, unsigned branches) { - _cleanup_strv_free_ char **deps = NULL; - char **c; - int r; - usec_t service_longest = 0; - int to_print = 0; - UnitTimes *times; - BootTimes *boot; - - if (strv_extend(units, name)) - return log_oom(); - - r = list_dependencies_get_dependencies(bus, name, &deps); - if (r < 0) - return r; - - typesafe_qsort(deps, strv_length(deps), 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); /* lgtm [cpp/inconsistent-null-check] */ - if (times_in_range(times, boot) && times->activated >= service_longest) - service_longest = times->activated; - } - - if (service_longest == 0) - return r; - - STRV_FOREACH(c, deps) { - times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */ - if (times_in_range(times, boot) && service_longest - times->activated <= arg_fuzz) - to_print++; - } - - if (!to_print) - return r; - - STRV_FOREACH(c, deps) { - times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */ - if (!times_in_range(times, boot) || 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 == 0) - break; - } - return 0; -} - -static int list_dependencies(sd_bus *bus, const char *name) { - _cleanup_strv_free_ char **units = NULL; - UnitTimes *times; - int r; - const char *id; - _cleanup_free_ char *path = NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - BootTimes *boot; - - assert(bus); - - path = unit_dbus_path_from_name(name); - if (!path) - return -ENOMEM; - - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "Id", - &error, - &reply, - "s"); - if (r < 0) - return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); - - r = sd_bus_message_read(reply, "s", &id); - if (r < 0) - return bus_log_parse_error(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(), id, - FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal()); - else if (times->activated > boot->userspace_time) - printf("%s @%s\n", id, - FORMAT_TIMESPAN(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(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL; - Hashmap *h; - int n, r; - - r = acquire_bus(&bus, NULL); - if (r < 0) - return bus_log_connect_error(r, arg_transport); - - n = acquire_time_data(bus, ×); - if (n <= 0) - return n; - - h = hashmap_new(&string_hash_ops); - if (!h) - return log_oom(); - - for (UnitTimes *u = times; u->has_data; u++) { - r = hashmap_put(h, u->name, u); - if (r < 0) - return log_error_errno(r, "Failed to add entry to hashmap: %m"); - } - unit_times_hashmap = h; - - pager_open(arg_pager_flags); - - puts("The time when unit became active or started is printed after the \"@\" character.\n" - "The time the unit took to start is printed after the \"+\" character.\n"); - - if (argc > 1) { - char **name; - STRV_FOREACH(name, strv_skip(argv, 1)) - list_dependencies(bus, *name); - } else - list_dependencies(bus, SPECIAL_DEFAULT_TARGET); - - h = hashmap_free(h); - return 0; -} - static int analyze_time(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ char *buf = NULL; diff --git a/src/analyze/analyze.h b/src/analyze/analyze.h index 7d4b0d408f7..57740acc8c3 100644 --- a/src/analyze/analyze.h +++ b/src/analyze/analyze.h @@ -16,6 +16,7 @@ typedef enum DotMode { extern DotMode arg_dot; extern char **arg_dot_from_patterns, **arg_dot_to_patterns; +extern usec_t arg_fuzz; extern PagerFlags arg_pager_flags; extern BusTransport arg_transport; extern const char *arg_host; diff --git a/src/analyze/meson.build b/src/analyze/meson.build index 383437eb277..71a4f2da4c8 100644 --- a/src/analyze/meson.build +++ b/src/analyze/meson.build @@ -11,6 +11,8 @@ systemd_analyze_sources = files(''' analyze-cat-config.h analyze-condition.c analyze-condition.h + analyze-critical-chain.c + analyze-critical-chain.h analyze-dot.c analyze-dot.h analyze-dump.c