Fix cross-thread ownership check: Use bits 8-15 for owner_tid_low
Problem: - TLS_SLL_PUSH_DUP crash in Larson multi-threaded benchmark - Cross-thread frees incorrectly routed to same-thread TLS path - Root cause: pthread_t on glibc is 256-byte aligned (TCB base) so lower 8 bits are ALWAYS 0x00 for ALL threads Fix: - Change owner_tid_low from (tid & 0xFF) to ((tid >> 8) & 0xFF) - Bits 8-15 actually vary between threads, enabling correct detection - Applied consistently across all ownership check locations: - superslab_inline.h: ss_owner_try_acquire/release/is_mine - slab_handle.h: slab_try_acquire - tiny_free_fast.inc.h: tiny_free_is_same_thread_ss - tiny_free_fast_v2.inc.h: cross-thread detection - tiny_superslab_free.inc.h: same-thread check - ss_allocation_box.c: slab initialization - hakmem_tiny_superslab.c: ownership handling Also added: - Address watcher debug infrastructure (tiny_region_id.h) - Cross-thread detection in malloc_tiny_fast.h Front Gate Test results: - Larson 1T/2T/4T: PASS (no TLS_SLL_PUSH_DUP crash) - random_mixed: PASS - Performance: ~20M ops/s (regression from 48M, needs optimization) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -10,6 +10,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <execinfo.h>
|
||||
#include <dlfcn.h>
|
||||
#include "hakmem_build_flags.h"
|
||||
@ -17,6 +19,7 @@
|
||||
#include "ptr_track.h"
|
||||
#include "hakmem_super_registry.h"
|
||||
#include "superslab/superslab_inline.h"
|
||||
#include "hakmem_tiny.h" // For TinyTLSSLL type
|
||||
|
||||
// Feature flag: Enable header-based class_idx lookup
|
||||
#ifndef HAKMEM_TINY_HEADER_CLASSIDX
|
||||
@ -42,6 +45,122 @@
|
||||
#define HEADER_MAGIC 0xA0
|
||||
#define HEADER_CLASS_MASK 0x0F
|
||||
|
||||
// ========== Address Watcher (Debug Only) ==========
|
||||
|
||||
#if !HAKMEM_BUILD_RELEASE
|
||||
// Helper: Get current thread ID (watcher-local version to avoid redefinition)
|
||||
static inline uint32_t watcher_self_u32(void) {
|
||||
return (uint32_t)(uintptr_t)pthread_self();
|
||||
}
|
||||
|
||||
// Address watcher: Tracks when a specific address is allocated or freed
|
||||
// Usage: HAKMEM_WATCH_ADDR=0x7f1234567890 ./program
|
||||
static inline uintptr_t get_watch_addr(void) {
|
||||
static uintptr_t watch_addr = 0;
|
||||
static int initialized = 0;
|
||||
|
||||
if (!initialized) {
|
||||
const char* env = getenv("HAKMEM_WATCH_ADDR");
|
||||
if (env && *env) {
|
||||
// Parse hex address (with or without 0x prefix)
|
||||
if (env[0] == '0' && (env[1] == 'x' || env[1] == 'X')) {
|
||||
watch_addr = (uintptr_t)strtoull(env + 2, NULL, 16);
|
||||
} else {
|
||||
watch_addr = (uintptr_t)strtoull(env, NULL, 16);
|
||||
}
|
||||
if (watch_addr != 0) {
|
||||
fprintf(stderr, "[WATCH_INIT] Watching address: %p\n", (void*)watch_addr);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
return watch_addr;
|
||||
}
|
||||
|
||||
// Allocation source tracking
|
||||
typedef enum {
|
||||
ALLOC_SOURCE_UNKNOWN = 0,
|
||||
ALLOC_SOURCE_TLS_SLL, // TLS freelist pop
|
||||
ALLOC_SOURCE_FREELIST, // Slab freelist pop
|
||||
ALLOC_SOURCE_CARVE, // Linear carve from slab
|
||||
ALLOC_SOURCE_NEW_SLAB, // Newly allocated slab
|
||||
} AllocSource;
|
||||
|
||||
static __thread AllocSource g_last_alloc_source = ALLOC_SOURCE_UNKNOWN;
|
||||
|
||||
// Use int to match extern declarations in other files
|
||||
static inline void set_alloc_source(int source) {
|
||||
g_last_alloc_source = (AllocSource)source;
|
||||
}
|
||||
|
||||
static inline const char* alloc_source_name(AllocSource source) {
|
||||
switch (source) {
|
||||
case ALLOC_SOURCE_TLS_SLL: return "TLS_SLL";
|
||||
case ALLOC_SOURCE_FREELIST: return "FREELIST";
|
||||
case ALLOC_SOURCE_CARVE: return "CARVE";
|
||||
case ALLOC_SOURCE_NEW_SLAB: return "NEW_SLAB";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
// Watch trigger: Called when watch address is allocated
|
||||
static inline void watch_alloc_trigger(void* base, int class_idx, AllocSource source) {
|
||||
extern __thread TinyTLSSLL g_tls_sll[];
|
||||
extern _Atomic uint64_t g_debug_op_count;
|
||||
|
||||
uint64_t op = atomic_load(&g_debug_op_count);
|
||||
uint32_t tls_count = g_tls_sll[class_idx].count;
|
||||
void* freelist_head = g_tls_sll[class_idx].head;
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "========================================\n");
|
||||
fprintf(stderr, "[WATCH_ALLOC_HIT] Address %p allocated!\n", base);
|
||||
fprintf(stderr, "========================================\n");
|
||||
fprintf(stderr, " Operation: #%lu\n", (unsigned long)op);
|
||||
fprintf(stderr, " Class: %d (%zu bytes)\n", class_idx, tiny_stride_for_class(class_idx));
|
||||
fprintf(stderr, " Source: %s\n", alloc_source_name(source));
|
||||
fprintf(stderr, " TLS count: %u\n", tls_count);
|
||||
fprintf(stderr, " TLS head: %p\n", freelist_head);
|
||||
fprintf(stderr, " Thread: %u\n", (unsigned)watcher_self_u32());
|
||||
|
||||
// Try to get slab metadata if available
|
||||
struct SuperSlab* ss = hak_super_lookup(base);
|
||||
if (ss && ss->magic == SUPERSLAB_MAGIC) {
|
||||
int slab_idx = slab_index_for(ss, base);
|
||||
if (slab_idx >= 0 && slab_idx < ss_slabs_capacity(ss)) {
|
||||
TinySlabMeta* meta = &ss->slabs[slab_idx];
|
||||
fprintf(stderr, " Slab metadata:\n");
|
||||
fprintf(stderr, " SuperSlab: %p\n", (void*)ss);
|
||||
fprintf(stderr, " Slab index: %d\n", slab_idx);
|
||||
fprintf(stderr, " Slab class: %u\n", (unsigned)meta->class_idx);
|
||||
fprintf(stderr, " Used: %u\n", (unsigned)meta->used);
|
||||
fprintf(stderr, " Capacity: %u\n", (unsigned)meta->capacity);
|
||||
fprintf(stderr, " Freelist: %p\n", meta->freelist);
|
||||
fprintf(stderr, " Owner TID: %u\n", (unsigned)meta->owner_tid_low);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "========================================\n");
|
||||
fprintf(stderr, "\n");
|
||||
fflush(stderr);
|
||||
|
||||
// Print backtrace for debugging
|
||||
void* bt[16];
|
||||
int frames = backtrace(bt, 16);
|
||||
fprintf(stderr, "[WATCH_BACKTRACE] %d frames:\n", frames);
|
||||
backtrace_symbols_fd(bt, frames, fileno(stderr));
|
||||
fprintf(stderr, "\n");
|
||||
fflush(stderr);
|
||||
|
||||
// Abort to capture the exact moment
|
||||
fprintf(stderr, "[WATCH_ABORT] Aborting to preserve state...\n");
|
||||
fflush(stderr);
|
||||
abort();
|
||||
}
|
||||
#endif // !HAKMEM_BUILD_RELEASE
|
||||
|
||||
// ========== Write Header (Allocation) ==========
|
||||
|
||||
// Write class_idx to header (called after allocation)
|
||||
@ -50,6 +169,14 @@
|
||||
static inline void* tiny_region_id_write_header(void* base, int class_idx) {
|
||||
if (!base) return base;
|
||||
|
||||
#if !HAKMEM_BUILD_RELEASE
|
||||
// Address watcher: Check if this is the watched address
|
||||
uintptr_t watch = get_watch_addr();
|
||||
if (watch != 0 && (uintptr_t)base == watch) {
|
||||
watch_alloc_trigger(base, class_idx, g_last_alloc_source);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Phase E1-CORRECT: ALL classes (C0-C7) have 1-byte header (no exceptions)
|
||||
// Rationale: Unified box structure enables:
|
||||
// - O(1) class identification (no registry lookup)
|
||||
@ -105,6 +232,23 @@ static inline void* tiny_region_id_write_header(void* base, int class_idx) {
|
||||
PTR_TRACK_HEADER_WRITE(base, HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK));
|
||||
void* user = header_ptr + 1; // skip header for user pointer
|
||||
PTR_TRACK_MALLOC(base, 0, class_idx); // Track at BASE (where header is)
|
||||
|
||||
// ========== ALLOCATION LOGGING (Debug builds only) ==========
|
||||
// NOTE: This logging is ALWAYS active (not guarded by HAKMEM_BUILD_RELEASE)
|
||||
// because we need to track allocations even in optimized debug builds
|
||||
{
|
||||
extern _Atomic uint64_t g_debug_op_count;
|
||||
extern __thread TinyTLSSLL g_tls_sll[];
|
||||
uint64_t op = atomic_fetch_add(&g_debug_op_count, 1);
|
||||
if (op < 2000) { // ALL classes for comprehensive tracing
|
||||
fprintf(stderr, "[OP#%04lu ALLOC] cls=%d ptr=%p base=%p from=write_header tls_count=%u\n",
|
||||
(unsigned long)op, class_idx, user, base,
|
||||
g_tls_sll[class_idx].count);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
// ========== END ALLOCATION LOGGING ==========
|
||||
|
||||
// Optional guard: log stride/base/user for targeted class
|
||||
extern int tiny_guard_is_enabled(void);
|
||||
extern void tiny_guard_on_alloc(int cls, void* base, void* user, size_t stride);
|
||||
|
||||
Reference in New Issue
Block a user