Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions tools/testbench/include/testbench/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ struct testbench_prm {
#endif
};

struct tb_heap_usage_record {
char *module_name;
size_t heap_max;
};

extern int debug;

int tb_decode_enum(struct snd_soc_tplg_enum_control *enum_ctl, char *token);
Expand Down
67 changes: 58 additions & 9 deletions tools/testbench/testbench.c
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,47 @@ static int parse_input_args(int argc, char **argv, struct testbench_prm *tp)
return ret;
}

static void test_pipeline_stats(struct testbench_prm *tp, long long delta_t)
#if CONFIG_IPC_MAJOR_4
static int tb_collect_heap_usage(struct testbench_prm *tp, struct tb_heap_usage_record *records,
int *count_out)
{
struct list_item *item;
int count = 0;

list_for_item(item, &tp->widget_list) {
struct tplg_comp_info *info = container_of(item, struct tplg_comp_info, item);
uint32_t comp_id = IPC4_COMP_ID(info->module_id, info->instance_id);
struct comp_dev *dev = ipc4_get_comp_dev(comp_id);

if (!dev || !dev->mod)
continue;

/* In testbench environment, skip AIF/DAI because they are not real components. */
if (info->type == SND_SOC_TPLG_DAPM_AIF_IN ||
info->type == SND_SOC_TPLG_DAPM_AIF_OUT ||
info->type == SND_SOC_TPLG_DAPM_DAI_IN ||
info->type == SND_SOC_TPLG_DAPM_DAI_OUT)
continue;

if (count >= TB_NUM_WIDGETS_SUPPORTED) {
fprintf(stderr, "Error: Too many components for heap records, max %d.\n",
TB_NUM_WIDGETS_SUPPORTED);
break;
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tb_collect_heap_usage() caps heap records at TB_NUM_WIDGETS_SUPPORTED (16) and breaks out, which will silently drop heap usage for remaining modules on larger topologies. Consider sizing the record buffer to the actual number of non-AIF/DAI components (e.g., first pass to count, then allocate), or introduce a dedicated "max components" constant that is large enough for real-world IPC4 topologies.

Suggested change
break;
*count_out = count;
return -EINVAL;

Copilot uses AI. Check for mistakes.
}

records[count].module_name = info->name;
records[count].heap_max = dev->mod->priv.resources.heap_high_water_mark;
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tb_collect_heap_usage() reads dev->mod->priv.resources.heap_high_water_mark directly. Since module_adapter_heap_usage(mod, &hwm) exists in the module adapter API, prefer using that accessor to avoid coupling testbench to internal processing_module layout/details.

Suggested change
records[count].heap_max = dev->mod->priv.resources.heap_high_water_mark;
if (module_adapter_heap_usage(dev->mod, &records[count].heap_max) < 0)
continue;

Copilot uses AI. Check for mistakes.
count++;
}

*count_out = count;
return 0;
}
#endif

static void test_pipeline_stats(struct testbench_prm *tp, long long delta_t,
struct tb_heap_usage_record *heap_records,
int heap_records_count)
{
long long file_cycles, pipeline_cycles;
float pipeline_mcps;
Expand Down Expand Up @@ -295,11 +335,15 @@ static void test_pipeline_stats(struct testbench_prm *tp, long long delta_t)
printf("Warning: Use -d 3 or smaller value to avoid traces to increase MCPS.\n");
}

if (delta_t)
printf("Total execution time: %lld us, %.2f x realtime\n",
delta_t, (float)frames_out / tp->fs_out * 1000000 / delta_t);
if (heap_records_count > 0) {
for (i = 0; i < heap_records_count; i++)
printf("Heap usage for module %s: %u bytes\n",
heap_records[i].module_name, (uint32_t)heap_records[i].heap_max);
}

printf("\n");
if (delta_t)
printf("Total execution time: %lld us, %.2f x realtime\n\n", delta_t,
(float)frames_out / tp->fs_out * 1000000 / delta_t);
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_pipeline_stats() no longer prints a trailing blank line when delta_t == 0 (previously it always printed \n). If delta_t can be 0 for short runs or error cases, consider keeping an unconditional newline to avoid the next output running into the summary block.

Suggested change
(float)frames_out / tp->fs_out * 1000000 / delta_t);
(float)frames_out / tp->fs_out * 1000000 / delta_t);
else
printf("\n");

Copilot uses AI. Check for mistakes.
}

/*
Expand All @@ -308,14 +352,16 @@ static void test_pipeline_stats(struct testbench_prm *tp, long long delta_t)
*/
static int pipline_test(struct testbench_prm *tp)
{
float samples_to_ns;
int dp_count = 0;
struct timespec td0, td1;
struct tb_heap_usage_record heap_usage_records[TB_NUM_WIDGETS_SUPPORTED];
struct file_state *out_stat;
struct timespec td0, td1;
long long delta_t;
int64_t next_control_ns;
int64_t time_ns;
float samples_to_ns;
int err;
int heap_usage_records_count = 0;
int dp_count = 0;

/* build, run and teardown pipelines */
while (dp_count < tp->dynamic_pipeline_iterations) {
Expand Down Expand Up @@ -396,6 +442,9 @@ static int pipline_test(struct testbench_prm *tp)
tb_gettime(&td1);

out:
#if CONFIG_IPC_MAJOR_4
tb_collect_heap_usage(tp, heap_usage_records, &heap_usage_records_count);
#endif
err = tb_set_reset_state(tp);
if (err < 0) {
fprintf(stderr, "error: pipeline reset %d failed %d\n",
Expand All @@ -408,7 +457,7 @@ static int pipline_test(struct testbench_prm *tp)
*/
delta_t = (td1.tv_sec - td0.tv_sec) * 1000000;
delta_t += (td1.tv_nsec - td0.tv_nsec) / 1000;
test_pipeline_stats(tp, delta_t);
test_pipeline_stats(tp, delta_t, heap_usage_records, heap_usage_records_count);
Comment on lines 458 to +460
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In error paths that goto out before tb_gettime(&td0) / tb_gettime(&td1) run, td0/td1 can be uninitialized, but delta_t is still computed from them. This is undefined behavior and can print garbage timing (or worse). Initialize td0/td1 and/or gate the delta_t calculation/printing behind a flag that indicates timing was captured.

Copilot uses AI. Check for mistakes.

err = tb_free_all_pipelines(tp);
if (err < 0) {
Expand Down