Example:
#include <assert.h>
int v = 0;
struct Foo {
Foo() { ++v; }
~Foo()
{
--v;
assert(v != 1); // artificially stop the debugger here
}
};
int main()
{
Foo a; // assert in destructor here?
Foo b; // assert in destructor here?
Foo c; // assert in destructor here?
return 0;
} // main()
This code asserts. I think the debugger should be able to tell me which line/instance of Foo is asserting, but it only shows that it's exiting the scope of main():
#6 0x00007ffff7d9ee96 in __GI___assert_fail (assertion=0x555555556019 "v != 1", file=0x555555556010 "main.cpp", line=8,
function=0x555555556004 "Foo::~Foo()") at ./assert/assert.c:101
#7 0x000055555555521c in Foo::~Foo (this=<optimized out>, __in_chrg=<optimized out>) at main.cpp:8
#8 0x00005555555551b6 in main () at main.cpp:17
(gdb) f 8
#8 0x00005555555551b6 in main () at main.cpp:17
17 } // main()
I know the culprit is b, but how can I make gcc tell me?
Is this just a missing feature or is there somethig more fundamental preventing debuggers from showing this information?
If I add a scope for each instance it becomes clear, but I don't think I should have to recompile.
int main()
{
Foo a;
{
Foo b;
{
Foo c;
}
} // <-- backtrace of the assert shows this line
return 0;
}
The same problem exists for members of a class:
class Bar {
Foo a;
Foo b;
Foo c;
};
[EDIT] What I'd really like to see is the debugger stepping back to the line with Foo c; when it returns from main(), then Foo b; etc. Just like what happens in the following example, where the debugger steps through the Baz() lines one at a time before finally entering func().
func(Baz(),
Baz(),
Baz());
but how can I make gcc tell me?
You can't (at least not directly) -- it has no idea that you are interested in this. But you can give each instance of Foo a name it to print the value of this and the name in both constructor and destructor.
You could also get this info from GDB:
Program received signal SIGABRT, Aborted.
__pthread_kill_implementation (threadid=<optimized out>, signo=6, no_tid=no_tid#entry=0) at ./nptl/pthread_kill.c:44
44 ./nptl/pthread_kill.c: No such file or directory.
(gdb) bt
#0 __pthread_kill_implementation (threadid=<optimized out>, signo=6, no_tid=no_tid#entry=0) at ./nptl/pthread_kill.c:44
#1 0x00007ffff7e55d2f in __pthread_kill_internal (signo=<optimized out>, threadid=<optimized out>) at ./nptl/pthread_kill.c:89
#2 __GI___pthread_kill (threadid=<optimized out>, signo=<optimized out>) at ./nptl/pthread_kill.c:89
#3 0x00007ffff7f9ee70 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#4 0x00007ffff7df1472 in __GI_abort () at ./stdlib/abort.c:79
#5 0x00007ffff7df1395 in __assert_fail_base (fmt=<optimized out>, assertion=<optimized out>, file=<optimized out>, line=<optimized out>, function=<optimized out>) at ./assert/assert.c:92
#6 0x00007ffff7dffdf2 in __GI___assert_fail (assertion=0x555555556015 "v != 1", file=0x555555556010 "t.cc", line=10, function=0x555555556004 "Foo::~Foo()") at ./assert/assert.c:101
#7 0x0000555555555200 in Foo::~Foo (this=0x7fffffffd81e, __in_chrg=<optimized out>) at t.cc:10
#8 0x0000555555555183 in main () at t.cc:20
(gdb) fr 8
#8 0x0000555555555183 in main () at t.cc:20
20 } // main()
(gdb) info locals
a = {<No data fields>}
b = {<No data fields>}
c = {<No data fields>}
(gdb) p &a
$1 = (Foo *) 0x7fffffffd81f
(gdb) p &b
$2 = (Foo *) 0x7fffffffd81e <<=== match for this in frame 7
This of course requires that this isn't optimized (as it is in your stack), or that you print the value of this in the destructor.
[EDIT] What I'd really like to see is the debugger stepping back to the line with Foo c; when it returns from main(), then Foo b; etc.
A compiler could in theory do that, but (I suspect) this will be exceedingly confusing to most end-users: "why is the destructor being invoked when I am simply constructing Foo?".
Related
I am getting a seg fault trigger when the destructor below destroys it's vector elements. Originally it was a vector<Parent> but I changed this to vector<unique_ptr<Parent>> and since then the crash occurs every time:
class Owner
{
Owner() = default;
~Owner() = default; // Seg faults
void assignVec(std::vector<std::unique_ptr<Parent>>& vec)
{
_vec = std::move(vec);
}
std::vector<std::unique_ptr<Parent>> _vec;
};
Each vector element subtype is a polymorphic class, also inheriting from boost::python::wrapper
class Child: public Parent, public boost::python::wrapper<Parent>
{
Child();
virtual ~Child() = default;
};
where:
class Parent
{
Parent() = default;
virtual ~Parent() = default;
};
So the entire inheritance hierarchy does have virtual destructors.
GDB backtrace is showing:
#0 0x00007ffff636b207 in __GI_raise (sig=sig#entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:55
#1 0x00007ffff636c8f8 in __GI_abort () at abort.c:90
#2 0x00007ffff63add27 in __libc_message (do_abort=do_abort#entry=2, fmt=fmt#entry=0x7ffff64bf678 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/unix/sysv/linux/libc_fatal.c:196
#3 0x00007ffff63b6489 in malloc_printerr (ar_ptr=0x7ffff66fb760 <main_arena>, ptr=<optimized out>, str=0x7ffff64bcd31 "free(): invalid pointer", action=3) at malloc.c:5004
#4 _int_free (av=0x7ffff66fb760 <main_arena>, p=<optimized out>, have_lock=0) at malloc.c:3843
#5 0x00007fffc373972f in Child::~Child (this=0x2742b10, __in_chrg=<optimized out>) at Child.h:23
#6 0x000000000045694e in std::default_delete<Parent>::operator() (this=0x11922e0, __ptr=0x2742b10) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/unique_ptr.h:81
#7 0x0000000000454c27 in std::unique_ptr<Parent, std::default_delete<Parent> >::~unique_ptr (this=0x11922e0, __in_chrg=<optimized out>) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/unique_ptr.h:274
#8 0x000000000045a882 in std::_Destroy<std::unique_ptr<Parent, std::default_delete<Parent> > > (__pointer=0x11922e0) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_construct.h:98
#9 0x0000000000458f67 in std::_Destroy_aux<false>::__destroy<std::unique_ptr<Parent, std::default_delete<Parent> >*> (__first=0x11922e0, __last=0x11922e8) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_construct.h:108
#10 0x0000000000457636 in std::_Destroy<std::unique_ptr<Parent, std::default_delete<Parent> >*> (__first=0x11922e0, __last=0x11922e8) at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_construct.h:137
#11 0x000000000045584d in std::_Destroy<std::unique_ptr<Parent, std::default_delete<Parent> >*, std::unique_ptr<Parent, std::default_delete<Parent> > > (__first=0x11922e0, __last=0x11922e8)
at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_construct.h:206
#12 0x000000000049b53d in std::vector<std::unique_ptr<Parent, std::default_delete<Parent> >, std::allocator<std::unique_ptr<Parent, std::default_delete<Parent> > > >::~vector (this=0x7fffffffc4a8, __in_chrg=<optimized out>)
at /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_vector.h:567
#13 0x000000000048c677 in Owner::~Owner (this=0x7fffffffc4a8, __in_chrg=<optimized out>)
Printing this in frame 5 does show a valid object. Frame 4 source code of free() is:
static void _int_free(mstate av, mchunkptr p, int have_lock)
{
INTERNAL_SIZE_T size; /* its size */
mfastbinptr* fb; /* associated fastbin */
mchunkptr nextchunk; /* next contiguous chunk */
INTERNAL_SIZE_T nextsize; /* its size */
int nextinuse; /* true if nextchunk is used */
INTERNAL_SIZE_T prevsize; /* size of previous contiguous chunk */
mchunkptr bck; /* misc temp for linking */
mchunkptr fwd; /* misc temp for linking */
const char *errstr = NULL;
int locked = 0;
size = chunksize(p);
/* Little security check which won't hurt performance: the
allocator never wrapps around at the end of the address space.
Therefore we can exclude some size values which might appear
here by accident or by "design" from some intruder. */
if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
|| __builtin_expect (misaligned_chunk (p), 0))
{
errstr = "free(): invalid pointer";
errout:
if (have_lock || locked)
(void)mutex_unlock(&av->mutex);
malloc_printerr (check_action, errstr, chunk2mem(p), av); // CRASHES HERE
Does anyone have any advice how to proceed debugging this?
UPDATE:
I have created a small example in a unit test, creating Owner and a vector, calling assignVec() and the problem does not occur. However, once the vector is passed in, nothing else obtains the Parent memory.
UPDATE2:
We think the problem is with boost python needing to be informed of the smart pointer. Apparently Boost Python doesn't support unique_ptr and we're struggling to get it to recognize shared_ptr (both Boost and std), even using the typical register technique.
Based on the code you are giving and with the assumption boost::python has not a bug here I would guess your usage of moving semantics might be the cause:
void assignVec(std::vector<std::unique_ptr<Parent>>& vec)
{
_vec = std::move(vec);
}
Here you are moving from a l-value-reference to a vector, vector& to your member. Problem is: Usually one only moves from r-values references (vector&&) as they ca not accessed any more, because the only bind to temporaries or if the one explicitly acknowledges the moving by creating an r/x-values reference like you did by using std::move.
Problem is: I bet you caller of assignVec might not be aware of this, because then why did you not already use a r-value reference in the signature, so that the caller has to std::move explicitly? My assumption is, you caller does not, and is doing more than the one thing that is legal to moved from values: Destruct them.
Of course you ask yourself, why does it break then in the destructor? In my experience segmentation faults occur some expression after the cause, in this case I would say the undefined behaviour of the caller of assignVec by still using the vector given to assignVec.
I have got one crash. and I use gdb to analyze the stack,I got the below result.
13 0x00007f423c6e9670 in ?? ()
#14 0x00007f42340496d8 in ?? ()
#15 0x0000000003cef568 in ?? ()
#16 0x00000000008da861 in HuffmanEnd ()
#17 0x00000000008d4a83 in faacEncClose ()
#18 0x00000000004fd797 in RecorderSession::~RecorderSession (this=0x7f423404ea90, __in_chrg=<value optimized out>)
at /root/Desktop/VideoRecoder/2.0/src/videorecorder/RecorderSession.cpp:203
#19 0x00000000004fdae9 in RecorderSession::~RecorderSession (this=0x7f423404ea90, __in_chrg=<value optimized out>)
at /root/Desktop/VideoRecoder/2.0/src/videorecorder/RecorderSession.cpp:203
#20 0x0000000000500d0b in RecorderSession::OnHangup (this=0x7f423404ea90) at /root/Desktop/VideoRecoder/2.0/src/videorecorder/RecorderSession.cpp:295
#21 0x000000000045e083 in CSipPhone::on_call_state (call_id=2, e=<value optimized out>)
As we see, the crash happens in the HuffmanEnd. But I don't understand why the ~RecorderSession is called twice although I use code "delete this" to delete the RecorderSession object as below:
int RecorderSession::OnHangup()
{
delete this;
return 0;
}
So does the "delete this" cause this phenomenon?
The chances are that your function OnHangup itself is already being called from the destructor of the object in question. Thus you are calling delete on yourself when the object is already in the middle of being destroyed, causing the double delete.
It seems your object is created by placement new, or as a local object on the stack, or as a namespace-scope / global, or as a member of another object.
In that case Dtor will be called one more time.
I am trying to use std::shared_ptr to point to the data being produced by one thread and consumed by another. The storage field is a shared pointer to the base class,
Here's the simplest Google Test I could create that reproduced the problem:
#include "gtest/gtest.h"
#include <thread>
struct A
{
virtual ~A() {}
virtual bool isSub() { return false; }
};
struct B : public A
{
bool isSub() override { return true; }
};
TEST (SharedPointerTests, threadedProducerConsumer)
{
int loopCount = 10000;
shared_ptr<A> ptr;
thread producer([loopCount,&ptr]()
{
for (int i = 0; i < loopCount; i++)
ptr = make_shared<B>(); // <--- THREAD
});
thread consumer([loopCount,&ptr]()
{
for (int i = 0; i < loopCount; i++)
shared_ptr<A> state = ptr; // <--- THREAD
});
producer.join();
consumer.join();
}
When run, sometimes gives:
[ RUN ] SharedPointerTests.threadedProducerConsumer
pure virtual method called
terminate called without an active exception
Aborted (core dumped)
GDB shows the crash with two threads at the locations shown. The stacks follow:
Stack 1
#0 0x00000000006f430a in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release (this=0x7fffe00008c0)
at /usr/include/c++/4.8/bits/shared_ptr_base.h:144
#1 0x00000000006f26a7 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count (this=0x7fffdf960bc8,
__in_chrg=<optimized out>) at /usr/include/c++/4.8/bits/shared_ptr_base.h:553
#2 0x00000000006f1692 in std::__shared_ptr<A, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr (this=0x7fffdf960bc0,
__in_chrg=<optimized out>) at /usr/include/c++/4.8/bits/shared_ptr_base.h:810
#3 0x00000000006f16ca in std::shared_ptr<A>::~shared_ptr (this=0x7fffdf960bc0, __in_chrg=<optimized out>)
at /usr/include/c++/4.8/bits/shared_ptr.h:93
#4 0x00000000006e7288 in SharedPointerTests_threadedProducerConsumer_Test::__lambda2::operator() (__closure=0xb9c940)
at /home/drew/dev/SharedPointerTests.hh:54
#5 0x00000000006f01ce in std::_Bind_simple<SharedPointerTests_threadedProducerConsumer_Test::TestBody()::__lambda2()>::_M_invoke<>(std::_Index_tuple<>) (this=0xb9c940) at /usr/include/c++/4.8/functional:1732
#6 0x00000000006efe13 in std::_Bind_simple<SharedPointerTests_threadedProducerConsumer_Test::TestBody()::__lambda2()>::operator()(void) (
this=0xb9c940) at /usr/include/c++/4.8/functional:1720
#7 0x00000000006efb7c in std::thread::_Impl<std::_Bind_simple<SharedPointerTests_threadedProducerConsumer_Test::TestBody()::__lambda2()> >::_M_run(void) (this=0xb9c928) at /usr/include/c++/4.8/thread:115
#8 0x00007ffff6d19ac0 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#9 0x00007ffff717bf8e in start_thread (arg=0x7fffdf961700) at pthread_create.c:311
#10 0x00007ffff647ee1d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:113
Stack 2
#0 0x0000000000700573 in std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<B, std::allocator<B>, (__gnu_cxx::_Lock_policy)2> > >::_S_destroy<std::_Sp_counted_ptr_inplace<B, std::allocator<B>, (__gnu_cxx::_Lock_policy)2> > (__a=..., __p=0x7fffe00008f0)
at /usr/include/c++/4.8/bits/alloc_traits.h:281
#1 0x00000000007003b6 in std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<B, std::allocator<B>, (__gnu_cxx::_Lock_policy)2> > >::destroy<std::_Sp_counted_ptr_inplace<B, std::allocator<B>, (__gnu_cxx::_Lock_policy)2> > (__a=..., __p=0x7fffe00008f0)
at /usr/include/c++/4.8/bits/alloc_traits.h:405
#2 0x00000000006ffe76 in std::_Sp_counted_ptr_inplace<B, std::allocator<B>, (__gnu_cxx::_Lock_policy)2>::_M_destroy (
this=0x7fffe00008f0) at /usr/include/c++/4.8/bits/shared_ptr_base.h:416
#3 0x00000000006f434c in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release (this=0x7fffe00008f0)
at /usr/include/c++/4.8/bits/shared_ptr_base.h:161
#4 0x00000000006f26a7 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count (this=0x7fffe8161b68,
__in_chrg=<optimized out>) at /usr/include/c++/4.8/bits/shared_ptr_base.h:553
#5 0x00000000006f16b0 in std::__shared_ptr<A, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr (this=0x7fffe8161b60,
__in_chrg=<optimized out>) at /usr/include/c++/4.8/bits/shared_ptr_base.h:810
#6 0x00000000006f4c3f in std::__shared_ptr<A, (__gnu_cxx::_Lock_policy)2>::operator=<B>(std::__shared_ptr<B, (__gnu_cxx::_Lock_policy)2>&&) (this=0x7fffffffdcb0, __r=<unknown type in /home/drew/dev/unittests, CU 0x0, DIE 0x58b8c>)
at /usr/include/c++/4.8/bits/shared_ptr_base.h:897
#7 0x00000000006f2d2a in std::shared_ptr<A>::operator=<B>(std::shared_ptr<B>&&) (this=0x7fffffffdcb0,
__r=<unknown type in /home/drew/dev/unittests, CU 0x0, DIE 0x55e1c>)
at /usr/include/c++/4.8/bits/shared_ptr.h:299
#8 0x00000000006e7232 in SharedPointerTests_threadedProducerConsumer_Test::__lambda1::operator() (__closure=0xb9c7a0)
at /home/drew/dev/SharedPointerTests.hh:48
#9 0x00000000006f022c in std::_Bind_simple<SharedPointerTests_threadedProducerConsumer_Test::TestBody()::__lambda1()>::_M_invoke<>(std::_Index_tuple<>) (this=0xb9c7a0) at /usr/include/c++/4.8/functional:1732
#10 0x00000000006efe31 in std::_Bind_simple<SharedPointerTests_threadedProducerConsumer_Test::TestBody()::__lambda1()>::operator()(void) (
this=0xb9c7a0) at /usr/include/c++/4.8/functional:1720
#11 0x00000000006efb9a in std::thread::_Impl<std::_Bind_simple<SharedPointerTests_threadedProducerConsumer_Test::TestBody()::__lambda1()> >::_M_run(void) (this=0xb9c788) at /usr/include/c++/4.8/thread:115
#12 0x00007ffff6d19ac0 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#13 0x00007ffff717bf8e in start_thread (arg=0x7fffe8162700) at pthread_create.c:311
#14 0x00007ffff647ee1d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:113
I have tried various approaches here, including using std::dynamic_pointer_cast but I haven't had any luck.
In reality the producer stores many different subclasses of A by their type_id in a std::map<type_id const*,std::shared_ptr<A>> (one instance per type) which I look up from the consumer by type.
My understanding is that std::shared_ptr is threadsafe for these types of operations. What am I missing?
shared_ptr has thread-safety on its control block. When a shared_ptr is created and points to a newly created resource it creates a control block. According to MSDN this holds:
The shared_ptr objects that own a resource share a control block. The control block holds:
the number of shared_ptr objects that own the resource,
the number of weak_ptr objects that point to the resource,
the deleter for that resource if it has one,
the custom allocator for the control block if it has one.
This means that shared_ptr will ensure that there are no synchronization issues with multiple copies of shared_ptr pointing to the same memory. However, it does not manage the synchronization of the memory itself. See the section on thread safety (emphasis mine)
Multiple threads can read and write different shared_ptr objects at the same time, even when the objects are copies that share ownership.
Your code shares ptr which means you have a data race. Also note that it is possible for your producer thread to produce several objects before the consumer thread is scheduled to run, meaning that you lose some objects.
As has been pointed out in a comment, you can use atomic operations on shared_ptr. The producer thread then looks like:
thread producer([loopCount,&ptr]()
{
for (int i = 0; i < loopCount; i++)
{
auto p = std::make_shared<B>(); // <--- THREAD
std::atomic_store<A>( &ptr, p );
}
});
The object is created and then atomically stored into ptr. The consumer then needs to atomically load the object.
thread consumer([loopCount,&ptr]()
{
for (int i = 0; i < loopCount; i++)
{
auto state = std::atomic_load<A>( &ptr ); // <--- THREAD
}
});
This still has the disadvantage that objects will be lost when the producer thread is allowed to run for multiple iterations.
These examples were written in Visual Studio 2012. At this time, gcc hasn't fully implemented atomic shared_ptr access, as noted in section 20.7.2.5
In the following backtrace from a core dump A2:~A2 is called twice:
#0 0x086f5371 in B1::~B1 (this=0xe6d3a030,
__in_chrg=<value optimized out>)
at /fullpath/b1.cpp:400
#1 0x086ffd43 in ~B2 (this=0xe6d3a030,
__in_chrg=<value optimized out>)
at /fullpath/b2.h:21
#2 B2::~B2 (this=0xe6d3a030,
__in_chrg=<value optimized out>)
at /fullpath/b2.h:21
#3 0x086ea516 in A1::~A1 (this=0xe3e93958,
__in_chrg=<value optimized out>)
at /fullpath/a1.cpp:716
#4 0x0889b85d in A2::~A2 (this=0xe3e93958,
__in_chrg=<value optimized out>)
at /fullpath/a2.cpp:216
#5 0x0889b893 in A2::~A2 (this=0xe3e93958,
__in_chrg=<value optimized out>)
at /fullpath/a2.cpp:216
#6 0x0862c0f1 in E::Identify (this=0xe8083e20, t=PT_UNKNOWN)
at /fullpath/e.cpp:713
A2 is derived from A1 and B2 is derived from B1. Only B2 has a default destructor, all base class destructors are virtual.
The code looks something like this:
e.cpp:
E::E(){
//... some code ...
myA1= new A2();
}
void E::Identify(){
//...
if(myA1){
delete myA1; //line 713 of e.cpp
myA1 = NULL;
}
}
a2.cpp:
A2::~A2(){
//...
if (sd) //sd is not null here and also not made null after deletion
{
delete [] sd; //when called the second time shouldn't it crash here?
}
//...
} // line 216 of a2.cpp
a1.cpp
A1::A1(){
//...
myB1 = new B2();
//...
}
A1::~A1(){
//...
delete myB1; //line 716 of a1.cpp
//...
}
I cannot understand why A2::~A2 is called twice for the same object ( the this pointer in the backtrace has the same value for both 4 and 5 frames).
If I go to frame 4 and disassemble it prints a very different result from the frame 5 disassembeled code (about 90 lines of assembly code vs about 20 lines of assembly code).
I minimalized the example to
#include <cassert>
class A1 {
public:
virtual ~A1() {
assert(false);
}
};
class A2 : public A1 {
};
int main() {
A1* a = new A2;
delete a;
return 0;
}
with the assert to trigger a core dump.
Compiling with g++ 4.7.2, we get the double destructor backtrace in gdb
#0 0x00007f16060e92c5 in raise () from /usr/lib/libc.so.6
#1 0x00007f16060ea748 in abort () from /usr/lib/libc.so.6
#2 0x00007f16060e2312 in __assert_fail_base () from /usr/lib/libc.so.6
#3 0x00007f16060e23c2 in __assert_fail () from /usr/lib/libc.so.6
#4 0x00000000004007c8 in A1::~A1 (this=0xf60010, __in_chrg=<optimized out>) at double.cpp:6
#5 0x000000000040084d in A2::~A2 (this=0xf60010, __in_chrg=<optimized out>) at double.cpp:10
#6 0x0000000000400880 in A2::~A2 (this=0xf60010, __in_chrg=<optimized out>) at double.cpp:10
#7 0x000000000040078c in main () at double.cpp:15
while the backtrace of the same code compiled with g++ 4.3.2 looks similar but with only one frame for A2::~A2.
Both backtraces extracted with the same version of gdb (7.5.1).
So it is an artifact of the code generated by g++ 4.7, nothing to worry about for the behaviour of the compiled binary. It is not a real double call to the destructor.
This could be your scenario (but you didn't show us this part of the code)...
If class E holds a private member pointer to A2, and it doesn't have a copy constructor or operator= ....
Then, there could be a situation where an Object of type E is copied to another object (variable) of type E with the default copy constructor or operator =.
That will cause shallow copying of members, which will cause both objects to now point to the same object A1.
When the object E's are destroyed, they both try to delete the same A2 object.
I have a coredump,it shows a reference address is NULL....
details:
(gdb) bt
#0 0xffffe410 in __kernel_vsyscall ()
#1 0x005b6c10 in raise () from /lib/libc.so.6
#2 0x005b8521 in abort () from /lib/libc.so.6
#3 0xf749e641 in Application::fatalSignal () at Application.cc:277
#4 <signal handler called>
#5 SdlProcess::procedureReturn (this=0x0, aSignal=0, aArg1=1, aArg2=0x0)
at /include/c++/4.1.1/bits/stl_list.h:652
#6 0x08112edb in Sdl::authFailurer (aSdlProcess=#0x0, aLabel=0, aSignal=0, arg2=0x0) at /Mgr/SdlAuth.cc:1781
#7 0x00000000 in ?? ()
================
define:
Sdl::authFailurer(SdlProcess& aSdlProcess, int aLabel, int aSignal, int /*arg1*/, void* arg2)
#5 aSdlProcess.procedureReturn(0, 1, 0);
and in stl_list.h:652
bool empty() const
{ return this->_M_impl._M_node._M_next == &this->_M_impl._M_node; } ---->652
in procedureReturn()
{
...
if (!mProcedureStack.empty())---->here,mProcedureStack is a datamember of SdlProcess
...
{
...}
}
You can get a NULL reference by dereferencing a NULL pointer. I'm not sure how that could happen in this particular case, unless mProcedureStack is a reference.
The most often cause of this problem is when you return a dangling reference, such as:
int& foo() {
int bar = 5;
return bar;
}
The returned reference for foo now points to the stack and will contain undefined data.