+ fprintf(fp, " %llu (%llu-%llu)\n",
+ num, min, max);
+}
+
+void timers_dump(const struct timers *timers, FILE *fp)
+{
+ unsigned int l, i, off;
+ unsigned long long base;
+
+ if (!fp)
+ fp = stderr;
+
+ fprintf(fp, "Base: %llu\n", (unsigned long long)timers->base);
+
+ if (!timers->level[0])
+ goto past_levels;
+
+ fprintf(fp, "Level 0:\n");
+
+ /* First level is simple. */
+ off = timers->base % PER_LEVEL;
+ for (i = 0; i < PER_LEVEL; i++) {
+ const struct list_head *h;
+
+ fprintf(fp, " Bucket %llu (%lu):",
+ (i+off) % PER_LEVEL, timers->base + i);
+ h = &timers->level[0]->list[(i+off) % PER_LEVEL];
+ dump_bucket_stats(fp, h);
+ }
+
+ /* For other levels, "current" bucket has been emptied, and may contain
+ * entries for the current + level_size bucket. */
+ for (l = 1; l < ARRAY_SIZE(timers->level) && timers->level[l]; l++) {
+ uint64_t per_bucket = 1ULL << (TIMER_LEVEL_BITS * l);
+
+ off = ((timers->base >> (l*TIMER_LEVEL_BITS)) % PER_LEVEL);
+ /* We start at *next* bucket. */
+ base = (timers->base & ~(per_bucket - 1)) + per_bucket;
+
+ fprintf(fp, "Level %u:\n", l);
+ for (i = 1; i <= PER_LEVEL; i++) {
+ const struct list_head *h;
+
+ fprintf(fp, " Bucket %llu (%llu - %llu):",
+ (i+off) % PER_LEVEL,
+ base, base + per_bucket - 1);
+
+ h = &timers->level[l]->list[(i+off) % PER_LEVEL];
+ dump_bucket_stats(fp, h);
+ base += per_bucket;
+ }
+ }
+
+past_levels:
+ if (!list_empty(&timers->far)) {
+ fprintf(fp, "Far timers:");
+ dump_bucket_stats(fp, &timers->far);
+ }