Index: debuginfo-tests/dexter-tests/memvars/bitcast.c =================================================================== --- /dev/null +++ debuginfo-tests/dexter-tests/memvars/bitcast.c @@ -0,0 +1,71 @@ +// REQUIRES: lldb +// RUN: %dexter --fail-lt 1.0 -w --debugger lldb \ +// RUN: --builder 'clang-c' --cflags "-O3 -glldb -fno-inline" -- %s + +//// Adapted from https://bugs.llvm.org/show_bug.cgi?id=34136#c1 +//// LowerDbgDeclare has since been updated to look through bitcasts. We still +//// get suboptimal coverage at the beginning of 'main' though. For each local, +//// LowerDbgDeclare inserts a dbg.value and a dbg.value+DW_OP_deref before the +//// store (after the call to 'getint') and the call to 'alias' respectively. +//// The first dbg.value describes the result of the 'getint' call, eventually +//// becoming a register location. The second points back into the stack +//// home. There is a gap in the coverage between the quickly clobbered register +//// location and the stack location, even though the stack location is valid +//// during that gap. For x86 we end up with this code at the start of main: +//// 00000000004004b0
: +//// 4004b0: sub rsp,0x18 +//// 4004b4: mov edi,0x5 +//// 4004b9: call 400480 +//// 4004be: mov DWORD PTR [rsp+0x14],eax +//// 4004c2: mov edi,0x5 +//// 4004c7: call 400480 +//// 4004cc: mov DWORD PTR [rsp+0x10],eax +//// 4004d0: mov edi,0x5 +//// 4004d5: call 400480 +//// 4004da: mov DWORD PTR [rsp+0xc],eax +//// ... +//// With these variable locations: +//// DW_TAG_variable +//// DW_AT_location (0x00000000: +//// [0x00000000004004be, 0x00000000004004cc): DW_OP_reg0 RAX +//// [0x00000000004004de, 0x0000000000400503): DW_OP_breg7 RSP+20) +//// DW_AT_name ("x") +//// ... +//// DW_TAG_variable +//// DW_AT_location (0x00000037: +//// [0x00000000004004cc, 0x00000000004004da): DW_OP_reg0 RAX +//// [0x00000000004004e8, 0x0000000000400503): DW_OP_breg7 RSP+16) +//// DW_AT_name ("y") +//// ... +//// DW_TAG_variable +//// DW_AT_location (0x0000006e: +//// [0x00000000004004da, 0x00000000004004e8): DW_OP_reg0 RAX +//// [0x00000000004004f2, 0x0000000000400503): DW_OP_breg7 RSP+12) +//// DW_AT_name ("z") +//// ... + +char g = 1; +int five = 5; +int getint(int x) { + g = x - 4; + return x * g; +} + +void alias(char* c) { + g = *c; + *c = (char)five; +} + +int main() { + int x = getint(5); + int y = getint(5); // DexLabel('s1') + int z = getint(5); // DexLabel('s2') + alias((char*)&x); // DexLabel('s3') + alias((char*)&y); + alias((char*)&z); + return 0; // DexLabel('s4') +} + +// DexExpectWatchValue('x', '5', from_line='s1', to_line='s4') +// DexExpectWatchValue('y', '5', from_line='s2', to_line='s4') +// DexExpectWatchValue('z', '5', from_line='s3', to_line='s4') Index: debuginfo-tests/dexter-tests/memvars/const-branch.c =================================================================== --- /dev/null +++ debuginfo-tests/dexter-tests/memvars/const-branch.c @@ -0,0 +1,41 @@ +// REQUIRES: lldb +// RUN: %dexter --fail-lt 1.0 -w --debugger lldb \ +// RUN: --builder 'clang-c' --cflags "-O3 -glldb -fno-inline" -- %s + +//// Adapted from https://bugs.llvm.org/show_bug.cgi?id=34136#c4 + +int g; +void esc(int* p) { + g = *p; + *p = 5; +} +void thing(int x) { + g = x; +} +int fun(int param) { + esc(¶m); //// alloca is live until here DexLabel('s1') + if (param == 0) { //// end of alloca live range + //// param is now a constant, but without lowering to dbg.value we can't + //// capture that and would still point to the stack slot that may even have + //// been reused by now. + //// + //// Right now we get suboptimal coverage for x86: the param load below is + //// CSE'd with the if condition. + //// Instcombine runs LowerDbgDeclare and inserts a dbg.value after the load. + //// SelectionDAG combines the load and cmp. We go from this IR: + //// %0 = load i32, i32* %param.addr, align 4, !dbg !42, !tbaa !20 + //// call void @llvm.dbg.value(metadata i32 %0, ... + //// %cmp = icmp eq i32 %0, 0, !dbg !44 + //// to this MIR: + //// DBG_VALUE $noreg, $noreg, !"param"... + //// CMP32mi8 %param.addr, 1, $noreg, 0, $noreg, 0, implicit-def $eflags, debug-location !44 + thing(param); + } + return 0; // DexLabel('s2') +} +int main() { + return fun(5); +} + +// DexExpectWatchValue('param', '5', from_line='s1', to_line='s2') + Index: debuginfo-tests/dexter-tests/memvars/ctrl-flow.c =================================================================== --- /dev/null +++ debuginfo-tests/dexter-tests/memvars/ctrl-flow.c @@ -0,0 +1,31 @@ +// REQUIRES: lldb +// RUN: %dexter --fail-lt 1.0 -w --debugger lldb \ +// RUN: --builder clang-c --cflags "-O2 -glldb -fno-inline" -- %s + +//// Check that we give good locations to a variable ('local') which is escaped +//// down some control paths and not others. This example is handled well currently. + +int g; +void leak(int *ptr) { + g = *ptr; + *ptr = 2; +} + +int fun(int cond) { + int local = 0; // DexLabel('s1') + if (cond) + leak(&local); + else + local = 1; + return local; // DexLabel('s2') +} + +int main() { + int a = fun(1); + int b = fun(0); + return a + b; +} + +//// fun(1) fun(0) +// DexExpectWatchValue('local', '0', '0', on_line='s1') +// DexExpectWatchValue('local', '2', '1', on_line='s2') Index: debuginfo-tests/dexter-tests/memvars/inlining.c =================================================================== --- /dev/null +++ debuginfo-tests/dexter-tests/memvars/inlining.c @@ -0,0 +1,27 @@ +// REQUIRES: lldb +// RUN: %dexter --fail-lt 1.0 -w --debugger lldb \ +// RUN: --builder clang-c --cflags "-O2 -glldb" -- %s +// +//// Inlining can really hurt escaped locals. Currently 'param' is 'optimized out' after +//// function 'use' is inlined into 'fun'. + +#ifdef _MSC_VER +# define DEX_NOINLINE __declspec(noinline) +# define DEX_INLINE __forceinline +#else +# define DEX_NOINLINE __attribute__((__noinline__)) +# define DEX_INLINE __attribute__((__always_inline__)) +#endif + +int g; +DEX_INLINE static void use(int* p) { + g = *p; +} +DEX_NOINLINE void fun(int param) { + use(¶m); // DexLabel('s1') +} +int main() { + fun(5); +} + +// DexExpectWatchValue('param', '5', on_line='s1') Index: debuginfo-tests/dexter-tests/memvars/loop.c =================================================================== --- /dev/null +++ debuginfo-tests/dexter-tests/memvars/loop.c @@ -0,0 +1,48 @@ +// REQUIRES: lldb +// RUN: %dexter --fail-lt 1.0 -w --debugger lldb \ +// RUN: --builder 'clang-c' --cflags "-O3 -glldb -fno-inline" -- %s + +//// Check that escaped local 'param' in function 'fun' has sensible debug info +//// after the escaping function 'use' gets arg promotion (int* -> int). Currently +//// we lose track of param after the loop header. + +int g = 0; +//// no-inline (compiler args), read-only function with internal linkage is a +//// good candidate for arg promotion. +static void use(const int* p) { + //// Promoted args would be a good candidate for an DW_OP_implicit_pointer. + g = *p; // DexLabel('s1') +} + +void do_thing(int x) { + g *= x; +} + +int fun(int param) { + do_thing(0); // DexLabel('s2') + for (int i = 0; i < param; ++i) { + use(¶m); + } + + //// x86 loop body looks like this, with param in ebx: + //// 4004b0: mov edi,ebx + //// 4004b2: call 4004d0 <_ZL3usePKi> + //// 4004b7: add ebp,0xffffffff + //// 4004ba: jne 4004b0 <_Z3funi+0x20> + + //// But we lose track of param's location before the loop: + //// DW_TAG_formal_parameter + //// DW_AT_location (0x00000039: + //// [0x0000000000400490, 0x0000000000400495): DW_OP_reg5 RDI + //// [0x0000000000400495, 0x00000000004004a2): DW_OP_reg3 RBX) + //// DW_AT_name ("param") + + return g; // DexLabel('s3') +} + +int main() { + return fun(5); +} + +// DexExpectWatchValue('*p', 5, 5, 5, 5, 5, on_line='s1') +// DexExpectWatchValue('param', 5, from_line='s2', to_line='s3') Index: debuginfo-tests/dexter-tests/memvars/ptr-to.c =================================================================== --- /dev/null +++ debuginfo-tests/dexter-tests/memvars/ptr-to.c @@ -0,0 +1,22 @@ +// REQUIRES: lldb +// RUN: %dexter --fail-lt 1.0 -w --debugger lldb \ +// RUN: --builder clang-c --cflags "-O2 -glldb -fno-inline" -- %s +// +//// Check that a pointer to a variable living on the stack dereferences to the +//// variable value. + +int glob; +void esc(int* p) { + glob = *p; + *p = 0xFF; +} + +int main() { + int local = 0xA; + int *plocal = &local; + esc(plocal); // DexLabel('s1') + local = 0xB; //// DCE + return 0; // DexLabel('s2') +} + +// DexExpectWatchValue('(local == *plocal)', 'true', from_line='s1', to_line='s2') Index: debuginfo-tests/dexter-tests/memvars/struct-dse.c =================================================================== --- /dev/null +++ debuginfo-tests/dexter-tests/memvars/struct-dse.c @@ -0,0 +1,28 @@ +// REQUIRES: lldb +// RUN: %dexter --fail-lt 1.0 -w --debugger lldb \ +// RUN: --builder clang-c --cflags "-O2 -glldb -fno-inline" -- %s +// +//// Check debug-info for the escaped struct variable num is reasonable. +//// Currently, LowerDbgDeclare doesn't lower dbg.declares pointing at allocas +//// for structs. + +#include +struct Nums { int a, b, c; }; +struct Nums glob; +void esc(struct Nums* nums) { + glob = *nums; +} + +int main() { + struct Nums nums = { .c=1 }; //// Dead store. + printf("s1 nums.c: %d\n", nums.c); // DexLabel('s1') + + nums.c = 2; //// Killing store. + printf("s2 nums.c: %d\n", nums.c); // DexLabel('s2') + + esc(&nums); //// Force nums to live on the stack. + return 0; // DexLabel('s3') +} + +// DexExpectWatchValue('nums.c', '1', on_line='s1') +// DexExpectWatchValue('nums.c', '2', from_line='s2', to_line='s3')