blob: 57d219d944995f7477cc37663c42fd82483f0379 [file] [edit]
#include <stdlib.h>
// A simple, deterministic, single-threaded nested call chain. We stop at the
// innermost function and walk the stack.
//
// The breakpoint is in the innermost frame (func_e), and that frame carries
// locals of every kind, so that examining *just the stopped frame* already
// exercises the various memory-read paths in one frame:
// - scalar locals (int / long / double)
// - aggregate locals (a struct and a fixed stack array)
// - a variable-length array (dynamically sized stack storage, like alloca)
// - pointer locals, including a pointer to heap memory
//
// The outer frames (func_d / func_c) also carry locals of these kinds, so that
// walking the whole stack and examining every frame reads the same variety of
// memory across several frames.
//
// The frame-pointer backchain is expedited at the public stop, so the backtrace
// itself is packet-free; reading the *values* of these locals is not expedited
// and must read memory from the stub.
#define HEAP_COUNT 8
// A volatile sink so the locals are observably used.
volatile long g_sink;
struct Stats {
long sum;
long min;
long max;
double mean;
};
// The innermost frame, where we stop. It carries several kind of local: a
// scalar, aggregates (struct + array), pointers (including one into heap
// memory) and a variable-length array. Examining this single frame on a stop
// reads both stack and heap memory.
static int func_e(int depth) {
int i = depth + 1;
long l = (long)depth * 1000;
double d = depth + 0.5;
struct Stats stats = {.sum = i, .min = i - 1, .max = i + 1, .mean = d};
long arr[4] = {i, i + 1, i + 2, i + 3};
int n = i + 4; // a runtime bound, so vla is a true variable-length array
long vla[n]; // dynamically sized stack storage (like alloca)
for (int k = 0; k < n; ++k)
vla[k] = (long)i - k;
long *heap = (long *)malloc(sizeof(long) * HEAP_COUNT);
for (int k = 0; k < HEAP_COUNT; ++k)
heap[k] = (long)i + k;
const char *str = "hello from func_e";
int *self = &i;
g_sink = i + l + (long)d + stats.sum + arr[3] + vla[n - 1] +
heap[HEAP_COUNT - 1] + str[0] + *self; // break here
int r = i + (int)l + (int)d + (int)stats.sum + (int)arr[3] + (int)vla[n - 1] +
(int)heap[HEAP_COUNT - 1] + str[0] + *self;
free(heap);
return r;
}
// Aggregate locals: a struct and a fixed-size stack array.
static int func_d(int x) {
struct Stats stats = {.sum = x, .min = x - 1, .max = x + 1, .mean = x + 0.5};
long arr[4] = {x, x + 1, x + 2, x + 3};
int r = func_e(x);
return r + (int)stats.sum + (int)arr[3];
}
// Pointer locals, including a pointer into heap memory.
static int func_c(int x) {
long *heap = (long *)malloc(sizeof(long) * HEAP_COUNT);
for (int i = 0; i < HEAP_COUNT; ++i)
heap[i] = (long)x + i;
const char *str = "hello from func_c";
int *self = &x;
int r = func_d(x);
r += (int)heap[HEAP_COUNT - 1] + (int)str[0] + *self;
free(heap);
return r;
}
static int func_b(int x) { return func_c(x) + 1; }
static int func_a(int x) { return func_b(x) + 1; }
int main() {
g_sink = func_a(0);
return 0;
}