--- /dev/null
+/* 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;
+}
#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"
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;
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;