SIP memory leak using std::vector

Phil Thompson phil at riverbankcomputing.com
Mon May 24 10:23:56 BST 2021


On 21/05/2021 14:22, Marian Thomsen wrote:
> Hello,
> 
> I struggle for some time now to close a memory leak using std::vector
> with SIP (first SIP v6.0.3 and now 6.1).
> 
> The valgrind output:
> 
> 
> ==26606== 16 bytes in 1 blocks are definitely lost in loss record 6 of 
> 4,537
> ==26606==    at 0x4C3217F: operator new(unsigned long) (in
> /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==26606==    by 0x6FB1542: allocate (new_allocator.h:111)
> ==26606==    by 0x6FB1542: allocate (alloc_traits.h:436)
> ==26606==    by 0x6FB1542: _M_allocate (stl_vector.h:172)
> ==26606==    by 0x6FB1542:
> _M_allocate_and_copy<__gnu_cxx::__normal_iterator<ActionID* const*,
> std::vector<ActionID*> > > (stl_vector.h:1260)
> ==26606==    by 0x6FB1542: std::vector<ActionID*,
> std::allocator<ActionID*> >::operator=(std::vector<ActionID*,
> std::allocator<ActionID*> > const&) (vector.tcc:206)
> ==26606==    by 0x6FBE60C:
> varset_RoadWorksContainerExtended_referenceDenms
> (sipdenm_pduRoadWorksContainerExtended.cpp:347)
> ==26606==    by 0x7CEF538: sipVariableDescr_descr_set 
> (descriptors.c:415)
> ==26606==    by 0x5CDD3F: _PyObject_GenericSetAttrWithDict (in
> /usr/bin/python3.8)
> ==26606==    by 0x5D613F: PyObject_SetAttr (in /usr/bin/python3.8)
> ==26606==    by 0x578DE9: _PyEval_EvalFrameDefault (in 
> /usr/bin/python3.8)
> ==26606==    by 0x5760EC: _PyEval_EvalCodeWithName (in 
> /usr/bin/python3.8)
> ==26606==    by 0x6025C6: _PyFunction_Vectorcall (in 
> /usr/bin/python3.8)
> ==26606==    by 0x578798: _PyEval_EvalFrameDefault (in 
> /usr/bin/python3.8)
> ==26606==    by 0x5760EC: _PyEval_EvalCodeWithName (in 
> /usr/bin/python3.8)
> ==26606==    by 0x66299D: ??? (in /usr/bin/python3.8)
> 
> 
> The wrapper snipped mentioned in valgrind ->
> (sipdenm_pduRoadWorksContainerExtended.cpp:347):
> 
> 
> extern "C" {static int
> varset_RoadWorksContainerExtended_referenceDenms(void *, PyObject *,
> PyObject *);}
> static int varset_RoadWorksContainerExtended_referenceDenms(void
> *sipSelf, PyObject *sipPy, PyObject *)
> {
>      ::ReferenceDenms*sipVal;
>      ::RoadWorksContainerExtended *sipCpp = reinterpret_cast<
> ::RoadWorksContainerExtended *>(sipSelf);
> 
>     int sipIsErr = 0;
>     int sipValState;
>     sipVal = reinterpret_cast< ::ReferenceDenms
> *>(sipForceConvertToType(sipPy, sipType_std_vector_0101ActionID,
> SIP_NULLPTR, SIP_NOT_NONE, &sipValState, &sipIsErr));
> 
>     if (sipIsErr)
>         return -1;
> 
>     sipCpp->referenceDenms = *sipVal; <- 347
> 
>     sipReleaseType(sipVal, sipType_std_vector_0101ActionID, 
> sipValState);
> 
>     return 0;
> }
> 
> 
> The python code:
> 
> 
> actionId1 = denm_pdu.ActionID()
> actionId1.originatingStationID = 4294902015
> actionId1.sequenceNumber = 12
> actionId2 = denm_pdu.ActionID()
> actionId2.originatingStationID = 4294902016
> actionId2.sequenceNumber = 13
> roadWorksContainerExtended.referenceDenms = [actionId1, actionId2]
> 
> The sip-file:
> 
> ...
> 
> typedef std::vector<ActionID*> ReferenceDenms;
> 
> ...
> 
> 
> struct RoadWorksContainerExtended {
> 
> %TypeHeaderCode
> #include <denm-desc.h>
> %End
> 
>    ReferenceDenms referenceDenms;
>    RoadWorksContainerExtended ();
> } ;
> 
> 
> 
> The MappedType:
> 
> 
> template<TYPE>
> %MappedType std::vector<TYPE *>
> {
> %TypeHeaderCode
> #include <vector>
> #include <iostream>
> %End
> 
> // from python
> %ConvertToTypeCode
>     const sipTypeDef* kpTypeDef = sipFindType("TYPE");
> 
>     if (!kpTypeDef) {
>         std::cout << "Error in vector_ptr.sip - ConvertToTypeCode: "
>                      "Could not find a matching type for 'TYPE'\n";
>         return 0;
>     }
> 
>     // See if we are just being asked to check the type of the Python 
> object.
>     if (sipIsErr == NULL) {
>         // Check it is a list.
>         if (!PyList_Check(sipPy)) {
>             return 0;
>         }
> 
>         // Now check each element of the list is of the type we expect.
>         // The template is for a pointer type so we don't disallow 
> None.
>         for (Py_ssize_t i = 0; i < PyList_GET_SIZE(sipPy); ++i) {
> 
>             PyObject *item = PyList_GET_ITEM(sipPy, i);
> 
>             if (!sipCanConvertToType(item, kpTypeDef, SIP_NOT_NONE)) {
>                 std::cout << "Error in vector_ptr.sip - 
> ConvertToTypeCode: "
>                              "Object cannot be converted to TYPE'\n";
>                 return 0;
>             }
>         }
>         return 1;
>     }
> 
>     Py_ssize_t len = PyList_GET_SIZE(sipPy);
>     // Create the instance on the heap.
>     std::vector<TYPE *> *v = new std::vector<TYPE *>;
>     v->reserve(len);
> 
>     for (Py_ssize_t i = 0; i < len; ++i) {
> 
>         int state;
>         // Use the SIP API to convert the Python object to the
> corresponding C++ instance.  Note that we apply any
>         // ownership transfer to the list itself, not the individual 
> elements.
>         TYPE *t = static_cast<TYPE
> *>(sipConvertToType(PyList_GET_ITEM(sipPy, i), kpTypeDef, NULL,
> SIP_NOT_NONE, &state, sipIsErr));
> 
>         // Deal with any errors
>         if (*sipIsErr) {
>             std::cout << "Error in vector_ptr.sip - ConvertToTypeCode: 
> "
>                          "Error occurred - cleanup on the heap ...'\n";
>             // Tidy up.
>             sipReleaseType(t, kpTypeDef, state);
>             delete v;
> 
>             // There is nothing on the heap.
>             return 0;
>         }
> 
>         // Add the pointer to the C++ instance.
>         v->push_back(t);
>     }
> 
>     // Return the instance on the heap.
>     *sipCppPtr = v;
> 
>     // Apply the normal transfer.
>     return sipGetState(sipTransferObj);
> %End
> 
> // from c++
> %ConvertFromTypeCode
>     PyObject *l;
> 
>     const sipTypeDef* kpTypeDef = sipFindType("TYPE");
> 
>     if (!kpTypeDef) {
>         std::cout << "Error in vector_ptr.sip - ConvertFromTypeCode: "
>                      "Could not find a matching type for 'TYPE'\n";
>         return NULL;
>     }
> 
>     // Create the Python list of the correct length.
>     if ((l = PyList_New(sipCpp->size())) == NULL) {
>         std::cout << "Error in vector_ptr.sip - ConvertFromTypeCode: "
>                      "Could not create python list'\n";
>         return NULL;
>     }
> 
>     int i = 0;
>     // Go through each element in the C++ instance and convert it to
> the corresponding Python object.
>     for(std::vector<TYPE *>::iterator iter = sipCpp->begin(); iter !=
> sipCpp->end(); ++iter) {
>         TYPE *t = *iter;
>         PyObject *tobj;
> 
>         if ((tobj = sipConvertFromType(t, kpTypeDef, sipTransferObj)) 
> == NULL) {
> 
>             // There was an error so garbage collect the Python list.
>             Py_XDECREF(tobj);
>             Py_XDECREF(l);
>             std::cout << "Error in vector_ptr.sip - 
> ConvertFromTypeCode: "
>                          "Could not convert type in Python list'\n";
>             return NULL;
>         }
> 
>         PyList_SET_ITEM(l, i++, tobj);
>     }
> 
>     // Return the Python list.
>     return l;
> %End
> };
> 
> 
> 
> I tried changing the ownership to c++ of every item when creating
> vector in the MappedType - ConvertToTypeCode but no effect.
> 
> Running sip-install with tracing or debug option gives no further 
> information.
> 
> 
> Does someone have an idea how this leak could be closed or what else
> one could try to close it?
> 
> Any help would be appreciated.

Your best chance of getting help is to produce a short, complete example 
that demonstrates the problem and that people can actually run.

Phil


More information about the PyQt mailing list