Another simple(r) example : http://msdn.microsoft.com/en-us/library/t296ys27(VS.80).aspx
<http://msdn.microsoft.com/en-us/library/t296ys27(VS.80).aspx>But, as discussed in bug 8544, we've see many purecall crashes that happens and we don't think it's related to virtual functions. The only thing I can think of is that the vtable is corrupted. (overwritten or freed) Does it not make sense? Nicolas On Thu, Apr 2, 2009 at 1:54 PM, cpu <c...@chromium.org> wrote: > > After reading some speculation in bugs such as > http://code.google.com/p/chromium/issues/detail?id=8544 I felt > compelled to dispel some myths and misunderstandings about the origin > and meaning of the mythical _purecall_ exception. My hope is that then > you can spot the problems in our source code and fix them. Sorry for > the long post. > > So first of all, what do you see when you get this error? if you are > in a debug build and you are not eating the exceptions via some custom > handler you see this dialog: > > --------------------------- > Debug Error! > R6025 > - pure virtual function call > (Press Retry to debug the application) > --------------------------- > Abort Retry Ignore > --------------------------- > > For chrome/chromium we install a special handler, which forces a crash > dump in which case you'll see in in the debugger analysis something > like this: > > [chrome_dll_main.cc:100] - `anonymous namespace'::PureCall() > [purevirt.c:47] - _purecall > > Before going into too much detail, let me show you a small program > that causes this exception: > > ================================= > class Base { > public: > virtual ~Base() { > ThreeFn(); > } > > virtual void OneFn() = 0; > virtual void TwoFn() = 0; > > void ThreeFn() { > OneFn(); > TwoFn(); > } > }; > > class Concrete : public Base { > public: > Concrete() : state_(0) { > } > > virtual void OneFn() { > state_ += 1; > } > virtual void TwoFn() { > state_ += 2; > } > private: > int state_; > }; > > > int _tmain(int argc, _TCHAR* argv[]) { > > Concrete* obj = new Concrete(); > obj->OneFn(); > obj->TwoFn(); > obj->ThreeFn(); > > delete obj; > > return 0; > } > ================================= > > Can you spot the problem? do you know at which line it crashes, do you > know why? if so I have wasted your time, apologies. If you are unsure > then read on. > > This program crashes when trying to call OneFn() with a purecall > exception on debug build. On release build it exits with no error, but > your mileage might vary depending on what optimizations are active. > > The call stack for the crash is: > > msvcr80d.dll!__purecall() + 0x25 <------ > shows the > dialog (debug only) > app.exe!Base::ThreeFn() Line 16 + 0xfc <----- error here > app.exe!Base::~Base() Line 10 C++ > app.exe!Concrete::~Concrete() + 0x2b > app.exe!Concrete::`scalar deleting destructor'() + 0x2b <----- > delete obj > > So as you have guessed it has to do with calling virtual functions > from a destructor. > > What happens is that during construction an object evolves from the > earliest base class to the actual type and during destruction the > object devolves (is that a word?) from the actual object to the > earliest base class; when we reach ~Base() body the object is no > longer of type Concrete but of type Base and thus the call Base::OneFn > () is an error because that class does not in fact have any > implementation. > > What the compiler does is create two vtables, the vtable of Concrete > looks like this: > > vtable 1: > [ 0 ] -> Concrete::OneFn() > [ 1 ] -> Concrete::TwoFn() > > vtable 2: > [ 0 ]-> msvcr80d.dll!__purecall() > [ 1 ]-> msvcr80d.dll!__purecall() > > The dtor of Concrete is the default dtor which does nothing except > calling Base::~Base(), but the dtor of base does: > > this->vtbl_ptr = vtable2 > call ThreeFn() > > > Now, why doesn't the release build crash? > > That's because the compiler does not bother with generating the second > vtable, after all is not going to be used and thus also eliminates the > related lines such as this->vtbl_ptr = vtable2. Therefore the object > reaches the base dtor with the vtbl_ptr pointing to vtable1 which > makes the call ThreeFn() just work. > > But that was just luck. If you ever modify the base class, such as > introducing a new virtual function that is not pure, like this: > > class Base { > public: > virtual ~Base() { > ThreeFn(); > } > > virtual void OneFn() = 0; > virtual void TwoFn() = 0; > > virtual void FourFn() { <--- new function, not pure virtual > wprintf(L"aw snap"); > } > > void ThreeFn() { > OneFn(); > TwoFn(); > } > }; > > // Same program below. > // ....... > // ======================== > > Then you are forcing the compiler to generate vtable 2, which looks: > > vtable 2: > [ 0 ]-> msvcr80d.dll!__purecall() > [ 1 ]-> msvcr80d.dll!__purecall() > [ 2 [-> Base::FourFn() > > And now the purecall crash magically happens (on the same spot) on > release builds, which is quite surprising since the trigger was the > introduction of FourFn() which has _nothing_ to do with the crash or > the problem and is many commits after the introduction of the problem. > > So the moral of the story? beware of virtual calls on dtors and ctors. > Note that in practice this is quite tricky because of layers of > indirection / complexity of the code base. > > ... so and what about the manbearpig ? Ah, yes no longer a myth: > > http://www.thinkgene.com/scientists-successfully-create-human-bear-pig-chimera/ > > -cpu > > > > --~--~---------~--~----~------------~-------~--~----~ Chromium Developers mailing list: chromium-dev@googlegroups.com View archives, change email options, or unsubscribe: http://groups.google.com/group/chromium-dev -~----------~----~----~----~------~----~------~--~---