/* 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 */
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("<g>\n");
+ svg("<title>");
+ plot_tooltip(u);
+ svg("</title>\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);
u->name, FORMAT_TIMESPAN(u->time, USEC_PER_MSEC));
else
svg_text(b, u->activating, y, "%s", u->name);
+ svg("</g>\n");
return 1;
}
double text_start, text_width;
if (u->activating > boot->finish_time) {
- u->name = mfree(u->name);
+ unit_times_clear(u);
continue;
}
/* 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);
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;
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));
return log_oom();
t->has_data = true;
+ /* Prevent destructor from running on t reference. */
+ TAKE_PTR(t);
c++;
}
if (r < 0)