Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Pure virtual function call in Winamp?

7 views
Skip to first unread message

Robbie Hatley

unread,
Oct 22, 2009, 8:35:16 PM10/22/09
to

I was playing a Cat Stevens song in Winamp just now, and I got this
error message, which popped up about 40 times, about 1 second apart:
http://www.well.com/~lonewolf/pure-virtual.jpg
What's up with that? I'm guessing this program tried to call a
pure-virtual member function of a base class directly, rather than
through an object of a derived class that actually defines the
function in question? I have a hard time imagining how that could
happen in production code... but it did. Any speculations on how
this kind of error can arise?

(Off-topic bonus question, just for fun: See if anyone can
identify the building in the background.)

--
Cheers,
Robbie Hatley
lonewolf at well dot com
www dot well dot com slant tilde lonewolf slant

Alf P. Steinbach

unread,
Oct 22, 2009, 9:45:51 PM10/22/09
to
* Robbie Hatley:

> I was playing a Cat Stevens song in Winamp just now, and I got this
> error message, which popped up about 40 times, about 1 second apart:
> http://www.well.com/~lonewolf/pure-virtual.jpg
> What's up with that? I'm guessing this program tried to call a
> pure-virtual member function of a base class directly, rather than
> through an object of a derived class that actually defines the
> function in question? I have a hard time imagining how that could
> happen in production code... but it did. Any speculations on how
> this kind of error can arise?
>
> (Off-topic bonus question, just for fun: See if anyone can
> identify the building in the background.)
>

I think it's possibly due to some malware, but most probably a Windows update,
infesting your system during the last 7 days.

It has nothing to do with WinAmp specifically; it's something affecting the
Microsoft C++ runtime DLL.

Unfortunately it also got me.

Reason for my belief regarding cause: every Windows update breaks some existing
functionality in old versions of Windows. On my XP prof, for example, ftype and
netstat and openfiles commands were broken by different updates. I think that is
by design (the netstat thing was provably by design, t'was documented). Sort of
Microsoft's way of saying "Hey, move on, we need to sell upgrades and we
absolutely don't want to shelve out good $$$ on supporting old versions!".

By the way, if you're using AVG anti-virus and/or have installed some Python
tools these last days then that would be something in common with me, but I
really do think it's much more likely a Windows update is the cause.


Cheers & hth.,

- Alf

Puppet_Sock

unread,
Oct 22, 2009, 9:53:02 PM10/22/09
to
On Oct 22, 8:35 pm, "Robbie Hatley" <lonew...@well.com> wrote:
[off topic post snipped]

> (Off-topic bonus question, just for fun: See if anyone can
> identify the building in the background.)

Dude! Bugs in commercial apps are clearly off topic.

And the building is obviously your mom's house where
you live in the basement.
Socks

Robbie Hatley

unread,
Oct 22, 2009, 11:39:04 PM10/22/09
to

Alf P. Steinbach wrote:

> Robbie Hatley wrote:
> > I was playing a Cat Stevens song in Winamp just now, and I got this
> > error message, which popped up about 40 times, about 1 second apart:
> > http://www.well.com/~lonewolf/pure-virtual.jpg
> > What's up with that? I'm guessing this program tried to call a
> > pure-virtual member function of a base class directly, rather than
> > through an object of a derived class that actually defines the
> > function in question? I have a hard time imagining how that could
> > happen in production code... but it did. Any speculations on how
> > this kind of error can arise?
> >
> > (Off-topic bonus question, just for fun: See if anyone can
> > identify the building in the background.)
>

> ... It has nothing to do with WinAmp specifically; it's something
> affecting the Microsoft C++ runtime DLL ...

I tend to disagree. I think it's in Winamp. The reason? I've
never seen that bug in a production computer program in my life,
before or since. Only in Winamp, and only once, after switching
to a particular skin. If it was actually in the C++ runtime
library, I'm thinking it would pop up a LOT. I mean, come on,
what percentage of the big apps on a typical Windows 2000 computer
use C++ and have pure virtual functions somewhere? 80%? If it
was in the C++ runtime library, I think most apps on my computer
would be broken, but they're not.

So I think something in this one program is, under a very particular
set of circumstances, trying to call the base-class version of a
pure virtual function.

I should also alert the Winamp folks to this, just to be a nice guy.

My reason for posting this *here* is to see if anyone has actually
run into bugs like this, either in their own code, or someone else's.
Being a C++ developer myself, it seems to me a very unusual bug;
something like that would normally be caught early in alpha testing,
I should think.

Paavo Helde

unread,
Oct 23, 2009, 2:24:20 AM10/23/09
to
"Robbie Hatley" <lone...@well.com> wrote in
news:0cGdnaaabt2ruHzX...@giganews.com:

> So I think something in this one program is, under a very particular
> set of circumstances, trying to call the base-class version of a
> pure virtual function.
>

> My reason for posting this *here* is to see if anyone has actually
> run into bugs like this, either in their own code, or someone else's.
> Being a C++ developer myself, it seems to me a very unusual bug;
> something like that would normally be caught early in alpha testing,
> I should think.

You cannot test for everything, especially if the problem appears in a
"very particular set of circumstances".

I have definitely seen that bug myself, mostly in my own code during
development. In my code, the bugs were always caused by too complex data
structures, where child objects had backlink pointers or references to
their parents, and the object lifetimes were not entirely clear. The
following example ought to exhibit the bug, about once per each 10 runs.

#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <string>

bool BadMoonPhase() {
return rand()%10 == 0;
}

class X {
public:
virtual ~X() {}
};

class A {
public:
A(): x_(NULL) {}
void SetX(X* x) {x_ = x;}
virtual std::string Description() const=0;
virtual ~A() {
delete x_;
}
private:
X* x_;

};


class Y: public X {
public:
Y(A& parent): parent_(parent) {}

std::string Description() const {
return "Y, inside of a " + parent_.Description();
}
~Y() {
if (BadMoonPhase()) {
std::cerr << "Bad moon phase discovered by: ";
std::cerr << Description() << "\n";
}
}
private:
A& parent_;
};


class B: public A {
public:
B() {
SetX(new Y(*this));
}
virtual std::string Description() const { return "B object";}
};


int main() {
srand(time(NULL));
B b;
}

Vladimir Jovic

unread,
Oct 23, 2009, 3:47:25 AM10/23/09
to
Paavo Helde wrote:
> "Robbie Hatley" <lone...@well.com> wrote in
> news:0cGdnaaabt2ruHzX...@giganews.com:
>
>
> You cannot test for everything, especially if the problem appears in a
> "very particular set of circumstances".
>

Yes, you can. Search for TDD

> I have definitely seen that bug myself, mostly in my own code during
> development. In my code, the bugs were always caused by too complex data
> structures, where child objects had backlink pointers or references to
> their parents, and the object lifetimes were not entirely clear. The
> following example ought to exhibit the bug, about once per each 10 runs.
>

<CUT THE BUGGY CODE>

I modified your example (the destructors should be virtual, and I added
some debug messages).
This example explains better why it happens:


#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <string>

bool BadMoonPhase() {
return true;
//return rand()%10 == 0;
}

class X {
public:
X()
{
std::cout<<"X()"<<std::endl;
}
virtual ~X()
{
std::cout<<"~X()"<<std::endl;
}
};

class A {
public:
A(): x_(NULL)
{

std::cout<<"A()"<<std::endl;


}
void SetX(X* x) {x_ = x;}
virtual std::string Description() const=0;
virtual ~A()
{

std::cout<<"~A()"<<std::endl;


delete x_;
}
private:
X* x_;

};


class Y: public X {
public:
Y(A& parent): parent_(parent)
{

std::cout<<"Y()"<<std::endl;
}

std::string Description() const {
return "Y, inside of a " + parent_.Description();
}

virtual ~Y()
{
std::cout<<"~Y()"<<std::endl;


if (BadMoonPhase())
{
std::cerr << "Bad moon phase discovered by: ";
std::cerr << Description() << "\n";
}
}
private:
A& parent_;
};


class B: public A {
public:
B()
{

std::cout<<"B()"<<std::endl;
SetX(new Y(*this));
}
virtual ~B()
{
std::cout<<"~B()"<<std::endl;


}
virtual std::string Description() const { return "B object";}
};


int main() {
srand(time(NULL));
B b;
}


Therefore, this has nothing to do with bad moon phase, but with the bad
design, and trying to be clever.


--
Bolje je ziveti sto godina kao bogatun, nego jedan dan kao siromah!

Vladimir Jovic

unread,
Oct 23, 2009, 3:48:32 AM10/23/09
to
Paavo Helde wrote:
> "Robbie Hatley" <lone...@well.com> wrote in
> news:0cGdnaaabt2ruHzX...@giganews.com:
>
>
> You cannot test for everything, especially if the problem appears in a
> "very particular set of circumstances".
>

Yes, you can. Search for TDD

> I have definitely seen that bug myself, mostly in my own code during


> development. In my code, the bugs were always caused by too complex data
> structures, where child objects had backlink pointers or references to
> their parents, and the object lifetimes were not entirely clear. The
> following example ought to exhibit the bug, about once per each 10 runs.
>

<CUT THE BUGGY CODE>

I modified your example (the destructors should be virtual, and I added
some debug messages).
This example explains better why it happens:

#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <string>

bool BadMoonPhase() {
return true;
//return rand()%10 == 0;
}

class X {
public:


X()
{
std::cout<<"X()"<<std::endl;
}
virtual ~X()
{
std::cout<<"~X()"<<std::endl;
}
};

class A {
public:
A(): x_(NULL)
{


std::cout<<"A()"<<std::endl;
}

void SetX(X* x) {x_ = x;}
virtual std::string Description() const=0;
virtual ~A()
{

std::cout<<"~A()"<<std::endl;

delete x_;
}
private:
X* x_;

};


class Y: public X {
public:
Y(A& parent): parent_(parent)
{

std::cout<<"Y()"<<std::endl;
}

std::string Description() const {
return "Y, inside of a " + parent_.Description();
}

virtual ~Y()
{
std::cout<<"~Y()"<<std::endl;

if (BadMoonPhase())
{
std::cerr << "Bad moon phase discovered by: ";
std::cerr << Description() << "\n";
}
}
private:
A& parent_;
};


class B: public A {
public:
B()
{

std::cout<<"B()"<<std::endl;
SetX(new Y(*this));
}
virtual ~B()
{
std::cout<<"~B()"<<std::endl;
}

virtual std::string Description() const { return "B object";}
};


int main() {
srand(time(NULL));
B b;
}

Vladimir Jovic

unread,
Oct 23, 2009, 3:49:39 AM10/23/09
to
Paavo Helde wrote:
> "Robbie Hatley" <lone...@well.com> wrote in
> news:0cGdnaaabt2ruHzX...@giganews.com:
>
>
> You cannot test for everything, especially if the problem appears in a
> "very particular set of circumstances".
>

Yes, you can. Search for TDD

> I have definitely seen that bug myself, mostly in my own code during


> development. In my code, the bugs were always caused by too complex data
> structures, where child objects had backlink pointers or references to
> their parents, and the object lifetimes were not entirely clear. The
> following example ought to exhibit the bug, about once per each 10 runs.
>

<CUT THE BUGGY CODE>

I modified your example (the destructors should be virtual, and I added
some debug messages).
This example explains better why it happens:

#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <string>

bool BadMoonPhase() {
return true;
//return rand()%10 == 0;
}

class X {
public:


X()
{
std::cout<<"X()"<<std::endl;
}
virtual ~X()
{
std::cout<<"~X()"<<std::endl;
}
};

class A {
public:
A(): x_(NULL)
{


std::cout<<"A()"<<std::endl;
}

void SetX(X* x) {x_ = x;}
virtual std::string Description() const=0;
virtual ~A()
{

std::cout<<"~A()"<<std::endl;

delete x_;
}
private:
X* x_;

};


class Y: public X {
public:
Y(A& parent): parent_(parent)
{

std::cout<<"Y()"<<std::endl;
}

std::string Description() const {
return "Y, inside of a " + parent_.Description();
}

virtual ~Y()
{
std::cout<<"~Y()"<<std::endl;

if (BadMoonPhase())
{
std::cerr << "Bad moon phase discovered by: ";
std::cerr << Description() << "\n";
}
}
private:
A& parent_;
};


class B: public A {
public:
B()
{

std::cout<<"B()"<<std::endl;
SetX(new Y(*this));
}
virtual ~B()
{
std::cout<<"~B()"<<std::endl;
}

virtual std::string Description() const { return "B object";}
};


int main() {
srand(time(NULL));
B b;
}

Vladimir Jovic

unread,
Oct 23, 2009, 3:50:50 AM10/23/09
to
Vladimir Jovic wrote:
[doh]

Sorry for triple post. The news server made a hiccup

James Kanze

unread,
Oct 23, 2009, 5:24:13 AM10/23/09
to
On Oct 23, 8:47 am, Vladimir Jovic <vladasp...@gmail.com> wrote:
> Paavo Helde wrote:
> > "Robbie Hatley" <lonew...@well.com> wrote in
> >news:0cGdnaaabt2ruHzX...@giganews.com:

> > You cannot test for everything, especially if the problem
> > appears in a "very particular set of circumstances".

> Yes, you can. Search for TDD

No you can't, and TDD isn't a silver bullet. (Some proponents
of TDD mail claim it does test everything, but they do so by
redefining "everything".)

--
James Kanze

Vladimir Jovic

unread,
Oct 23, 2009, 6:07:45 AM10/23/09
to

Ok, I exaggerated a bit ;)
But a thing like in this example is testable, and should be caught by an
unit test, no?

Juha Nieminen

unread,
Oct 23, 2009, 10:18:54 AM10/23/09
to
Alf P. Steinbach wrote:
>> http://www.well.com/~lonewolf/pure-virtual.jpg

>
> I think it's possibly due to some malware, but most probably a Windows
> update, infesting your system during the last 7 days.
>
> It has nothing to do with WinAmp specifically; it's something affecting
> the Microsoft C++ runtime DLL.

Don't be ridiculous. A "pure virtual call" runtime error is easy to
reproduce in any C++ program. For example:

//--------------------------------------------------------------------------
#include <iostream>

class A
{
public:
virtual void foo() = 0;
void bar() { foo(); }
~A() { bar(); }
};

class B: public A
{
public:

virtual void foo() { std::cout << "B::foo()\n"; }
};

int main()
{
B b;
}
//--------------------------------------------------------------------------

When run on a linux system, that program produces:

pure virtual method called
terminate called without an active exception

So it is perfectly possible that the bug is in WinAmp.

James Kanze

unread,
Oct 23, 2009, 10:46:00 AM10/23/09
to
On Oct 23, 11:07 am, Vladimir Jovic <vladasp...@gmail.com> wrote:
> James Kanze wrote:
> > On Oct 23, 8:47 am, Vladimir Jovic <vladasp...@gmail.com> wrote:
> >> Paavo Helde wrote:
> >>> "Robbie Hatley" <lonew...@well.com> wrote in
> >>>news:0cGdnaaabt2ruHzX...@giganews.com:

> >>> You cannot test for everything, especially if the problem
> >>> appears in a "very particular set of circumstances".

> >> Yes, you can. Search for TDD

> > No you can't, and TDD isn't a silver bullet. (Some
> > proponents of TDD mail claim it does test everything, but
> > they do so by redefining "everything".)

> Ok, I exaggerated a bit ;)
> But a thing like in this example is testable, and should be
> caught by an unit test, no?

Probably. There's always a problem with programs using plugins,
since you can't determine in advance what the plugin might do.
But this type of bug usually crops up systematically, each time
the specific constructor is called, which means that the unit
tests never even called the constructor in question, which does
seem a bit inconceivable.

--
James Kanze

Paavo Helde

unread,
Oct 23, 2009, 12:22:07 PM10/23/09
to
Vladimir Jovic <vlada...@gmail.com> wrote in
news:hbrn13$vnc$1...@news.albasani.net:

> Paavo Helde wrote:
>> "Robbie Hatley" <lone...@well.com> wrote in
>> news:0cGdnaaabt2ruHzX...@giganews.com:
>>
>>
>> You cannot test for everything, especially if the problem appears in
>> a "very particular set of circumstances".
>>
>
> Yes, you can. Search for TDD

Typically such problems hide in error reporting paths, otherwise they
would have surfaced immediately. For catching them, the test environment
ought to be able to emulate all existing error conditions, at each time
when they are checked in the program, which to me seems not so trivial.
As far as I can see, TDD is more oriented to get something working, and
does not help so much when coping with failures.

>
> I modified your example (the destructors should be virtual, and I
> added some debug messages).

The destructors were virtual in my code. You just wasted some
bits/keystrokes ;-)

>
>
> Therefore, this has nothing to do with bad moon phase, but with the
> bad design, and trying to be clever.

With that I agree.

Paavo

Andy Champ

unread,
Oct 23, 2009, 5:50:21 PM10/23/09
to

I'm just going to chip in here and add my vote: James is right (as
usual!). There's no such thing as 100% test coverage - it requires you
to go through all paths, and that is an enormous space on any reasonable
program. Our automated tests, run after every nightly build, currently
take about 100 machine hours (farmed out to a pool, so they are normally
done by morning!).

And I once, many years ago, had a bug which was a timing problem between
two routines. Once we had pinned down the exact circumstances that
could cause the fail we could make it break most days. Sometimes it
took longer than that for the timing to get just right.

Andy

James Kanze

unread,
Oct 24, 2009, 4:55:11 AM10/24/09
to
On Oct 23, 11:50 pm, Andy Champ <no....@nospam.invalid> wrote:
> James Kanze wrote:
> > On Oct 23, 8:47 am, Vladimir Jovic <vladasp...@gmail.com> wrote:
> >> Paavo Helde wrote:
> >>> "Robbie Hatley" <lonew...@well.com> wrote in
> >>>news:0cGdnaaabt2ruHzX...@giganews.com:

> >>> You cannot test for everything, especially if the problem
> >>> appears in a "very particular set of circumstances".

> >> Yes, you can. Search for TDD

> > No you can't, and TDD isn't a silver bullet. (Some proponents
> > of TDD mail claim it does test everything, but they do so by
> > redefining "everything".)

> I'm just going to chip in here and add my vote: James is
> right (as usual!). There's no such thing as 100% test
> coverage - it requires you to go through all paths, and that
> is an enormous space on any reasonable program.

Literally, it would require you going through all paths with all
possible values for those paths. I don't think anyone claims
that this is possible, however, and most of the time, going
through all possible paths in each function is a close enough
approximation. (Floating point is a notable exception, given
its non-linearity. Multithreading also introduces some
limitations: unit tests are not normally run in a multithreaded
environment, and certainly not in a realistic one. These sort
of things create code which literally isn't testable.)

Even given this, the problem is guaranteeing that you've
executed all possible paths in each function. I've evaluated
tools claiming to do this in the past, and they all miss some of
even the more trivial cases. Done seriously (and that's an
important condition), TDD, and even more so code review, can
help, but they are both done by fallible humans, so some cases
slip through.

> Our automated tests, run after every nightly build, currently
> take about 100 machine hours (farmed out to a pool, so they
> are normally done by morning!).

> And I once, many years ago, had a bug which was a timing
> problem between two routines. Once we had pinned down the
> exact circumstances that could cause the fail we could make it
> break most days. Sometimes it took longer than that for the
> timing to get just right.

Timing problems and mathematical instability due to rounding
errors are probably the two most difficult things to catch
through testing.

--
James Kanze

Vladimir Jovic

unread,
Oct 26, 2009, 1:55:23 AM10/26/09
to
Paavo Helde wrote:
> Vladimir Jovic <vlada...@gmail.com> wrote in
>
>> I modified your example (the destructors should be virtual, and I
>> added some debug messages).
>
> The destructors were virtual in my code. You just wasted some
> bits/keystrokes ;-)

I thought the derived classes have to have virtual destructors as well :(

James Kanze

unread,
Oct 26, 2009, 1:16:26 PM10/26/09
to
On Oct 26, 6:55 am, Vladimir Jovic <vladasp...@gmail.com> wrote:
> Paavo Helde wrote:
> > Vladimir Jovic <vladasp...@gmail.com> wrote in

> >> I modified your example (the destructors should be virtual,
> >> and I added some debug messages).

> > The destructors were virtual in my code. You just wasted
> > some bits/keystrokes ;-)

> I thought the derived classes have to have virtual destructors
> as well :(

If the base class destructors are virtual, then the derived
class destructors are as well.

Of course, it's always preferrable to spell it out anyway, for
the reader.

--
James Kanze

Vladimir Jovic

unread,
Oct 27, 2009, 3:35:17 AM10/27/09
to
James Kanze wrote:
> On Oct 26, 6:55 am, Vladimir Jovic <vladasp...@gmail.com> wrote:
>> Paavo Helde wrote:
>>> Vladimir Jovic <vladasp...@gmail.com> wrote in
>
>>>> I modified your example (the destructors should be virtual,
>>>> and I added some debug messages).
>
>>> The destructors were virtual in my code. You just wasted
>>> some bits/keystrokes ;-)
>
>> I thought the derived classes have to have virtual destructors
>> as well :(
>
> If the base class destructors are virtual, then the derived
> class destructors are as well.

This example:

class A
{
public:
virtual ~A(){}


};
class B : public A
{
public:

~B(){}
};
class C : public B
{
public:
~C(){}
};

If I understood you correctly, classes B and C have virtual destructors,
although it is not explicitly said. Correct?

In that case, I can do this:
B *b = new C;
without causing UB.

Paavo Helde

unread,
Oct 27, 2009, 1:53:03 PM10/27/09
to
Vladimir Jovic <vlada...@gmail.com> wrote in news:hc67q6$vm$1
@news.albasani.net:

Correct. For inherited classes there is the principle "once virtual,
always virtual". This holds for all member functions, not only for the
destructor.

>
> In that case, I can do this:
> B *b = new C;
> without causing UB.

This is no UB, virtual dtor or not. The deletion step is what might
potentially cause UB if the dtor is not virtual.

Paavo

Vladimir Jovic

unread,
Oct 28, 2009, 2:43:23 AM10/28/09
to
Paavo Helde wrote:
>> class A
>> {
>> public:
>> virtual ~A(){}
>> };
>> class B : public A
>> {
>> public:
>> ~B(){}
>> };
>> class C : public B
>> {
>> public:
>> ~C(){}
>> };
>>
>> If I understood you correctly, classes B and C have virtual
> destructors,
>> although it is not explicitly said. Correct?
>
> Correct. For inherited classes there is the principle "once virtual,
> always virtual". This holds for all member functions, not only for the
> destructor.
>

Great. Thanks. I didn't know that

>> In that case, I can do this:
>> B *b = new C;
>> without causing UB.
>
> This is no UB, virtual dtor or not. The deletion step is what might
> potentially cause UB if the dtor is not virtual.

Yes, I forgot that line

0 new messages