fd: allow limiting RD dumps to specific frames and submits

RD dump generation can be expensive and can only be desired for some
specific part of execution. Trigger file mechanism helps with this to a
certain degree but is still somewhat inexact.

FD_RD_DUMP_SUBMITS environment variable can be used to specify ranges of
submit indices for which RD dumps of command streams should be generated.
FD_RD_DUMP_FRAMES environment variable can similarly be used to specify
ranges of frames under which RD dumps for submitted command streams should
be generated. Frame ranges only really work with Turnip since the frame
count data is available there.

Signed-off-by: Zan Dobersek <zdobersek@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/37499>
This commit is contained in:
Zan Dobersek 2025-09-20 18:23:03 +02:00 committed by Marge Bot
parent 19b3237408
commit 27c16c46fd
5 changed files with 110 additions and 6 deletions

View file

@ -409,6 +409,11 @@ Output dump files and trigger file (when enabled) are hard-coded to be placed
under ``/tmp``, or ``/data/local/tmp`` under Android. `FD_RD_DUMP_TESTNAME` can
be used to specify a more descriptive prefix for the output or trigger files.
Dumping can be limited to specific ranges of frames or submits. For example,
``FD_RD_DUMP_SUBMITS=120-140,160,165`` will dump command streams only for the
specified submits. Similarly, ``FD_RD_DUMP_FRAMES`` can be set to specify for
which frames any submitted command stream should be dumped.
Functionality is generic to any Freedreno-based backend, but is currently only
integrated in the MSM backend of Turnip. Using the existing ``TU_DEBUG=rd``
option will translate to ``FD_RD_DUMP=enable``.

View file

@ -8,6 +8,7 @@
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
@ -35,6 +36,11 @@ static const struct debug_control fd_rd_dump_options[] = {
struct fd_rd_dump_env fd_rd_dump_env;
struct fd_rd_dump_range {
unsigned range_begin;
unsigned range_end;
};
static void
fd_rd_dump_env_init_once(void)
{
@ -69,6 +75,81 @@ fd_rd_output_sanitize_name(char *name)
}
}
static void
fd_rd_parse_dump_range(const char *option_name, struct util_dynarray *range_array)
{
util_dynarray_init(range_array, NULL);
const char *range_value = os_get_option(option_name);
if (!range_value)
return;
const char *p = range_value;
while (p && *p) {
char *ep = NULL;
struct fd_rd_dump_range range;
range.range_begin = strtol(p, &ep, 0);
if (ep == p)
break;
p = ep;
range.range_end = range.range_begin;
if (*p == '-') {
ep = NULL;
range.range_end = strtol(++p, &ep, 0);
if (ep == p)
break;
p = ep;
}
util_dynarray_append(range_array, struct fd_rd_dump_range, range);
if (*p == ',')
++p;
if (!isdigit(*p) && !!*p)
break;
}
if (*p == '\0') {
mesa_logi("[fd_rd_output] %s specified %" PRIuPTR " dump ranges:",
option_name, util_dynarray_num_elements(range_array, struct fd_rd_dump_range));
util_dynarray_foreach(range_array, struct fd_rd_dump_range, range) {
mesa_logi("[fd_rd_output] [%u, %u]", range->range_begin, range->range_end);
}
} else {
mesa_logi("[fd_rd_output] failed to parse dump range '%s' for %s",
range_value, option_name);
util_dynarray_clear(range_array);
struct fd_rd_dump_range invalid_range = {
.range_begin = UINT_MAX,
.range_end = UINT_MAX,
};
util_dynarray_append(range_array, struct fd_rd_dump_range, invalid_range);
}
}
static bool
fd_rd_output_allowed(struct fd_rd_output *output, uint32_t frame, uint32_t submit)
{
/* Allow output if no ranges were specified. */
if (!util_dynarray_num_elements(&output->frame_ranges, struct fd_rd_dump_range) &&
!util_dynarray_num_elements(&output->submit_ranges, struct fd_rd_dump_range))
return true;
util_dynarray_foreach(&output->frame_ranges, struct fd_rd_dump_range, range) {
if (frame >= range->range_begin && frame <= range->range_end)
return true;
}
util_dynarray_foreach(&output->submit_ranges, struct fd_rd_dump_range, range) {
if (submit >= range->range_begin && submit <= range->range_end)
return true;
}
return false;
}
void
fd_rd_output_init(struct fd_rd_output *output, const char* output_name)
{
@ -101,6 +182,9 @@ fd_rd_output_init(struct fd_rd_output *output, const char* output_name)
fd_rd_output_base_path, output->name);
output->trigger_fd = open(file_path, O_RDWR | O_CREAT | O_TRUNC, 0600);
}
fd_rd_parse_dump_range("FD_RD_DUMP_FRAMES", &output->frame_ranges);
fd_rd_parse_dump_range("FD_RD_DUMP_SUBMITS", &output->submit_ranges);
}
void
@ -125,6 +209,9 @@ fd_rd_output_fini(struct fd_rd_output *output)
fd_rd_output_base_path, output->name);
unlink(file_path);
}
util_dynarray_fini(&output->frame_ranges);
util_dynarray_fini(&output->submit_ranges);
}
static void
@ -193,7 +280,7 @@ fd_rd_output_update_trigger_count(struct fd_rd_output *output)
}
bool
fd_rd_output_begin(struct fd_rd_output *output, uint32_t submit_idx)
fd_rd_output_begin(struct fd_rd_output *output, uint32_t frame, uint32_t submit)
{
assert(output->combine ^ (output->file == NULL));
@ -207,12 +294,20 @@ fd_rd_output_begin(struct fd_rd_output *output, uint32_t submit_idx)
--output->trigger_count;
}
if (!fd_rd_output_allowed(output, frame, submit))
return false;
if (output->combine)
return true;
char file_path[PATH_MAX];
snprintf(file_path, sizeof(file_path), "%s/%s_%.5d.rd",
fd_rd_output_base_path, output->name, submit_idx);
if (frame != UINT_MAX) {
snprintf(file_path, sizeof(file_path), "%s/%s_frame%.5d_submit%.5d.rd",
fd_rd_output_base_path, output->name, frame, submit);
} else {
snprintf(file_path, sizeof(file_path), "%s/%s_submit%.5d.rd",
fd_rd_output_base_path, output->name, submit);
}
output->file = gzopen(file_path, "w");
return true;
}

View file

@ -11,6 +11,7 @@
#include <zlib.h>
#include "redump.h"
#include "util/u_dynarray.h"
#ifdef __cplusplus
extern "C" {
@ -41,6 +42,9 @@ struct fd_rd_output {
int trigger_fd;
uint32_t trigger_count;
struct util_dynarray frame_ranges;
struct util_dynarray submit_ranges;
};
void
@ -50,7 +54,7 @@ void
fd_rd_output_fini(struct fd_rd_output *output);
bool
fd_rd_output_begin(struct fd_rd_output *output, uint32_t submit_idx);
fd_rd_output_begin(struct fd_rd_output *output, uint32_t frame, uint32_t submit_idx);
void
fd_rd_output_write_section(struct fd_rd_output *output, enum rd_sect_type type,

View file

@ -121,7 +121,7 @@ msm_dump_rd(struct fd_pipe *pipe, struct drm_msm_gem_submit *req)
struct fd_rd_output *rd = &pipe->dev->rd;
if (!fd_rd_dump_env.flags || !req->nr_cmds ||
!fd_rd_output_begin(rd, req->fence))
!fd_rd_output_begin(rd, UINT_MAX, req->fence))
return;
if (FD_RD_DUMP(FULL)) {

View file

@ -163,7 +163,7 @@ queue_submit(struct vk_queue *_queue, struct vk_queue_submit *vk_submit)
if (cmdbuf_count && FD_RD_DUMP(ENABLE) &&
fd_rd_output_begin(&queue->device->rd_output,
queue->device->submit_count)) {
queue->device->vk.current_frame, queue->device->submit_count)) {
struct tu_device *device = queue->device;
struct fd_rd_output *rd_output = &device->rd_output;