Uncaught exception when mixing Clang/GCC generated code

Dear Cling experts,

I’m unable to catch exceptions in GCC generated code that were thrown by Cling interpreted code. Please consider the following code snippets illustrating my use case.

class ScriptDefinedClass : public somewhere::in::sharedobj::Base {
  public:
    ScriptDefinedClass() {}

    void DoSth() override {
      throw std::runtime_error("This is an exception");
    }
};

somewhere::in::sharedobj::Base* InterpretMe() {
  return new ScriptDefinedClass; 
}
int main(int argc, char **argv) {
  // ...
  cpp_interpreter->GetClingInterpreter()->LoadFile(script_filepath);
    
  cling::Value return_value;
  cpp_interpreter->GetClingInterpreter()->Process("InterpretMe();", &return_value);
  somewhere::in::sharedobj::Base *base = return_value.getAs<somewhere::in::sharedobj::Base *>();
  
  try {
    base->DoSth();
  } catch (const std::exception& e) {
    // This code is never executed
    std::cout << "Caught exception!" << std::endl;
  }
}

Please note that both LLVM/Clang/Cling were compiled against the same version of GCC as the test program.

ldd testpgm
    ...
    libstdc++.so.6 => /net/software/libraries/linux/RHEL6-32bit/release/gcc-4.9.4/lib/libstdc++.so.6
    libgcc_s.so.1 => /net/software/libraries/linux/RHEL6-32bit/release/gcc-4.9.4/lib/libgcc_s.so.1
    libc.so.6 => /lib/libc.so.6
    ...
ldd cling
    ...
    libstdc++.so.6 => /net/software/libraries/linux/RHEL6-32bit/release/gcc-4.9.4/lib/libstdc++.so.6
    libgcc_s.so.1 => /net/software/libraries/linux/RHEL6-32bit/release/gcc-4.9.4/lib/libgcc_s.so.1
    libc.so.6 => /lib/libc.so.6
    ...

The embedded Cling interpreter is invoked like so:

std::vector<std::string> argv =
    boost::assign::list_of(std::string(LLVM_PATH_MACRO) + "/bin/cling")("-fcxx-exceptions");
    argv.insert(argv.end(), this->clang_args_.begin(), this->clang_args_.end());

this->interpreter_ = new cling::Interpreter(argv.size(), this->ConvertCommandLineArgv(argv), LLVM_PATH_MACRO);

Can anyone help me with that?

Hi,

The same happens when throwing interactively. Let’s make sure this works:

[cling]$ throw 42;
Exception occurred. Recovering...
[cling]$ 

Then: did you enable exceptions for your binary? Try to throw and catch something inside your binary.

Cheers, Axel.

Hi Axel,

exception handling is working for both script and binary. I tested your suggestion using Cling interactively, in my script and in binary. Just the combination as described above is not working.

Should I mention that after invoking embedded Cling with

this->interpreter_ = new cling::Interpreter(argv.size(), this->ConvertCommandLineArgv(argv), LLVM_PATH_MACRO);

it prints

Do you have another idea?

Best regards

Hi,

the binary called “cling” has a catch, just like your binary. It manages to catch the exception thrown by the interpreter.

Thus there must be a problem with how you compile or link your binary. Did you consider adding the exception flag I was proposing? Did you consider doing the compiled throw, compiled catch test I was proposing?

Cheers, Axel.

I was able to track down the problem a little further. Class “somewhere::in::sharedobj::Base” is an implementation of a state machine.

class Base {
  private:
    std::function<void()> current_state_;
    std::function<bool()> current_transition_condition_;
    
    // ...
    
  public:
    // ...
    
    void Trigger() {
      this->SetCurrentState([this]() { this->OnTriggered(); });
      
      // ...
    }
    virtual void OnTriggered() = 0;
    
    void SetCurrentState(std::function<void()> const& current_state,
                       std::function<bool()> const& transition_condition = []() -> bool { return true; });
    std::function<void()> const& GetCurrentState() const { return this->current_state_; }
};

If the base class is triggered using “Base::Trigger” the current state is set to “Base::OnTriggered”. Class “Base” is part of a shared library compiled with GCC.

Now please consider the script defined class “ScriptDefinedClass” where exception handling works:

class ScriptDefinedClass : public somewhere::in::sharedobj::Base {
 public:
  ScriptDefinedClass() {}

  void OnTriggered() override {
    throw std::runtime_error("This is an exception.");
  
    this->SetCurrentState([this]() { this->OnState1(); });
  }

  void OnState1() {
    return;
  }

  // ...
};

If an exception is thrown from within “ScriptDefinedClass::OnTriggered”, it can be caught.
If the script defined class “ScriptDefinedClass” is defined like so

class ScriptDefinedClass : public somewhere::in::sharedobj::Base {
 public:
  ScriptDefinedClass() {}

  void OnTriggered() override {
    this->SetCurrentState([this]() { this->OnState1(); });
  }

  void OnState1() {
    throw std::runtime_error("This is an exception.");
    return;
  }

  // ...
};

the exception can’t be caught.

I was first thinking that this might be caused by the way GCC and Clang are translating lambda functions, but even std::bind is showing the same behaviour.

I am lost…

Hi,

In cling, I can create a lambda that throws, call it, and cling’s compiled catch is catching it.

Can you run valgrind on this, to make sure there is no memory issue involved? Ideally you’d run valgrind on a compiled version (i.e. not using cling), to make better sense of stack traces.

Cheers, Axel.

I did not run valgrind but when using the script code in a compiled version inside the executable the exception is caught as expected. I did successfully check if my compilation of cling is able to catch exceptions thrown in lambdas as you suggested.

???

Hi,

FYI - we had a case where exceptions weren’t caught. That’s fixed in the master - could you check your case?

Cheers, Axel.