diff --git a/src/asahi/lib/agx_bo.c b/src/asahi/lib/agx_bo.c index 1935d646f04..f23a7c90470 100644 --- a/src/asahi/lib/agx_bo.c +++ b/src/asahi/lib/agx_bo.c @@ -6,6 +6,9 @@ #include "agx_bo.h" #include +#include +#include "util/hash_table.h" +#include "util/ralloc.h" #include "agx_device.h" #include "decode.h" @@ -168,6 +171,152 @@ agx_bo_reference(struct agx_bo *bo) } } +struct label_stat { + const char *label; + uint32_t count; + size_t alloc_B; + size_t mapped_B; +}; + +static void +account_bo(struct label_stat *stat, struct agx_bo *bo) +{ + stat->count++; + stat->alloc_B += bo->size; + + if (bo->_map != NULL) + stat->mapped_B += bo->size; +} + +static void +print_size(FILE *fp, size_t size_B) +{ + if (size_B >= (1024 * 1024 * 1024)) { + fprintf(fp, "%.1f GiB", (double)size_B / (double)(1024 * 1024 * 1024)); + } else if (size_B >= (1024 * 1024)) { + fprintf(fp, "%.1f MiB", (double)size_B / (double)(1024 * 1024)); + } else if (size_B >= 1024) { + fprintf(fp, "%zu KiB", DIV_ROUND_UP(size_B, 1024)); + } else { + fprintf(fp, "%zu B", size_B); + } +} + +static void +print_stat(FILE *fp, struct label_stat *stat) +{ + const char *BOLD = "\e[1m"; + const char *RESET = "\e[0m"; + + fprintf(fp, "%s%s%s: ", BOLD, stat->label, RESET); + print_size(fp, stat->alloc_B); + + if (stat->mapped_B) { + fprintf(fp, ", mapped "); + print_size(fp, stat->mapped_B); + } + + fprintf(fp, ", %u BOs\n", stat->count); +} + +static int +compare_size(const void *a_, const void *b_) +{ + const struct label_stat *const *label_a = a_; + const struct label_stat *const *label_b = b_; + + size_t a = (*label_a)->alloc_B; + size_t b = (*label_b)->alloc_B; + + return (a > b) ? 1 : (a < b) ? -1 : 0; +} + +static void +agx_bo_dump_all(struct agx_device *dev) +{ + struct label_stat accum = {.label = "Total"}; + struct hash_table *totals = _mesa_string_hash_table_create(NULL); + bool verbose = dev->debug & AGX_DBG_BODUMPVERBOSE; + + if (verbose) + fprintf(stderr, "---\n"); + + for (uint32_t handle = 0; handle < dev->max_handle; handle++) { + struct agx_bo *bo = agx_lookup_bo(dev, handle); + if (!bo->size) + continue; + + if (verbose) { + fprintf(stderr, "%u: %s %zu KiB\n", handle, bo->label, + bo->size / 1024); + } + + account_bo(&accum, bo); + + assert(bo->label != NULL && "Everything must be labeled"); + + struct hash_entry *ent = _mesa_hash_table_search(totals, bo->label); + struct label_stat *ls; + if (ent != NULL) { + ls = ent->data; + } else { + ls = rzalloc(totals, struct label_stat); + ls->label = bo->label; + _mesa_hash_table_insert(totals, bo->label, ls); + } + + account_bo(ls, bo); + } + + if (verbose) { + fprintf(stderr, "\n"); + } + + unsigned nr_labels = _mesa_hash_table_num_entries(totals); + struct label_stat **stats = + rzalloc_array(totals, struct label_stat *, nr_labels); + unsigned i = 0; + + hash_table_foreach(totals, ent) { + assert(i < nr_labels); + stats[i++] = ent->data; + } + + assert(i == nr_labels); + + /* Sort labels in ascending order of size */ + qsort(stats, nr_labels, sizeof(struct label_stat *), compare_size); + + for (i = 0; i < nr_labels; ++i) { + print_stat(stderr, stats[i]); + } + + print_stat(stderr, &accum); + + if (verbose) + fprintf(stderr, "---\n\n"); + else + fprintf(stderr, "\n"); + + ralloc_free(totals); +} + +static void +agx_bo_dump_all_periodic(struct agx_device *dev) +{ + if (likely(!(dev->debug & (AGX_DBG_BODUMP | AGX_DBG_BODUMPVERBOSE)))) + return; + + static time_t agx_last_dumped_time = 0; + + time_t now = time(NULL); + if (now == agx_last_dumped_time) + return; + + agx_bo_dump_all(dev); + agx_last_dumped_time = now; +} + void agx_bo_unreference(struct agx_device *dev, struct agx_bo *bo) { @@ -193,6 +342,8 @@ agx_bo_unreference(struct agx_device *dev, struct agx_bo *bo) agx_bo_free(dev, bo); } + agx_bo_dump_all_periodic(dev); + pthread_mutex_unlock(&dev->bo_map_lock); } @@ -240,5 +391,6 @@ agx_bo_create(struct agx_device *dev, size_t size, unsigned align, if (dev->debug & AGX_DBG_TRACE) agxdecode_track_alloc(dev->agxdecode, bo); + agx_bo_dump_all_periodic(dev); return bo; } diff --git a/src/asahi/lib/agx_device.c b/src/asahi/lib/agx_device.c index c476ccfb128..d82498dadbc 100644 --- a/src/asahi/lib/agx_device.c +++ b/src/asahi/lib/agx_device.c @@ -46,6 +46,7 @@ asahi_simple_ioctl(struct agx_device *dev, unsigned cmd, void *req) /* clang-format off */ static const struct debug_named_value agx_debug_options[] = { {"trace", AGX_DBG_TRACE, "Trace the command stream"}, + {"bodump", AGX_DBG_BODUMP, "Periodically dump live BOs"}, {"no16", AGX_DBG_NO16, "Disable 16-bit support"}, {"perf", AGX_DBG_PERF, "Print performance warnings"}, #ifndef NDEBUG @@ -67,6 +68,7 @@ static const struct debug_named_value agx_debug_options[] = { {"scratch", AGX_DBG_SCRATCH, "Debug scratch memory usage"}, {"1queue", AGX_DBG_1QUEUE, "Force usage of a single queue for multiple contexts"}, {"nosoft", AGX_DBG_NOSOFT, "Disable soft fault optimizations"}, + {"bodumpverbose", AGX_DBG_BODUMPVERBOSE, "Include extra info with dumps"}, DEBUG_NAMED_VALUE_END }; /* clang-format on */ diff --git a/src/asahi/lib/agx_device.h b/src/asahi/lib/agx_device.h index 881c7ed8eef..94c58f5a816 100644 --- a/src/asahi/lib/agx_device.h +++ b/src/asahi/lib/agx_device.h @@ -30,7 +30,7 @@ static const uint64_t AGX_SUPPORTED_INCOMPAT_FEATURES = enum agx_dbg { AGX_DBG_TRACE = BITFIELD_BIT(0), - /* bit 1 unused */ + AGX_DBG_BODUMP = BITFIELD_BIT(1), AGX_DBG_NO16 = BITFIELD_BIT(2), AGX_DBG_DIRTY = BITFIELD_BIT(3), AGX_DBG_PRECOMPILE = BITFIELD_BIT(4), @@ -46,7 +46,7 @@ enum agx_dbg { AGX_DBG_SMALLTILE = BITFIELD_BIT(14), AGX_DBG_NOMSAA = BITFIELD_BIT(15), AGX_DBG_NOSHADOW = BITFIELD_BIT(16), - /* bit 17 unused */ + AGX_DBG_BODUMPVERBOSE = BITFIELD_BIT(17), AGX_DBG_SCRATCH = BITFIELD_BIT(18), AGX_DBG_NOSOFT = BITFIELD_BIT(19), AGX_DBG_FEEDBACK = BITFIELD_BIT(20),