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
-~----------~----~----~----~------~----~------~--~---

Reply via email to