+
+ times = hashmap_get(unit_times_hashmap, *a);
+ if (times)
+ usa = times->aet;
+ times = hashmap_get(unit_times_hashmap, *b);
+ if (times)
+ usb = times->aet;
+
+ return usb - usa;
+}
+
+static int list_dependencies_one(DBusConnection *bus, const char *name, unsigned int level, char ***units,
+ unsigned int branches) {
+ _cleanup_strv_free_ char **deps = NULL;
+ char **c;
+ int r = 0;
+ usec_t service_longest = 0;
+ int to_print = 0;
+ struct unit_times *times;
+ struct boot_times *boot;
+
+ if(strv_extend(units, name))
+ return log_oom();
+
+ r = list_dependencies_get_dependencies(bus, name, &deps);
+ if (r < 0)
+ return r;
+
+ qsort_safe(deps, strv_length(deps), sizeof (char*), 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);
+ if (times
+ && times->aet
+ && times->aet <= boot->finish_time
+ && (times->aet >= service_longest
+ || service_longest == 0)) {
+ service_longest = times->aet;
+ break;
+ }
+ }
+
+ if (service_longest == 0 )
+ return r;
+
+ STRV_FOREACH(c, deps) {
+ times = hashmap_get(unit_times_hashmap, *c);
+ if (times && times->aet
+ && times->aet <= boot->finish_time
+ && (service_longest - times->aet) <= arg_fuzz) {
+ to_print++;
+ }
+ }
+
+ if(!to_print)
+ return r;
+
+ STRV_FOREACH(c, deps) {
+ times = hashmap_get(unit_times_hashmap, *c);
+ if (!times
+ || !times->aet
+ || times->aet > boot->finish_time
+ || service_longest - times->aet > 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)
+ break;
+ }
+ return 0;
+}
+
+static int list_dependencies(DBusConnection *bus, const char *name) {
+ _cleanup_strv_free_ char **units = NULL;
+ char ts[FORMAT_TIMESPAN_MAX];
+ struct unit_times *times;
+ int r;
+ const char
+ *path, *id,
+ *interface = "org.freedesktop.systemd1.Unit",
+ *property = "Id";
+ DBusMessageIter iter, sub;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ struct boot_times *boot;
+
+ assert(bus);
+
+ path = unit_dbus_path_from_name(name);
+ if (path == NULL)
+ return -EINVAL;
+
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &property,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return r;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_get_basic(&sub, &id);
+
+ 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_ON, id,
+ format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ANSI_HIGHLIGHT_OFF);
+ else if (times->aet > boot->userspace_time)
+ printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->aet - 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(DBusConnection *bus, char *names[]) {
+ struct unit_times *times;
+ int n, r;
+ unsigned int i;
+ Hashmap *h;
+
+ n = acquire_time_data(bus, ×);
+ if (n <= 0)
+ return n;
+
+ h = hashmap_new(string_hash_func, string_compare_func);
+ if (!h)
+ return -ENOMEM;
+
+ for (i = 0; i < (unsigned)n; i++) {
+ r = hashmap_put(h, times[i].name, ×[i]);
+ if (r < 0)
+ return r;
+ }
+ unit_times_hashmap = h;
+
+ pager_open_if_enabled();
+
+ puts("The time after the unit is active or started is printed after the \"@\" character.\n"
+ "The time the unit takes to start is printed after the \"+\" character.\n");
+
+ if (!strv_isempty(names)) {
+ char **name;
+ STRV_FOREACH(name, names)
+ list_dependencies(bus, *name);
+ } else
+ list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
+
+ hashmap_free(h);
+ free_unit_times(times, (unsigned) n);
+ return 0;
+}
+
+static int analyze_blame(DBusConnection *bus) {
+ struct unit_times *times;
+ unsigned i;
+ int n;
+
+ n = acquire_time_data(bus, ×);
+ if (n <= 0)