|
From: Paul S. <pa...@ma...> - 2014-04-10 17:26:22
|
Hi all; I'm working on GNU/Linux with GCC 4.8.2, binutils 2.23.2, and
Valgrind 1.9.0. I'm building my code (for this test) with -g and no
optimization.
For various reasons we are using an inlined allocation function that
itself calls malloc(). In order to ensure it works properly for all our
situations (including building as both static and shared libraries: we
don't want to export the inlined function as a weak symbol) we are
marking it with "inline __attribute__((always_inline))".
This is working fine, but there's one issue we've noticed so far: when
Valgrind prints the stack traceback in its output, it's always using the
filename and line number of the _inlined_ function and not the calling
function. The _name_ of the calling function is correct though.
So for example if we have this in MyAlloc.h:
inline __attribute__((always_inline)) void* MyAlloc(size_t len)
{
return malloc(len);
}
(obviously our inline function does a bit more than that!), then when I
get a memory loss stacktrace I see something like this:
==20930== 18,400 bytes in 23 blocks are possibly lost in loss record 496 of 528
==20930== at 0x4C2C85E: malloc (vg_replace_malloc.c:292)
==20930== by 0x6946E0: SparseArray<unsigned int, 200u>::getPtr(unsigned int)(MyAlloc.h:3)
==20930== by 0x693250: SparseArray<unsigned int, 200u>::set(unsigned int, unsigned int) (SparseArray.h:125)
==20930== by 0x68E5DD: ...
Note how the function name is correct for the caller of malloc
(getPtr()), BUT the filename/linenumber is for the MyAlloc.h inlined
function rather than where the inlined function was invoked. This
example happens to C++ templates but the same thing happens for
straightforward function calls.
This is very painful because that function might allocate memory in many
different places and I have no idea which one valgrind is talking about.
If use GDB and set a breakpoint on malloc, then the stacktrace shown by
GDB is correct: it shows a separate stack frame for the inlined
allocation function and then the "right" stack frame for the caller.
Any thoughts?
|
|
From: John R. <jr...@Bi...> - 2014-04-10 18:15:16
|
> So for example if we have this in MyAlloc.h:
>
> inline __attribute__((always_inline)) void* MyAlloc(size_t len)
> {
> return malloc(len);
> }
>
> (obviously our inline function does a bit more than that!), then when I
> get a memory loss stacktrace I see something like this:
>
> ==20930== 18,400 bytes in 23 blocks are possibly lost in loss record 496 of 528
> ==20930== at 0x4C2C85E: malloc (vg_replace_malloc.c:292)
> ==20930== by 0x6946E0: SparseArray<unsigned int, 200u>::getPtr(unsigned int)(MyAlloc.h:3)
> ==20930== by 0x693250: SparseArray<unsigned int, 200u>::set(unsigned int, unsigned int) (SparseArray.h:125)
> ==20930== by 0x68E5DD: ...
>
> Note how the function name is correct for the caller of malloc
> (getPtr()), BUT the filename/linenumber is for the MyAlloc.h inlined
> function rather than where the inlined function was invoked. This
> example happens to C++ templates but the same thing happens for
> straightforward function calls.
>
> This is very painful because that function might allocate memory in many
> different places and I have no idea which one valgrind is talking about.
>
> If use GDB and set a breakpoint on malloc, then the stacktrace shown by
> GDB is correct: it shows a separate stack frame for the inlined
> allocation function and then the "right" stack frame for the caller.
Please post the traceback that GDB shows. You have done an excellent job
at presenting your environment (thank you for the software versions!)
and the actual output that you see from memcheck. However I'm unsure
of exactly the output that you desire.
If you want
==20930== 18,400 bytes in 23 blocks are possibly lost in loss record 496 of 528
==20930== at 0x4C2C85E: malloc (vg_replace_malloc.c:292)
==20930== by 0x6946E0: MyAlloc (MyAlloc.h:3)
==20930== by 0x6946E0: SparseArray<unsigned int, 200u>::getPtr(unsigned int) (SparseArray.h:234)
==20930== by 0x693250: SparseArray<unsigned int, 200u>::set(unsigned int, unsigned int) (SparseArray.h:125)
==20930== by 0x68E5DD: ...
then there may be a problem with having the same return address for MyAlloc.h:3 and SparseArray.h:234
when only one actual subroutine CALL instruction is involved.
|
|
From: Paul S. <pa...@ma...> - 2014-04-10 19:38:11
|
On Thu, 2014-04-10 at 11:15 -0700, John Reiser wrote:
> > So for example if we have this in MyAlloc.h:
> >
> > inline __attribute__((always_inline)) void* MyAlloc(size_t len)
> > {
> > return malloc(len);
> > }
> >
> > (obviously our inline function does a bit more than that!), then when I
> > get a memory loss stacktrace I see something like this:
> >
> > ==20930== 18,400 bytes in 23 blocks are possibly lost in loss record 496 of 528
> > ==20930== at 0x4C2C85E: malloc (vg_replace_malloc.c:292)
> > ==20930== by 0x6946E0: SparseArray<unsigned int, 200u>::getPtr(unsigned int)(MyAlloc.h:3)
> > ==20930== by 0x693250: SparseArray<unsigned int, 200u>::set(unsigned int, unsigned int) (SparseArray.h:125)
> > ==20930== by 0x68E5DD: ...
> >
> > Note how the function name is correct for the caller of malloc
> > (getPtr()), BUT the filename/linenumber is for the MyAlloc.h inlined
> > function rather than where the inlined function was invoked.
>
> Please post the traceback that GDB shows.
This is what I get from GDB 7.6.1 (I don't have an example for this same
stack trace but you can see the format):
#0 malloc (size=24)
#1 0x0000000000672337 in MyAlloc (size=24) at MyAlloc.h:3
#2 MsgBody::appendHeader (this=0x7f66d57feb90, cNumber=64) at MsgBody.cpp:101
#3 0x000000000076b77c in MsgPing::append (this=0x7f66d57feb90, val=0x7f66d8c7f700) at MsgPing.cpp:27
#4 ...
where MsgBody.cpp:101 is an invocation of the MyAlloc() inline function.
It seems that GDB basically shows two lines at the same instruction
location, and doesn't prefix the second one with the location.
> If you want
> ==20930== 18,400 bytes in 23 blocks are possibly lost in loss record 496 of 528
> ==20930== at 0x4C2C85E: malloc (vg_replace_malloc.c:292)
> ==20930== by 0x6946E0: MyAlloc (MyAlloc.h:3)
> ==20930== by 0x6946E0: SparseArray<unsigned int, 200u>::getPtr(unsigned int) (SparseArray.h:234)
> ==20930== by 0x693250: SparseArray<unsigned int, 200u>::set(unsigned int, unsigned int) (SparseArray.h:125)
> ==20930== by 0x68E5DD: ...
> then there may be a problem with having the same return address for MyAlloc.h:3 and SparseArray.h:234
> when only one actual subroutine CALL instruction is involved.
If I could only get one frame and I had to choose whether I got the
filename/linenumber info from the caller or the inline function, I would
choose the caller, myself.
In my situation the inline function is small and calls malloc() exactly
once so I can easily infer what happens and if I didn't have that info I
would probably not even notice. But, I can imagine other situations
where the inline function was more complex and contained multiple
invocations of malloc itself, then not having the inline
filename/linenumber as well could be a bummer.
But I still think, overall, the caller is more helpful most of the
time :-).
Cheers!
|
|
From: Matthias S. <zz...@ge...> - 2014-04-12 08:56:21
|
On 10.04.2014 21:16, Paul Smith wrote:
> On Thu, 2014-04-10 at 11:15 -0700, John Reiser wrote:
>>> So for example if we have this in MyAlloc.h:
>>>
>>> inline __attribute__((always_inline)) void* MyAlloc(size_t len)
>>> {
>>> return malloc(len);
>>> }
>>>
>>> (obviously our inline function does a bit more than that!), then when I
>>> get a memory loss stacktrace I see something like this:
>>>
>>> ==20930== 18,400 bytes in 23 blocks are possibly lost in loss record 496 of 528
>>> ==20930== at 0x4C2C85E: malloc (vg_replace_malloc.c:292)
>>> ==20930== by 0x6946E0: SparseArray<unsigned int, 200u>::getPtr(unsigned int)(MyAlloc.h:3)
>>> ==20930== by 0x693250: SparseArray<unsigned int, 200u>::set(unsigned int, unsigned int) (SparseArray.h:125)
>>> ==20930== by 0x68E5DD: ...
>>>
>>> Note how the function name is correct for the caller of malloc
>>> (getPtr()), BUT the filename/linenumber is for the MyAlloc.h inlined
>>> function rather than where the inlined function was invoked.
>>
>> Please post the traceback that GDB shows.
>
> This is what I get from GDB 7.6.1 (I don't have an example for this same
> stack trace but you can see the format):
>
> #0 malloc (size=24)
> #1 0x0000000000672337 in MyAlloc (size=24) at MyAlloc.h:3
> #2 MsgBody::appendHeader (this=0x7f66d57feb90, cNumber=64) at MsgBody.cpp:101
> #3 0x000000000076b77c in MsgPing::append (this=0x7f66d57feb90, val=0x7f66d8c7f700) at MsgPing.cpp:27
> #4 ...
>
> where MsgBody.cpp:101 is an invocation of the MyAlloc() inline function.
> It seems that GDB basically shows two lines at the same instruction
> location, and doesn't prefix the second one with the location.
>
Hi!
Yes, new enough gdb versions know how to display inlined function calls.
I tried to look into this code but did not understand it.
addr2line can also do the same magic.
Until now I solved this by using addr2line. For resolving callstacks
printed by valgrind you need to know the offset between the load address
and the values in the original library (e.g. printed by nm).
This can either be guessed (by comparing the addresses of some functions
in valgrind stacks to what nm prints (regarding offset+size and assuming
the last 3 hex digits stay the same).
That means choosing a short function immediately gives the unambigous
offset.
Additionally I did a patch: Valgrind now writes these load offsets into
the output xml file.
Then I have a postprocessing script that basically subtracts the load
offset of each single address and calls addr2line.
But this is another very slow step (so I only do it on demand).
I can send the patch on monday.
I would be happy if valgrind could do the magic on its own.
Regards
Matthias
|
|
From: Paul S. <pa...@ma...> - 2014-04-12 23:16:35
|
On Sat, 2014-04-12 at 10:56 +0200, Matthias Schwarzott wrote:
> On 10.04.2014 21:16, Paul Smith wrote:
> > On Thu, 2014-04-10 at 11:15 -0700, John Reiser wrote:
> >>> So for example if we have this in MyAlloc.h:
> >>>
> >>> inline __attribute__((always_inline)) void* MyAlloc(size_t len)
> >>> {
> >>> return malloc(len);
> >>> }
> >>>
> >>> (obviously our inline function does a bit more than that!), then when I
> >>> get a memory loss stacktrace I see something like this:
> >>>
> >>> ==20930== 18,400 bytes in 23 blocks are possibly lost in loss record 496 of 528
> >>> ==20930== at 0x4C2C85E: malloc (vg_replace_malloc.c:292)
> >>> ==20930== by 0x6946E0: SparseArray<unsigned int, 200u>::getPtr(unsigned int)(MyAlloc.h:3)
> >>> ==20930== by 0x693250: SparseArray<unsigned int, 200u>::set(unsigned int, unsigned int) (SparseArray.h:125)
> >>> ==20930== by 0x68E5DD: ...
> >>>
> >>> Note how the function name is correct for the caller of malloc
> >>> (getPtr()), BUT the filename/linenumber is for the MyAlloc.h inlined
> >>> function rather than where the inlined function was invoked.
> >>
> >> Please post the traceback that GDB shows.
> >
> > This is what I get from GDB 7.6.1 (I don't have an example for this same
> > stack trace but you can see the format):
> >
> > #0 malloc (size=24)
> > #1 0x0000000000672337 in MyAlloc (size=24) at MyAlloc.h:3
> > #2 MsgBody::appendHeader (this=0x7f66d57feb90, cNumber=64) at MsgBody.cpp:101
> > #3 0x000000000076b77c in MsgPing::append (this=0x7f66d57feb90, val=0x7f66d8c7f700) at MsgPing.cpp:27
> > #4 ...
>
> Yes, new enough gdb versions know how to display inlined function calls.
> I tried to look into this code but did not understand it.
> addr2line can also do the same magic.
It would be super-nice if this knowledge were abstracted into a separate
library that was useful and usable by other utilities, such as valgrind.
I don't see why everyone should have to roll their own. Although, I'm
not familiar with how the Valgrind VM works so maybe it's not
sufficiently similar to allow use of the same code as gdb/binutils?
> I can send the patch on monday.
>
> I would be happy if valgrind could do the magic on its own.
Boy, me too! :-).
I don't have the time to learn enough about Valgrind to attempt a fix
myself but I'm willing to test any patches, etc. anyone wants to send
along. And I have enough programming fu to be able to, if not run, then
at least amble with a proposed patch on my own.
|
|
From: Matthias S. <zz...@ge...> - 2014-04-16 05:20:33
Attachments:
valgrind-write-library-load-address-xml.patch
|
On 13.04.2014 01:16, Paul Smith wrote: >> I can send the patch on monday. >> The attached patch writes the library load addresses to the xml output. They can then be used to feed the contained addresses to addr2line. Regards Matthias |
|
From: Philippe W. <phi...@sk...> - 2014-06-16 21:45:08
|
On Sat, 2014-04-12 at 19:16 -0400, Paul Smith wrote: > > I would be happy if valgrind could do the magic on its own. Recent SVN version are doing the magic > > Boy, me too! :-). > > I don't have the time to learn enough about Valgrind to attempt a fix > myself but I'm willing to test any patches, etc. anyone wants to send > along. And I have enough programming fu to be able to, if not run, then > at least amble with a proposed patch on my own. Test and feedback welcome (you need to get and build an SVN version). Thanks Philippe |