[TOOLS] Add rbtree_walk() and optional dynablock size histogram (#3558)

Add rbtree_walk() for in-order tree traversal with user callback.
Use it to implement a dynablock size histogram printed at exit,
disabled by default behind //#define DYNAREC_HIST (following the
TRACE_MEMSTAT pattern). Ref #3554.
This commit is contained in:
Trung Lê authored and GitHub committed 2026-02-25 16:26:52 +01:00
1 parent fbfb4697ad
commit fafdb6e516
3 files changed
+69 -1

No files matched your search

+51 -1
View File
@@ -6,6 +6,7 @@
#include <signal.h>
#include <sys/mman.h>
#include <pthread.h>
#include <inttypes.h>
#include "os.h"
#include "box64context.h"
@@ -245,6 +246,41 @@ box64context_t *NewBox64Context(int argc)
void freeALProcWrapper(box64context_t* context);
void freeCUDAProcWrapper(box64context_t* context);
#ifdef DYNAREC
//#define DYNAREC_HIST // uncomment to print dynablock size histogram at exit
#ifdef DYNAREC_HIST
#define HIST_BUCKETS 12
static const uintptr_t bucket_limits[HIST_BUCKETS] = {
8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, (uintptr_t)-1
};
static const char* bucket_labels[HIST_BUCKETS] = {
"1-8", "9-16", "17-32", "33-64", "65-128", "129-256",
"257-512", "513-1024", "1025-2048", "2049-4096", "4097-8192", "8193+"
};
typedef struct {
uint64_t count[HIST_BUCKETS];
uint64_t total_blocks;
uint64_t total_bytes;
uintptr_t max_size;
} hist_data_t;
static void db_size_walk_cb(uintptr_t start, uintptr_t end, uint64_t data, void* userdata) {
(void)end;
hist_data_t* h = (hist_data_t*)userdata;
uintptr_t size = start; // key = x64_size
uint64_t n = data; // value = count of blocks
for (int i = 0; i < HIST_BUCKETS; i++) {
if (size <= bucket_limits[i]) {
h->count[i] += n;
break;
}
}
h->total_blocks += n;
h->total_bytes += size * n;
if (size > h->max_size) h->max_size = size;
}
#endif
#endif
EXPORTDYN
void FreeBox64Context(box64context_t** context)
{
@@ -342,7 +378,21 @@ void FreeBox64Context(box64context_t** context)
FreeMapSymbols(&ctx->uniques);
#ifdef DYNAREC
//dynarec_log(LOG_INFO, "BOX64 Dynarec at exit: Max DB=%d, rightmost=%d\n", ctx->max_db_size, rb_get_rightmost(ctx->db_sizes));
#ifdef DYNAREC_HIST
{
hist_data_t hist = {0};
rbtree_walk(ctx->db_sizes, db_size_walk_cb, &hist);
if(hist.total_blocks) {
printf_log(LOG_INFO, "BOX64 Dynarec block size histogram: %" PRIu64 " blocks, %" PRIu64 " total x86 bytes, max=%" PRIuPTR "\n",
hist.total_blocks, hist.total_bytes, hist.max_size);
for(int i = 0; i < HIST_BUCKETS; i++) {
if(hist.count[i])
printf_log(LOG_INFO, " %10s: %" PRIu64 " blocks (%.1f%%)\n",
bucket_labels[i], hist.count[i], hist.count[i] * 100.0 / hist.total_blocks);
}
}
}
#endif
rbtree_delete(ctx->db_sizes);
#endif
+9
View File
@@ -328,4 +328,13 @@ uintptr_t rb_get_leftmost(rbtree_t* tree);
*/
void rbtree_print(const rbtree_t* tree);
/**
* @brief Walk the tree in order and call a callback for each node.
*
* @param tree Pointer to the red-black tree to walk.
* @param cb Callback function receiving (start, end, data, userdata) for each node.
* @param userdata Opaque pointer passed to the callback.
*/
void rbtree_walk(const rbtree_t* tree, void (*cb)(uintptr_t start, uintptr_t end, uint64_t data, void* userdata), void* userdata);
#endif // RBTREE_H
+9
View File
@@ -1228,6 +1228,15 @@ static void cache_check(const rbtree_t *tree) {
printf_log(LOG_NONE, "<valid cached node> \n");
}
void rbtree_walk(const rbtree_t *tree, void (*cb)(uintptr_t start, uintptr_t end, uint64_t data, void* userdata), void* userdata) {
if (!tree || !tree->root) return;
rbnode *node = tree->leftmost;
while (node) {
cb(node->start, node->end, node->data, userdata);
node = succ_node(node);
}
}
void rbtree_print(const rbtree_t *tree) {
if (!tree) {
printf_log(LOG_NONE, "<NULL>\n");