bugGNU Octave - Bugs: bug #52123, Indirect memory leak in...


bug #52123: Indirect memory leak in cdef_manager::initialize ()

Submitter:  Piotr Held <jsoh425>
Submitted:  Tue 26 Sep 2017 10:46:39 PM UTC
Category:  Interpreter Severity:  3 - Normal
Priority:  5 - Normal Item Group:  Segfault, Bus Error, etc.
Status:  None Assigned to:  None
Originator Name:  Piotr Held Open/Closed:  * Open
Release:  * dev Operating System:  * Any
Fixed Release:  None Planned Release:  None
* Mandatory Fields

Add a New Comment Rich Markup

Wed 27 Sep 2017 05:27:48 PM UTC, comment #2: 

What I meant when I was talking about commenting out code was to comment out the entire cdef_manager::initialize() method (from line 3360 to line 3658 inclusive). This removes all leaks, at least on my system.

I added the code snippet to only show where a recursion occurs. I commented some code out of the snippet to make it more readable (I wanted the most important code to be visible).

To describe the recursion in other terms, using the code as is, after line 3368 the  tmp_meta_class.rep->refcount is 5:

  1. tmp_meta_class
  2. tmp_meta_class.get_rep()->klass (or in other words: (dynamic_cast&ltcdef_object_base *&gt(tmp_meta_class.rep))->klass )
  3. tmp_handle.get_rep()->klass (or in other words: (dynamic_cast&ltcdef_object_base *&gt(tmp_handle.rep))->klass )
  4. m_meta_class
  5. entry in cdef_manager::m_all_classes

The recursion exists between instances 1 and 2.
If all of the following:

  1. tmp_meta_class
  2. tmp_handle (or tmp_handle.get_rep()->klass)
  3. m_meta_class
  4. cdef_manager::m_all_classes

get destroyed the tmp_meta_class.rep->refcount will still be 1 (even though tmp_meta_class itself does not exist).

Piotr Held <jsoh425>
Wed 27 Sep 2017 03:43:12 AM UTC, comment #1: 

I tried commenting out the line you suggested in ov-classdef.cc:3367.

//  tmp_meta_class.set_class (tmp_meta_class);

But, when I run the AddressSanitizer I actually see an additional leak.

Rik <rik5>
Group administrator
Tue 26 Sep 2017 10:46:39 PM UTC, original submission:  

I have been getting a large printout memory leak every time I would run Octave with the addresss sanitizer. The printout would look more or less like this:

Indirect leak of 6 byte(s) in 1 object(s) allocated from:
    #0 0x7f2b7fa6ed70 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.3+0xc2d70)
    #1 0x7f2b7e4c2155 in Array<char>::ArrayRep::ArrayRep(long) ../build_source/liboctave/array/Array.h:159
    #2 0x7f2b7e4bd837 in Array<char>::Array(dim_vector const&) ../build_source/liboctave/array/Array.h:265
    #3 0x7f2b7cac9819 in Array<char>::resize1(long, char const&) (/mnt/sdb2/Projects/last_octave_ever/dbg-build/liboctave/.libs/liboctave.so.4+0x54f819)
    #4 0x7f2b7cac9abf in Array<char>::resize1(long) ../build_source/liboctave/array/Array.h:612
    #5 0x7f2b7ce28c1c in charNDArray::charNDArray(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ../build_source/liboctave/array/chNDArray.cc:65
    #6 0x7f2b7ea2b92f in octave_char_matrix::octave_char_matrix(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ../build_source/libinterp/octave-value/ov-ch-mat.h:73
    #7 0x7f2b7ea2bc9a in octave_char_matrix_str::octave_char_matrix_str(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ../build_source/libinterp/octave-value/ov-str-mat.h:74
    #8 0x7f2b7ea2bf10 in octave_char_matrix_sq_str::octave_char_matrix_sq_str(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ../build_source/libinterp/octave-value/ov-str-mat.h:203
    #9 0x7f2b7e9fd460 in octave_value::octave_value(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char) ../build_source/libinterp/octave-value/ov.cc:780
    #10 0x7f2b7e8e5677 in cdef_class::cdef_class_rep::set_name(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ../build_source/libinterp/octave-value/ov-classdef.h:686
    #11 0x7f2b7e8e60d0 in cdef_class::cdef_class(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::list<cdef_class, std::allocator<cdef_class> > const&) ../build_source/libinterp/octave-value/ov-classdef.h:827
    #12 0x7f2b7e8c9275 in make_class ../build_source/libinterp/octave-value/ov-classdef.cc:647
    #13 0x7f2b7e8e0eb1 in cdef_manager::initialize() ../build_source/libinterp/octave-value/ov-classdef.cc:3360
    #14 0x7f2b7ef65b69 in octave::interpreter::interpreter(octave::application*) ../build_source/libinterp/corefcn/interpreter.cc:421
    #15 0x7f2b7e3ad5e7 in octave::application::create_interpreter() ../build_source/libinterp/octave.cc:322
    #16 0x7f2b7e3add2f in octave::cli_application::execute() ../build_source/libinterp/octave.cc:382
    #17 0x55cf2d0e1233 in main ../build_source/src/main-cli.cc:90
    #18 0x7f2b7b5142b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)

With a summary of about 180K bytes leaked. I haven't seen a bug report for this issue so I'm submitting it.

I have been able to narrow it down to cdef_manager::initialize() as the problem (I reached this conclusion by commenting out the entire function).

I believe what is happening is this (from cdef_manager::initialize() ):

//other code

cdef_class tmp_meta_class
  // other code
  = make_meta_class ("meta.class", tmp_handle);

// other code
tmp_meta_class.set_class (tmp_meta_class);

When calling cdef_class.set_class() basically creates a recursive loop that disallows tmp_meta_class.rep to be destroyed.

It has been helpful for me to look at the inheritance diagrams for cdef_object and cdef_object_rep to not get lost in all of the different C++ classes.

This loop looks like this:
tmp_meta_class.rep == tmp_meta_class.get_class ().rep
For more explanation cdef_class::get_class() returns the property cdef_class cdef_object_base::klass property which holds "The class of the object" (from the source comment).
Which means that when there's only 1 tmp_meta_class.rep left the tmp_meta_class.rep->refcount == 2. And the only way to destroy tmp_meta_class.rep is to first destroy the cdef_object_base::klass.

Piotr Held <jsoh425>


(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:

No files currently attached


Depends on the following items: None found

Items that depend on this one: None found


Carbon-Copy List
  • -email is unavailable- added by rik5 (Posted a comment)
  • -email is unavailable- added by jsoh425 (Submitted the item)

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only project members can vote.


    Follows 1 latest change.

    Date Changed by Updated Field Previous Value => Replaced by
    2017-10-02 rik5 SummaryMemory leak in cdef_manager::initialize () Indirect memory leak in cdef_manager::initialize ()

    Back to the top

    Powered by Savane 3.12.
    Corresponding source code