zink: add mem debugging

modeled off turnip's debug infra, this adds debug printing for oom
scenarios

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/23653>
This commit is contained in:
Mike Blumenkrantz
2023-06-13 10:58:12 -04:00
committed by Marge Bot
parent 65fad783c7
commit 4edbe8f5a0
7 changed files with 155 additions and 3 deletions

View File

@@ -315,6 +315,8 @@ variable:
Disable async optimized pipeline compiles
``nobgc``
Disable all async pipeline compiles
``mem``
Enable memory allocation debugging
Vulkan Validation Layers
^^^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -303,6 +303,11 @@ bo_create_internal(struct zink_screen *screen,
VkResult ret = VKSCR(AllocateMemory)(screen->dev, &mai, NULL, &bo->mem);
if (!zink_screen_handle_vkresult(screen, ret)) {
mesa_loge("zink: couldn't allocate memory: heap=%u size=%" PRIu64, heap, size);
if (zink_debug & ZINK_DEBUG_MEM) {
zink_debug_mem_print_stats(screen);
/* abort with mem debug to allow debugging */
abort();
}
goto fail;
}

View File

@@ -65,6 +65,102 @@
#define ZINK_EXTERNAL_MEMORY_HANDLE 999
struct zink_debug_mem_entry {
uint32_t count;
uint64_t size;
const char *name;
};
static const char *
zink_debug_mem_add(struct zink_screen *screen, uint64_t size, const char *name)
{
assert(name);
simple_mtx_lock(&screen->debug_mem_lock);
struct hash_entry *entry = _mesa_hash_table_search(screen->debug_mem_sizes, name);
struct zink_debug_mem_entry *debug_bos;
if (!entry) {
debug_bos = calloc(1, sizeof(struct zink_debug_mem_entry));
debug_bos->name = strdup(name);
_mesa_hash_table_insert(screen->debug_mem_sizes, debug_bos->name, debug_bos);
} else {
debug_bos = (struct zink_debug_mem_entry *) entry->data;
}
debug_bos->count++;
debug_bos->size += align(size, 4096);
simple_mtx_unlock(&screen->debug_mem_lock);
return debug_bos->name;
}
static void
zink_debug_mem_del(struct zink_screen *screen, struct zink_bo *bo)
{
simple_mtx_lock(&screen->debug_mem_lock);
struct hash_entry *entry = _mesa_hash_table_search(screen->debug_mem_sizes, bo->name);
/* If we're finishing the BO, it should have been added already */
assert(entry);
struct zink_debug_mem_entry *debug_bos = entry->data;
debug_bos->count--;
debug_bos->size -= align(zink_bo_get_size(bo), 4096);
if (!debug_bos->count) {
_mesa_hash_table_remove(screen->debug_mem_sizes, entry);
free((void*)debug_bos->name);
free(debug_bos);
}
simple_mtx_unlock(&screen->debug_mem_lock);
}
static int
debug_bos_count_compare(const void *in_a, const void *in_b)
{
struct zink_debug_mem_entry *a = *(struct zink_debug_mem_entry **)in_a;
struct zink_debug_mem_entry *b = *(struct zink_debug_mem_entry **)in_b;
return a->count - b->count;
}
void
zink_debug_mem_print_stats(struct zink_screen *screen)
{
simple_mtx_lock(&screen->debug_mem_lock);
/* Put the HT's sizes data in an array so we can sort by number of allocations. */
struct util_dynarray dyn;
util_dynarray_init(&dyn, NULL);
uint32_t size = 0;
uint32_t count = 0;
hash_table_foreach(screen->debug_mem_sizes, entry)
{
struct zink_debug_mem_entry *debug_bos = entry->data;
util_dynarray_append(&dyn, struct zink_debug_mem_entry *, debug_bos);
size += debug_bos->size / 1024;
count += debug_bos->count;
}
qsort(dyn.data,
util_dynarray_num_elements(&dyn, struct zink_debug_mem_entry *),
sizeof(struct zink_debug_mem_entryos_entry *), debug_bos_count_compare);
util_dynarray_foreach(&dyn, struct zink_debug_mem_entry *, entryp)
{
struct zink_debug_mem_entry *debug_bos = *entryp;
mesa_logi("%30s: %4d bos, %lld kb\n", debug_bos->name, debug_bos->count,
(long long) (debug_bos->size / 1024));
}
mesa_logi("submitted %d bos (%d MB)\n", count, DIV_ROUND_UP(size, 1024));
util_dynarray_fini(&dyn);
simple_mtx_unlock(&screen->debug_mem_lock);
}
static bool
equals_ivci(const void *a, const void *b)
{
@@ -104,6 +200,8 @@ zink_destroy_resource_object(struct zink_screen *screen, struct zink_resource_ob
while (util_dynarray_contains(&obj->views, VkImageView))
VKSCR(DestroyImageView)(screen->dev, util_dynarray_pop(&obj->views, VkImageView), NULL);
}
if (!obj->dt && zink_debug & ZINK_DEBUG_MEM)
zink_debug_mem_del(screen, obj->bo);
util_dynarray_fini(&obj->views);
for (unsigned i = 0; i < ARRAY_SIZE(obj->copies); i++)
util_dynarray_fini(&obj->copies[i]);
@@ -1147,6 +1245,35 @@ retry:
obj->offset = zink_bo_get_offset(obj->bo);
obj->size = zink_bo_get_size(obj->bo);
}
if (zink_debug & ZINK_DEBUG_MEM) {
char buf[4096];
unsigned idx = 0;
if (obj->is_buffer) {
size_t size = (size_t)DIV_ROUND_UP(obj->size, 1024);
if (templ->bind == PIPE_BIND_QUERY_BUFFER && templ->usage == PIPE_USAGE_STAGING) //internal qbo
idx += snprintf(buf, sizeof(buf), "QBO(%zu)", size);
else
idx += snprintf(buf, sizeof(buf), "BUF(%zu)", size);
} else {
idx += snprintf(buf, sizeof(buf), "IMG(%s:%ux%ux%u)", util_format_short_name(templ->format), templ->width0, templ->height0, templ->depth0);
}
/*
zink_vkflags_func flag_func = obj->is_buffer ? (zink_vkflags_func)vk_BufferCreateFlagBits_to_str : (zink_vkflags_func)vk_ImageCreateFlagBits_to_str;
zink_vkflags_func usage_func = obj->is_buffer ? (zink_vkflags_func)vk_BufferUsageFlagBits_to_str : (zink_vkflags_func)vk_ImageUsageFlagBits_to_str;
if (obj->vkflags) {
buf[idx++] = '[';
idx += zink_string_vkflags_unroll(&buf[idx], sizeof(buf) - idx, obj->vkflags, flag_func);
buf[idx++] = ']';
}
if (obj->vkusage) {
buf[idx++] = '[';
idx += zink_string_vkflags_unroll(&buf[idx], sizeof(buf) - idx, obj->vkusage, usage_func);
buf[idx++] = ']';
}
*/
buf[idx] = 0;
obj->bo->name = zink_debug_mem_add(screen, obj->size, buf);
}
obj->coherent = screen->info.mem_props.memoryTypes[obj->bo->base.placement].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (!(templ->flags & PIPE_RESOURCE_FLAG_SPARSE)) {
@@ -1192,6 +1319,7 @@ retry:
}
}
}
for (unsigned i = 0; i < max_level; i++)
util_dynarray_init(&obj->copies[i], NULL);
return obj;

View File

@@ -191,6 +191,9 @@ zink_batch_resource_usage_set(struct zink_batch *batch, struct zink_resource *re
batch->has_work = true;
}
void
zink_debug_mem_print_stats(struct zink_screen *screen);
#ifdef __cplusplus
}
#endif

View File

@@ -97,6 +97,7 @@ zink_debug_options[] = {
{ "noopt", ZINK_DEBUG_NOOPT, "Disable async optimized pipeline compiles" },
{ "nobgc", ZINK_DEBUG_NOBGC, "Disable all async pipeline compiles" },
{ "dgc", ZINK_DEBUG_DGC, "Use DGC (driver testing only)" },
{ "mem", ZINK_DEBUG_MEM, "Debug memory allocations" },
DEBUG_NAMED_VALUE_END
};
@@ -1493,6 +1494,9 @@ zink_destroy_screen(struct pipe_screen *pscreen)
if (screen->bindless_layout)
VKSCR(DestroyDescriptorSetLayout)(screen->dev, screen->bindless_layout, NULL);
if (zink_debug & ZINK_DEBUG_MEM)
simple_mtx_destroy(&screen->debug_mem_lock);
simple_mtx_destroy(&screen->queue_lock);
VKSCR(DestroyDevice)(screen->dev, NULL);
VKSCR(DestroyInstance)(screen->instance, NULL);
@@ -2896,6 +2900,11 @@ zink_internal_create_screen(const struct pipe_screen_config *config)
}
}
if (zink_debug & ZINK_DEBUG_MEM) {
simple_mtx_init(&screen->debug_mem_lock, mtx_plain);
screen->debug_mem_sizes = _mesa_hash_table_create(screen, _mesa_hash_string, _mesa_key_string_equal);
}
init_driver_workarounds(screen);
screen->dev = zink_create_logical_device(screen);

View File

@@ -108,7 +108,7 @@ zink_string_vkflags_unroll(char *buf, size_t bufsize, uint64_t flags, zink_vkfla
u_foreach_bit64(bit, flags) {
if (!first)
buf[idx++] = '|';
idx += snprintf(&buf[idx], bufsize - idx, "%s", func((1ul<<bit)));
idx += snprintf(&buf[idx], bufsize - idx, "%s", func((BITFIELD64_BIT(bit))));
first = false;
}
return idx;

View File

@@ -238,6 +238,7 @@ enum zink_debug {
ZINK_DEBUG_NOOPT = (1<<15),
ZINK_DEBUG_NOBGC = (1<<16),
ZINK_DEBUG_DGC = (1<<17),
ZINK_DEBUG_MEM = (1<<18),
};
enum zink_pv_emulation_primitive {
@@ -711,6 +712,7 @@ struct zink_bo {
uint64_t offset;
uint32_t unique_id;
const char *name;
simple_mtx_t lock;
@@ -1253,8 +1255,8 @@ struct zink_resource_object {
VkDeviceSize offset, size, alignment;
VkImageCreateFlags vkflags;
VkImageUsageFlags vkusage;
uint64_t vkflags;
uint64_t vkusage;
VkFormatFeatureFlags vkfeats;
uint64_t modifier;
VkImageAspectFlags modifier_aspect;
@@ -1429,6 +1431,9 @@ struct zink_screen {
VkInstance instance;
struct zink_instance_info instance_info;
struct hash_table *debug_mem_sizes;
simple_mtx_t debug_mem_lock;
VkPhysicalDevice pdev;
uint32_t vk_version, spirv_version;
struct util_idalloc_mt buffer_ids;