How to get a line-numbered stack trace for a memory leak on Mac OS X?
I've managed to get the Xcode leaks
tool to report leaks in my command-line GCC Ada program (by adding delay 11.0;
at the end to let leaks
make its checks) and then
$ export MallocStackLogging=1
$ (./foobar &) && leaks foobar
which leads to (excerpted)
Process 52027: 18 nodes malloced for 2053 KB
Process 52027: 2 leaks for 32 total leaked bytes.
Leak: 0x1002000c0 size=16 zone: DefaultMallocZone_0x100175000 string '*'
Call stack: [thread 0x7fff70bbcca0]: | start | main | _ada_foobar | __gnat_malloc | malloc | malloc_zone_malloc
Leak: 0x1002000d0 size=16 zone: DefaultMallocZone_0x100175000 string 'T'
Call stack: [thread 0x7fff70bbcca0]: | start | main | _ada_foobar | __gnat_malloc | malloc | malloc_zone_malloc
which is a great deal better than nothing, 开发者_如何学Pythonbut would be considerably improved with line numbers.
Are there any build options I should have used? Would it work better if the Ada compiler (FSF GCC 4.6.0, not from Apple) was integrated with Xcode?
This is an x86_64 build on 10.6.7, Xcode 3.2.6. Using -g makes no difference.
In the call stack, main
is the main()
generated by gnatmake
, _ada_foobar
is the Ada program in which the leak actually occurs. The other frames are from the run time system.
Well, the chief issue here is exactly which compiler you have. The versions of Gnat you get from ACT come with a link libraray addr2line.lib that can be used to produce symbolic tracebacks at runtime.
The versions of Gnat you get from the FSF distributions don't have that library. However, there is still the addr2line program that comes with Gnu's "binutils". You do have that available for you on your Mac setup, right?
If you feed your hex addresses into that program, it should report the symbolic information you want. You may have to reformat your leaks
output a bit for addr2line to accept it.
Valgrind 3.7.0 is available for Mac OS X; my code test was
with Ada.Text_IO; use Ada.Text_IO;
procedure Leaker is
type P is access Integer;
procedure Inner is
V : P;
begin
Put_Line ("allocating for 42");
V := new Integer'(42);
Put_Line ("allocating for 0");
V := new Integer'(0);
Put_Line ("done.");
end Inner;
begin
Inner;
end Leaker;
and I ran with
valgrind --leak-check=full --dsymutil=yes ./leaker
to get a report including
8 bytes in 2 blocks are definitely lost in loss record 2 of 9
at 0xB823: malloc (vg_replace_malloc.c:266)
by 0x100010CC7: __gnat_malloc (s-memory.adb:92)
by 0x100001C7D: _ada_leaker (leaker.adb:14)
by 0x100001BAA: main (b~leaker.adb:191)
leaker.adb:14
is the call to Inner
.
Although less dispositive than valgrind
, illustrated here, it's possible to Spawn
an instance of leaks
without delay
as shown below. Spawn
blocks, allowing leaks
to conclude before proceeding. In the best case, it's a quick way to verify that no leaks were found; if not, paired calls can narrow the search for offending code.
Code:
with Ada.Text_IO; use Ada.Text_IO;
with GNAT.OS_Lib; use GNAT.OS_Lib;
procedure Leaker is
type P is access Integer;
procedure Inner is
V : P;
begin
Put_Line ("allocating for 42");
V := new Integer'(42);
Put_Line ("allocating for 0");
V := new Integer'(0);
Put_Line ("done.");
end Inner;
Result : Integer;
Command : constant String := "/usr/bin/leaks";
Arguments : constant Argument_List :=
(1 => new String'("-quiet"), 2 => new String'("leaker"));
begin
Result := Spawn (Command, Arguments);
Inner;
Result := Spawn (Command, Arguments);
end Leaker;
Console:
leaks Report Version: 4.0
Process 46711: 156 nodes malloced for 8 KB
Process 46711: 0 leaks for 0 total leaked bytes.
allocating for 42
allocating for 0
done.
leaks Report Version: 4.0
Process 46711: 158 nodes malloced for 8 KB
Process 46711: 2 leaks for 32 total leaked bytes.
2 (32 bytes) << TOTAL >>
1 (16 bytes) ROOT LEAK: 0x7f8f83504080 [16] length: 1 "*"
1 (16 bytes) ROOT LEAK: 0x7f8f83504090 [16]
精彩评论