Dbx tip of the day - tracing malloc/free or new/delete
On comp.unix.solaris, someone asked:I want to set a breakpoint such that it will stop when some specific heap address, say, 0x12345678, is allocated or freed. Is there any way to do this?After I replied an answer, someone else asked me how to do similar for new/delete in C++. It isn't too difficult to do, but it involves optimized code (malloc and free in libc are optimized), so it may not be obvious to someone who's not familiar with SPARC assembly or calling convention.
Enough intro. Here's my answers:
One possible solution is to set the breakpoint at just before returning from malloc, and right after free is called. Following is an example: $ cat t.c #includeUPDATE#include int main(void) { void * p = malloc(100); printf("%x\n", p); free(p); return 0; } $ cc -g t.c $ dbx ./a.out Reading a.out Reading ld.so.1 Reading libc.so.1 Reading libdl.so.1 Reading libc_psr.so.1 (dbx 1) stop in main (2) stop in main (dbx 2) run Running: a.out (process id 14122) stopped in main at line 5 in file "t.c" 5 void * p = malloc(100); (dbx 3) dis malloc 0x7fac1cb8: malloc : save %sp, -96, %sp 0x7fac1cbc: malloc+0x0004: call malloc+0xc ! 0x7fac1cc4 0x7fac1cc0: malloc+0x0008: sethi %hi(0x7a000), %o1 0x7fac1cc4: malloc+0x000c: inc 844, %o1 0x7fac1cc8: malloc+0x0010: add %o1, %o7, %o3 0x7fac1ccc: malloc+0x0014: ld [%o3 + 3788], %l0 0x7fac1cd0: malloc+0x0018: call _PROCEDURE_LINKAGE_TABLE_+0x3c 0x7fac1cd4: malloc+0x001c: mov %l0, %o0 0x7fac1cd8: malloc+0x0020: call _malloc_unlocked ! 0x7fac1cf4 0x7fac1cdc: malloc+0x0024: mov %i0, %o0 (dbx 4) dis 0x7fac1ce0: malloc+0x0028: mov %o0, %i0 0x7fac1ce4: malloc+0x002c: call _PROCEDURE_LINKAGE_TABLE_+0x48 0x7fac1ce8: malloc+0x0030: mov %l0, %o0 0x7fac1cec: malloc+0x0034: ret 0x7fac1cf0: malloc+0x0038: restore 0x7fac1cf4: _malloc_unlocked : save %sp, -96, %sp 0x7fac1cf8: _malloc_unlocked+0x0004: sethi %hi(0xffffdc00), %o0 0x7fac1cfc: _malloc_unlocked+0x0008: call _malloc_unlocked+0x10 0x7fac1d00: _malloc_unlocked+0x000c: sethi %hi(0x7a000), %o1 0x7fac1d04: _malloc_unlocked+0x0010: inc 999, %o0 (dbx 5) stopi at 0x7fac1cec -if $i0 == 0x20e38 (3) stopi at &malloc+0x34 -if $i0 == 0x20e38 (dbx 6) stop in free -if $o0 == 0x20e38 dbx: warning: 'free' has no debugger info -- will trigger on first instruction (4) stop in free -if $o0 == 0x20e38 (dbx 7) cont stopped in malloc at 0x7fac1cec 0x7fac1cec: malloc+0x0034: ret (dbx 8) up Current function is main 5 void * p = malloc(100); (dbx 9) cont 20e38 stopped in free at 0x7fac2b4c 0x7fac2b4c: free : save %sp, -96, %sp Current function is main 9 free(p); (dbx 10) cont execution completed, exit code is 0 (dbx 11) The first stopi point is at the "ret" instruction of malloc, and stops only when $i0 (the return value, %i0 in disassembly) is the value you want (in this case, 0x20e38). The second stop point is at the first instruction of free (stop in free) and only if $o0 (the first parameter) is the value you want. (dbx 8) promt caught malloc() from returning 0x20e38, and (dbx 10) caught free() being passed 0x20e38. Depending on what you want to do, apptrace might be useful also. with the same a.out: $ apptrace ./a.out apptrace: unexpected version: 3 a.out -> libc.so.1:atexit(func = 0x7fbaeaa8) = 0x0 a.out -> libc.so.1:atexit(func = 0x10c98) = 0x0 a.out -> libc.so.1:malloc(size = 0x64) = 0x20e38 a.out -> libc.so.1:printf(20e38 format = 0x10cb0, ...) = 6 a.out -> libc.so.1:free(ptr = 0x20e38) a.out -> libc.so.1:exit(status = 0) $ And of course, Dtrace in Solaris 10 can do what apptrace can do (and more). Another useful tool is dbx's rtc checking (if that's what you're trying to do). Just do: (dbx 1) check -access And it will do bunch of different runtime error checkings for memory allocation. Type "help check" on dbx command line to see more detail. > Can I do similar things to new and delete? New and delete calls malloc and free. So you'll see some additional calls on top of malloc and free but it's basically the same: $ cat t.c int main(void) { int * p = new int; *p = 10; delete p; return 0; } $ CC -g t.c $ dbx ./a.out Reading a.out Reading ld.so.1 Reading libCstd.so.1 Reading libCrun.so.1 Reading libm.so.1 Reading libc.so.1 Reading libdl.so.1 Reading libCstd_isa.so.1 Reading libc_psr.so.1 (dbx 1) si main (2) stop in main (dbx 2) r Running: a.out (process id 16911) stopped in main at line 2 in file "t.c" 2 int * p = new int; (dbx 3) si malloc dbx: warning: 'malloc' has no debugger info -- will trigger on first instruction (3) stop in malloc (dbx 4) c stopped in malloc at 0x7f8c1cb8 0x7f8c1cb8: malloc : save %sp, -96, %sp Current function is main 2 int * p = new int; (dbx 5) where [1] malloc(0x4, 0x0, 0x0, 0x0, 0x0, 0x0), at 0x7f8c1cb8 [2] operator new(0x4, 0x7f93c008, 0x13988, 0xb00, 0x7f9eae3c, 0x4), at 0x7f9d74d8 =>[3] main(), line 2 in "t.c" (dbx 6) si free More than one identifier 'free'. Select one of the following: 0) Cancel 1) `libc.so.1`free 2) `libCstd.so.1`#__1cDstdIvalarray4Cl_Efree6M_v_ [non -g, demangles to: std::valarray ::free()] 3) `libCstd.so.1`#__1cDstdIvalarray4Ci_Efree6M_v_ [non -g, demangles to: std::valarray ::free()] 4) `libCstd.so.1`#__1cDstdIvalarray4CL_Efree6M_v_ [non -g, demangles to: std::valarray ::free()] 5) `libCstd.so.1`#__1cDstdIvalarray4CI_Efree6M_v_ [non -g, demangles to: std::valarray ::free()] a) All > 1 dbx: warning: 'free' has no debugger info -- will trigger on first instruction (4) stop in free (dbx 8) cont stopped in free at 0x7f8c2b4c 0x7f8c2b4c: free : save %sp, -96, %sp Current function is main 4 delete p; (dbx 9) where [1] free(0x28620, 0x0, 0x0, 0x0, 0x0, 0x0), at 0x7f8c2b4c [2] operator delete(0x28620, 0x7f93c008, 0x13988, 0xb00, 0x7f9eae3c, 0x4), at 0x7f9d65b8 =>[3] main(), line 4 in "t.c" (dbx 10) Or an alternative is to set a breakint at constructor/destructor that you want to track: (dbx) stop in YourClass::YourClass -if this == ...pointer value... (dbx) stop in YourClass::~YourClass $ cat t.c #include class myclass { public: myclass(void) { printf("I'm here.\n"); }; ~myclass(void) { printf("I'm gone.\n"); }; }; int main(void) { myclass *p = new myclass(); delete p; return 0; } $ CC -g t.c $ dbx ./a.out Reading a.out Reading ld.so.1 Reading libCstd.so.1 Reading libCrun.so.1 Reading libm.so.1 Reading libc.so.1 Reading libdl.so.1 Reading libCstd_isa.so.1 Reading libc_psr.so.1 (dbx 1) si myclass::myclass (2) stop in myclass::myclass() (dbx 2) si myclass::~myclass (3) stop in myclass::~myclass() (dbx 3) r Running: a.out (process id 16929) stopped in myclass::myclass at line 4 in file "t.c" 4 myclass(void) { printf("I'm here.\n"); }; (dbx 4) w =>[1] myclass::myclass(this = 0x28838), line 4 in "t.c" [2] main(), line 8 in "t.c" (dbx 5) c I'm here. stopped in myclass::~myclass at line 5 in file "t.c" 5 ~myclass(void) { printf("I'm gone.\n"); }; (dbx 6) w =>[1] myclass::~myclass(this = 0x28838), line 5 in "t.c" [2] main(), line 10 in "t.c" (dbx 7) stop in myclass::myclass -if this == 0x28838 (4) stop in myclass::myclass() -if this == 0x28838 (dbx 8) stop in myclass::~myclass -if this == 0x28838 (5) stop in myclass::~myclass() -if this == 0x28838 (dbx 9) status (2) stop in myclass::myclass() *(3) stop in myclass::~myclass() (4) stop in myclass::myclass() -if this == 0x28838 (5) stop in myclass::~myclass() -if this == 0x28838 (dbx 10) handler -disable 2 3 (dbx 11) run Running: a.out (process id 16930) stopped in myclass::myclass at line 4 in file "t.c" 4 myclass(void) { printf("I'm here.\n"); }; (dbx 12) where =>[1] myclass::myclass(this = 0x28838), line 4 in "t.c" [2] main(), line 8 in "t.c" (dbx 13) c I'm here. stopped in myclass::~myclass at line 5 in file "t.c" 5 ~myclass(void) { printf("I'm gone.\n"); }; (dbx 14) where =>[1] myclass::~myclass(this = 0x28838), line 5 in "t.c" [2] main(), line 10 in "t.c" (dbx 15)
Looks like I learn new things whenever I blog something - our dbx guy told me about this dbx feature "stop returns" which effectively sets a breakpoint at all places that call the function (in reality, it will wait till the function is called and set a breapoint at the return address).
Anyway, if you use "stop returns" then $o0 will have the return address of malloc since the brekapoint will be at the call site, whereas in the above example, I set the breapoint at the return instruction so the return value is in $i0.
( Sep 13 2004, 11:10:09 AM PDT ) Permalink
Comments:
Post a Comment:
Comments are closed for this entry.

