wlav / cppyy

Other
404 stars 42 forks source link

Segfault when class owns a unique_ptr #212

Open ashleylll opened 8 months ago

ashleylll commented 8 months ago

It seems like cppyy segfaults when there's a unique_ptr in the class. See below for an example. The same piece of code doesn't seg fault when it's

  1. run directly in c++
  2. when unique_ptr is replaced with shared_ptr / stack allocated object
  3. when an empty d'tor / empty c'tor is defined for SomeClassOwner. (Adding a ~SomeClassOwner () { } removes the segfault. )
import cppyy
import cppyy.ll
cppyy.ll.set_signals_as_exception(True)
import os
os.environ['EXTRA_CLING_ARGS'] += '-g'

def segfault_fn():
    txt ="""
        #include <memory>

        class SomeClass 
        {
        public:
            SomeClass() {
                std::cout << "SomeClass CTOR" << std::endl;
            }

            ~SomeClass() { 
                std::cout << "SomeClass DTOR"  << std::endl;
            }
        };

        struct SomeClassOwner  
        {
            std::unique_ptr<SomeClass> obj;
            void fn() { std::cout << "FUNCTION RUN" << std::endl; }
        };

        """
    cppyy.cppdef(txt)
    from cppyy.gbl import SomeClassOwner 
    instance = SomeClassOwner()
    instance.fn()

segfault_fn()

The code above segfaults with the following stack trace

FUNCTION RUN
 *** Break *** segmentation violation
#0  0x00007fe7e141d60c in waitpid () from /lib64/libc.so.6
#1  0x00007fe7e139af62 in do_system () from /lib64/libc.so.6
#2  0x00007fe7da93ec65 in CppyyLegacy::TUnixSystem::StackTrace() () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCoreLegacy.so
#3  0x00007fe7d1880098 in (anonymous namespace)::TExceptionHandlerImp::HandleException(int) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libcppyy_backend.so
#4  0x00007fe7da93d991 in CppyyLegacy::TUnixSystem::DispatchSignals(CppyyLegacy::ESignals) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCoreLegacy.so
#5  <signal handler called>
#6  0x00007fe7d672bc00 in clang::Decl::castFromDeclContext(clang::DeclContext const*) [clone .localalias] () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#7  0x00007fe7d54506a9 in (anonymous namespace)::ItaniumCXXABI::NeedsVTTParameter(clang::GlobalDecl) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#8  0x00007fe7d5625c38 in clang::CodeGen::CodeGenFunction::GetVTTParameter(clang::GlobalDecl, bool, bool) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#9  0x00007fe7d545f1da in (anonymous namespace)::ItaniumCXXABI::EmitDestructorCall(clang::CodeGen::CodeGenFunction&, clang::CXXDestructorDecl const*, clang::CXXDtorType, bool, bool, clang::CodeGen::Address, clang::QualType) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#10 0x00007fe7d561a63e in clang::CodeGen::CodeGenFunction::destroyCXXObject(clang::CodeGen::CodeGenFunction&, clang::CodeGen::Address, clang::QualType) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#11 0x00007fe7d5642e45 in clang::CodeGen::CodeGenFunction::emitDestroy(clang::CodeGen::Address, clang::QualType, void (*)(clang::CodeGen::CodeGenFunction&, clang::CodeGen::Address, clang::QualType), bool) [clone .localalias] () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#12 0x00007fe7d5631c8c in clang::CodeGen::CodeGenFunction::PopCleanupBlock(bool) [clone .localalias] () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#13 0x00007fe7d563362e in clang::CodeGen::CodeGenFunction::PopCleanupBlocks(clang::CodeGen::EHScopeStack::stable_iterator, std::initializer_list<llvm::Value**>) [clone .localalias] () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#14 0x00007fe7d5633bb4 in clang::CodeGen::CodeGenFunction::PopCleanupBlocks(clang::CodeGen::EHScopeStack::stable_iterator, unsigned long, std::initializer_list<llvm::Value**>) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#15 0x00007fe7d562df76 in clang::CodeGen::CodeGenFunction::EmitDestructorBody(clang::CodeGen::FunctionArgList&) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#16 0x00007fe7d53ca34e in clang::CodeGen::CodeGenFunction::GenerateCode(clang::GlobalDecl, llvm::Function*, clang::CodeGen::CGFunctionInfo const&) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#17 0x00007fe7d55f5f3c in clang::CodeGen::CodeGenModule::codegenCXXStructor(clang::GlobalDecl) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#18 0x00007fe7d5450353 in (anonymous namespace)::ItaniumCXXABI::emitCXXStructor(clang::GlobalDecl) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#19 0x00007fe7d540376e in clang::CodeGen::CodeGenModule::EmitGlobalDefinition(clang::GlobalDecl, llvm::GlobalValue*) [clone .localalias] () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#20 0x00007fe7d540b3f3 in clang::CodeGen::CodeGenModule::EmitDeferred() [clone .localalias] () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#21 0x00007fe7d540b40f in clang::CodeGen::CodeGenModule::EmitDeferred() [clone .localalias] () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#22 0x00007fe7d540c893 in clang::CodeGen::CodeGenModule::Release() () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#23 0x00007fe7d53108fb in clang::CodeGeneratorImpl::HandleTranslationUnit(clang::ASTContext&) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#24 0x00007fe7d27949ba in cling::IncrementalParser::codeGenTransaction(cling::Transaction*) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#25 0x00007fe7d2794ce5 in cling::IncrementalParser::commitTransaction(llvm::PointerIntPair<cling::Transaction*, 2u, cling::IncrementalParser::EParseResult, llvm::PointerLikeTypeTraits<cling::Transaction*>, llvm::PointerIntPairInfo<cling::Transaction*, 2u, llvm::PointerLikeTypeTraits<cling::Transaction*> > >&, bool) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#26 0x00007fe7d2798c35 in cling::IncrementalParser::Compile(llvm::StringRef, cling::CompilationOptions const&) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#27 0x00007fe7d2702801 in cling::Interpreter::declare(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, cling::Transaction**) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#28 0x00007fe7d2703a52 in cling::Interpreter::DeclareCFunction(llvm::StringRef, llvm::StringRef, bool, cling::Transaction*&) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#29 0x00007fe7d2703c54 in cling::Interpreter::compileFunction(llvm::StringRef, llvm::StringRef, bool, bool) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#30 0x00007fe7d2615fbe in CppyyLegacy::TClingCallFunc::make_dtor_wrapper(CppyyLegacy::TClingClassInfo const*) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#31 0x00007fe7d2616693 in CppyyLegacy::TClingCallFunc::ExecDestructor(CppyyLegacy::TClingClassInfo const*, void*, unsigned long, bool) () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#32 0x00007fe7d262495f in CppyyLegacy::TClingClassInfo::Delete(void*, CppyyLegacy::TMetaUtils::TNormalizedCtxt const&) const () from /home/.conda/envs/env/lib/python3.11/site-packages/cppyy_backend/lib/libCling.so
#33 0x00007fe7cf8859c6 in CPyCppyy::op_dealloc_nofree(CPyCppyy::CPPInstance*) () from /home/.conda/envs/env/lib/python3.11/site-packages/libcppyy.cpython-311-x86_64-linux-gnu.so
#34 0x00007fe7cf885a03 in CPyCppyy::op_dealloc(CPyCppyy::CPPInstance*) () from /home/.conda/envs/env/lib/python3.11/site-packages/libcppyy.cpython-311-x86_64-linux-gnu.so
#35 0x000055627b90fde2 in subtype_dealloc ()
#36 0x000055627b8d2af8 in _PyFrame_Clear ()
#37 0x000055627b8c17c1 in _PyEval_EvalFrameDefault ()
#38 0x000055627b97f05e in _PyEval_Vector ()
#39 0x000055627b97e5ef in PyEval_EvalCode ()
#40 0x000055627b9a112c in run_eval_code_obj ()
#41 0x000055627b99d3a4 in run_mod ()
#42 0x000055627b9b2372 in pyrun_file ()
#43 0x000055627b9b1ca5 in _PyRun_SimpleFileObject.localalias ()
#44 0x000055627b9b1a73 in _PyRun_AnyFileObject.localalias ()
#45 0x000055627b9abb76 in Py_RunMain.localalias ()
#46 0x000055627b96ce19 in Py_BytesMain ()
#47 0x00007fe7e137a555 in __libc_start_main () from /lib64/libc.so.6
#48 0x000055627b96ccb1 in _start ()

The stack trace shows that the cpp object is being constructed and run correctly. Segfault error happens when object exits python frame, this is when cppyy tries to destruct the object - when cppyy tries to compile the dtor wrapper created in make_dtor_wrapper using cling. [see # 30 in stack trace] Looks like it is an issue with how cppyy handles destruction.

wlav commented 8 months ago

I remember seeing this before, but can't find it anymore. I forget what it was exactly, but something along the lines of Clang thinking a destructor was already emitted when it wasn't yet and then it segfaults. I pushed it upstream, but don't think it was ever resolved. The workaround is to add a destructor to SomeClassOwner:

        struct SomeClassOwner {
            ~SomeClassOwner() {}
            std::unique_ptr<SomeClass> obj;
            void fn() { std::cout << "FUNCTION RUN" << std::endl; }
        }

I realize that it's not always possible to modify code that gets included, so an alternative workaround is to add a global variable in C++ before using it in python. That instantiation, too, will bring the destructor into existence (as cleanup code will be generated) so that it is later found:

        struct SomeClassOwner {
            std::unique_ptr<SomeClass> obj;
            void fn() { std::cout << "FUNCTION RUN" << std::endl; }
        };

        SomeClassOwner workardound;