1

Somewhere deep inside an executable running on a Linux server, there is a C++ pure virtual function call. It causes the server to crash, leaving no program trace data, no stack trace and no core dump. Only some log files remain. It is almost certainly caused by a lifetime issue, and I have a good idea where it is happening, but I would like proof. I have tried to remedy this situation by using std::set_terminate to set a handler to be run when terminate is called. This works in tests. For example, if I cause a pure virtual call, using:

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

class Derived: public Base {
public:
    Derived(): n_(0) {}
    ~Derived(){}
    void foo() {
            n_ = 1;
    }
private:
    int n_;
};              

and then

Base* p = new Derived();
Base* p1 = p;
p->~Base();
p1->foo();

the handler works and produces trace data, stack trace and core dump. The runtime system prints

pure virtual method called

for this test.

There is no other call to set_terminate in the code. The test is done when the server is up and running, so any subsequent calls that might have gazumped my handler should have happened, if they were going to. However, in the actual server, this does not catch the pure virtual call. The only situation I can think of that might cause this is a call to set the terminate handler after mine. Are there any other ways that my terminate handler could be avoided?

Permaquid
  • 1,950
  • 1
  • 14
  • 15
  • Have you enable core dumps on that systems? Many default installations just disable them. http://stackoverflow.com/questions/2919378/how-to-enable-core-dump-in-my-linux-c-program – pentadecagon Apr 16 '14 at 14:02
  • Yes, cores are enabled. In any case, even if they were not, the terminate handler should still be invoked and various other data would be produced - for example, a stack trace. – Permaquid Apr 16 '14 at 15:47

1 Answers1

0

Actually, if you have Undefined Behavior, any change to execution environment or compiled binary can cause your code to misbehave in a different way.

That's especially observable with changed optimization and debug flags, but seemingly innocuous code-changes are also predistined to trigger this.

Next, might your error in any way be a heisenbug, meaning related to timing/multithreading?

Also, your compiler might completely omit virtual tables if they are not needed in construction/destruction and no object of the exact type is actually ever created.
So, your call to foo might equally just crash or call derived::foo despite the destructor having run.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • The real app reports a pure call, but this is not caught as I expected it to. – Permaquid Apr 16 '14 at 14:06
  • I'm marking this as accepted because it's about as close as one can get to a reasonable description of what is going on. After several weeks of this happening, it turns out that the handler sometimes works. Whether it does or not seems random. When it works, the place where it happens (determined from the stack trace) is consistent across multiple runs. So the answer is probably that if the memory is messed up enough, the handler won't be called. – Permaquid May 02 '14 at 17:19