From: Jade Lovelace Date: Sun, 1 Oct 2023 05:21:33 +0000 (-0700) Subject: analyze: add tooltips with dependency information to "plot" X-Git-Tag: v255-rc1~333^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ebaf55499ff975b8c2507bd68b17ea22ba2f2cee;p=thirdparty%2Fsystemd.git analyze: add tooltips with dependency information to "plot" This helps a lot with figuring out why units were started when they were, rather than guessing there is a dependency relation. We could perhaps also do fun JavaScript things in the future to highlight dependencies on mouse-over. --- diff --git a/src/analyze/analyze-plot.c b/src/analyze/analyze-plot.c index 0fbe17465e0..81fc25b31e0 100644 --- a/src/analyze/analyze-plot.c +++ b/src/analyze/analyze-plot.c @@ -1,13 +1,15 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "analyze.h" #include "analyze-plot.h" #include "analyze-time-data.h" +#include "analyze.h" #include "bus-error.h" #include "bus-map-properties.h" #include "format-table.h" #include "os-util.h" #include "sort-util.h" +#include "strv.h" +#include "unit-def.h" #include "version.h" #define SCALE_X (0.1 / 1000.0) /* pixels per us */ @@ -158,12 +160,32 @@ static void svg_graph_box(double height, double begin, double end) { SCALE_Y * height); } } + +static void plot_tooltip(const UnitTimes *ut) { + assert(ut); + assert(ut->name); + + svg("%s:\n", ut->name); + + UnitDependency i; + VA_ARGS_FOREACH(i, UNIT_AFTER, UNIT_BEFORE, UNIT_REQUIRES, UNIT_REQUISITE, UNIT_WANTS, UNIT_CONFLICTS, UNIT_UPHOLDS) + if (!strv_isempty(ut->deps[i])) { + svg("\n%s:\n", unit_dependency_to_string(i)); + STRV_FOREACH(s, ut->deps[i]) + svg(" %s\n", *s); + } +} + static int plot_unit_times(UnitTimes *u, double width, int y) { bool b; if (!u->name) return 0; + svg("\n"); + svg(""); + plot_tooltip(u); + svg("\n"); svg_bar("activating", u->activating, u->activated, y); svg_bar("active", u->activated, u->deactivating, y); svg_bar("deactivating", u->deactivating, u->deactivated, y); @@ -175,6 +197,7 @@ static int plot_unit_times(UnitTimes *u, double width, int y) { u->name, FORMAT_TIMESPAN(u->time, USEC_PER_MSEC)); else svg_text(b, u->activating, y, "%s", u->name); + svg("\n"); return 1; } @@ -220,7 +243,7 @@ static int produce_plot_as_svg( double text_start, text_width; if (u->activating > boot->finish_time) { - u->name = mfree(u->name); + unit_times_clear(u); continue; } diff --git a/src/analyze/analyze-time-data.c b/src/analyze/analyze-time-data.c index 70717fc4b27..741cab33b67 100644 --- a/src/analyze/analyze-time-data.c +++ b/src/analyze/analyze-time-data.c @@ -1,12 +1,14 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "analyze.h" #include "analyze-time-data.h" +#include "analyze.h" #include "bus-error.h" #include "bus-locator.h" #include "bus-map-properties.h" #include "bus-unit-util.h" +#include "memory-util.h" #include "special.h" +#include "strv.h" static void subtract_timestamp(usec_t *a, usec_t b) { assert(a); @@ -215,22 +217,41 @@ int pretty_boot_time(sd_bus *bus, char **ret) { return 0; } +void unit_times_clear(UnitTimes *t) { + if (!t) + return; + + FOREACH_ARRAY(d, t->deps, ELEMENTSOF(t->deps)) + *d = strv_free(*d); + + t->name = mfree(t->name); +} + UnitTimes* unit_times_free_array(UnitTimes *t) { if (!t) return NULL; for (UnitTimes *p = t; p->has_data; p++) - free(p->name); + unit_times_clear(p); return mfree(t); } +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(UnitTimes*, unit_times_clear, NULL); + int acquire_time_data(sd_bus *bus, bool require_finished, UnitTimes **out) { static const struct bus_properties_map property_map[] = { - { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(UnitTimes, activating) }, - { "ActiveEnterTimestampMonotonic", "t", NULL, offsetof(UnitTimes, activated) }, - { "ActiveExitTimestampMonotonic", "t", NULL, offsetof(UnitTimes, deactivating) }, - { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(UnitTimes, deactivated) }, + { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(UnitTimes, activating) }, + { "ActiveEnterTimestampMonotonic", "t", NULL, offsetof(UnitTimes, activated) }, + { "ActiveExitTimestampMonotonic", "t", NULL, offsetof(UnitTimes, deactivating) }, + { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(UnitTimes, deactivated) }, + { "After", "as", NULL, offsetof(UnitTimes, deps[UNIT_AFTER]) }, + { "Before", "as", NULL, offsetof(UnitTimes, deps[UNIT_BEFORE]) }, + { "Requires", "as", NULL, offsetof(UnitTimes, deps[UNIT_REQUIRES]) }, + { "Requisite", "as", NULL, offsetof(UnitTimes, deps[UNIT_REQUISITE]) }, + { "Wants", "as", NULL, offsetof(UnitTimes, deps[UNIT_WANTS]) }, + { "Conflicts", "as", NULL, offsetof(UnitTimes, deps[UNIT_CONFLICTS]) }, + { "Upholds", "as", NULL, offsetof(UnitTimes, deps[UNIT_UPHOLDS]) }, {}, }; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; @@ -254,14 +275,14 @@ int acquire_time_data(sd_bus *bus, bool require_finished, UnitTimes **out) { return bus_log_parse_error(r); while ((r = bus_parse_unit_info(reply, &u)) > 0) { - UnitTimes *t; + _cleanup_(unit_times_clearp) UnitTimes *t = NULL; - if (!GREEDY_REALLOC(unit_times, c + 2)) + if (!GREEDY_REALLOC0(unit_times, c + 2)) return log_oom(); - unit_times[c + 1].has_data = false; + /* t initially has pointers zeroed by the allocation, and unit_times_clearp will have zeroed + * them if the entry is being reused. */ t = &unit_times[c]; - t->name = NULL; assert_cc(sizeof(usec_t) == sizeof(uint64_t)); @@ -298,6 +319,8 @@ int acquire_time_data(sd_bus *bus, bool require_finished, UnitTimes **out) { return log_oom(); t->has_data = true; + /* Prevent destructor from running on t reference. */ + TAKE_PTR(t); c++; } if (r < 0) diff --git a/src/analyze/analyze-time-data.h b/src/analyze/analyze-time-data.h index 79240745cb3..9049d876b25 100644 --- a/src/analyze/analyze-time-data.h +++ b/src/analyze/analyze-time-data.h @@ -4,6 +4,7 @@ #include #include "time-util.h" +#include "unit-def.h" typedef struct BootTimes { usec_t firmware_time; @@ -45,11 +46,13 @@ typedef struct UnitTimes { usec_t deactivated; usec_t deactivating; usec_t time; + char **deps[_UNIT_DEPENDENCY_MAX]; } UnitTimes; int acquire_boot_times(sd_bus *bus, bool require_finished, BootTimes **ret); int pretty_boot_time(sd_bus *bus, char **ret); +void unit_times_clear(UnitTimes *t); UnitTimes* unit_times_free_array(UnitTimes *t); DEFINE_TRIVIAL_CLEANUP_FUNC(UnitTimes*, unit_times_free_array);