Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exiting from the cocotbenv wrapper does not always kill the specific simulator #4

Closed
stuarthodgson opened this issue Jun 17, 2013 · 1 comment
Labels
type:bug a bug in existing functionality

Comments

@stuarthodgson
Copy link
Collaborator

No description provided.

@stuarthodgson
Copy link
Collaborator Author

fixed in dd8625b

chiggs pushed a commit to chiggs/cocotb that referenced this issue Aug 21, 2015
Issue cocotb#17: Add recursive iteration test
chiggs added a commit to chiggs/cocotb that referenced this issue Sep 4, 2017
ktbarrett referenced this issue in ktbarrett/cocotb Mar 6, 2020
Issue cocotb#17: Add recursive iteration test
ktbarrett referenced this issue in ktbarrett/cocotb Mar 6, 2020
imphil added a commit to imphil/cocotb that referenced this issue Oct 12, 2022
cocotb crashes when running the mixed language example with Riviera-PRO,
using a Verilog toplevel and the pre-built binary wheels (manylinux2014,
built on CentOS7).

Valgrind reports

```
 COUT: ==6174== Invalid free() / delete / delete[] / realloc()
 COUT: ==6174==    at 0xC78271B: operator delete(void*) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
 COUT: ==6174==    by 0x1CE1AC80: std::string::assign(std::string const&) (in /home/philipp/src/cocotb-riviera-debug-3078-binary/.direnv/python-3.10.7/lib/python3.10/site-packages/cocotb/libs/libgpilog.so)
 COUT: ==6174==    by 0x1CDE56C6: GpiObjHdl::initialise(std::string const&, std::string const&) (GpiCbHdl.cpp:74)
 COUT: ==6174==    by 0x1CE3D587: VhpiObjHdl::initialise(std::string const&, std::string const&) (VhpiCbHdl.cpp:258)
 COUT: ==6174==    by 0x1CE46932: VhpiImpl::create_gpi_obj_from_handle(unsigned int*, std::string&, std::string&) (VhpiImpl.cpp:462)
 COUT: ==6174==    by 0x1CE470E5: VhpiImpl::native_check_create(std::string&, GpiObjHdl*) (VhpiImpl.cpp:575)
 COUT: ==6174==    by 0x1CDE6E1E: gpi_get_handle_by_name_(GpiObjHdl*, std::string, GpiImplInterface*) (GpiCommon.cpp:297)
 COUT: ==6174==    by 0x1CDE711C: gpi_get_handle_by_name (GpiCommon.cpp:339)
 COUT: ==6174==    by 0x14BF9595: get_handle_by_name((anonymous namespace)::gpi_hdl_Object<GpiObjHdl*>*, _object*) (simulatormodule.cpp:681)
 COUT: ==6174==    by 0x1CF7351A: ??? (in /usr/lib64/libpython3.10.so.1.0)
 COUT: ==6174==    by 0x1CF77976: _PyEval_EvalFrameDefault (in /usr/lib64/libpython3.10.so.1.0)
 COUT: ==6174==    by 0x1CF76492: ??? (in /usr/lib64/libpython3.10.so.1.0)
 COUT: ==6174==  Address 0xed71320 is 0 bytes inside data symbol "_ZNSs4_Rep20_S_empty_rep_storageE"
 COUT: ==6174==
```

A GDB backtrace looks identical, with slightly more symbol information:

```
 #0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
 #1  0x00007fe519095893 in __pthread_kill_internal (signo=6, threadid=<optimized out>) at pthread_kill.c:78
 cocotb#2  0x00007fe519042846 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
 cocotb#3  0x00007fe51902b81c in __GI_abort () at abort.c:79
 cocotb#4  0x00007fe5190889ae in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7fe5191b144f "%s\n") at ../sysdeps/posix/libc_fatal.c:155
 cocotb#5  0x00007fe5190a014c in malloc_printerr (str=str@entry=0x7fe5191af0a9 "free(): invalid pointer") at malloc.c:5660
 cocotb#6  0x00007fe5190a211c in _int_free (av=<optimized out>, p=<optimized out>, have_lock=have_lock@entry=0) at malloc.c:4435
 cocotb#7  0x00007fe5190a4b13 in __GI___libc_free (mem=<optimized out>) at malloc.c:3385
 cocotb#8  0x00007fe514f9ea91 in std::string::assign(std::string const&) ()
 from /home/philipp/src/cocotb-riviera-debug-3078-binary/.direnv/python-3.10.7/lib64/python3.10/site-packages/cocotb/libs/libgpilog.so
 cocotb#9  0x00007fe514fbe875 in std::string::operator= (__str=..., this=0x7fe50e0d2d58) at /opt/rh/devtoolset-10/root/usr/include/c++/10/bits/basic_string.h:3726
 cocotb#10 GpiObjHdl::initialise (this=0x7fe50e0d2d30, name=..., fq_name=...) at cocotb/share/lib/gpi/GpiCbHdl.cpp:74
 cocotb#11 0x00007fe514f761b3 in VhpiImpl::create_gpi_obj_from_handle (this=0x7fe50c17df00, new_hdl=<optimized out>, name=..., fq_name=...) at cocotb/share/lib/vhpi/VhpiImpl.cpp:462
 cocotb#12 0x00007fe514f796cb in VhpiImpl::native_check_create (this=0x7fe50c17df00, name=..., parent=<optimized out>) at cocotb/share/lib/vhpi/VhpiImpl.cpp:575
 cocotb#13 0x00007fe514fc1955 in gpi_get_handle_by_name_ (skip_impl=0x0, name=..., parent=0x7fe50c0b9ea0) at cocotb/share/lib/gpi/GpiCommon.cpp:297
 cocotb#14 gpi_get_handle_by_name_ (parent=0x7fe50c0b9ea0, name=..., skip_impl=0x0) at cocotb/share/lib/gpi/GpiCommon.cpp:260
 cocotb#15 0x00007fe514fc1b01 in gpi_get_handle_by_name (base=0x7fe50c0b9ea0, name=0x7fe5001192a0 "i_swapper_vhdl") at cocotb/share/lib/gpi/GpiCommon.cpp:339
 cocotb#16 0x00007fe5141136c6 in get_handle_by_name (self=0x7fe5000c07f0, args=<optimized out>) at cocotb/share/lib/simulator/simulatormodule.cpp:681
 cocotb#17 0x00007fe51471d51b in method_vectorcall_VARARGS (func=<method_descriptor at remote 0x7fe514193970>, args=0x7fe5000f4ee8, nargsf=<optimized out>, kwnames=0x0)
 at Objects/descrobject.c:311
```

This Valgrind report indicates that `free()` is called on an object which isn't
intended ot be freed, and that's
`std::basic_string<char, std::char_traits<char>, std::allocator<char>>::_Rep::_S_empty_rep_storage`
in this case.

This symbol within libstdc++ is documented as
(https://github.com/gcc-mirror/gcc/blob/releases/gcc-10/libstdc++-v3/include/bits/basic_string.h#L3230):

```
	// The following storage is init'd to 0 by the linker, resulting
        // (carefully) in an empty string with one reference.
        static size_type _S_empty_rep_storage[];
```

What seems to happen is that when we call

```
int GpiObjHdl::initialise(const std::string &name, const std::string &fq_name) {
    m_name = name;
    ...
}
```

`m_name` gets free()'d after it got the new `name` assigned. Since
`m_name` was empty/unset before, free() tries to access
`_S_empty_rep_storage`.

Assigning an explicit default value to `m_name` works around the problem
nicely.

That being said, it's unclear why free() is getting invoked on that symbol
at all. It's also unclear why the same code only causes issues for a
single mixed signal testcase. Finding out is unlikely to be trivial:
cocotb wheels link (parts of) a static libstdc++ from RHEL7, while
Riviera-PRO comes with its own dynamically-linked libstdc++. It's my
hope that the workaround in this commit serves us long enough before it
becomes a problem in itself.

Fixes cocotb#3078
imphil added a commit that referenced this issue Oct 13, 2022
cocotb crashes when running the mixed language example with Riviera-PRO,
using a Verilog toplevel and the pre-built binary wheels (manylinux2014,
built on CentOS7).

Valgrind reports

```
 COUT: ==6174== Invalid free() / delete / delete[] / realloc()
 COUT: ==6174==    at 0xC78271B: operator delete(void*) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
 COUT: ==6174==    by 0x1CE1AC80: std::string::assign(std::string const&) (in /home/philipp/src/cocotb-riviera-debug-3078-binary/.direnv/python-3.10.7/lib/python3.10/site-packages/cocotb/libs/libgpilog.so)
 COUT: ==6174==    by 0x1CDE56C6: GpiObjHdl::initialise(std::string const&, std::string const&) (GpiCbHdl.cpp:74)
 COUT: ==6174==    by 0x1CE3D587: VhpiObjHdl::initialise(std::string const&, std::string const&) (VhpiCbHdl.cpp:258)
 COUT: ==6174==    by 0x1CE46932: VhpiImpl::create_gpi_obj_from_handle(unsigned int*, std::string&, std::string&) (VhpiImpl.cpp:462)
 COUT: ==6174==    by 0x1CE470E5: VhpiImpl::native_check_create(std::string&, GpiObjHdl*) (VhpiImpl.cpp:575)
 COUT: ==6174==    by 0x1CDE6E1E: gpi_get_handle_by_name_(GpiObjHdl*, std::string, GpiImplInterface*) (GpiCommon.cpp:297)
 COUT: ==6174==    by 0x1CDE711C: gpi_get_handle_by_name (GpiCommon.cpp:339)
 COUT: ==6174==    by 0x14BF9595: get_handle_by_name((anonymous namespace)::gpi_hdl_Object<GpiObjHdl*>*, _object*) (simulatormodule.cpp:681)
 COUT: ==6174==    by 0x1CF7351A: ??? (in /usr/lib64/libpython3.10.so.1.0)
 COUT: ==6174==    by 0x1CF77976: _PyEval_EvalFrameDefault (in /usr/lib64/libpython3.10.so.1.0)
 COUT: ==6174==    by 0x1CF76492: ??? (in /usr/lib64/libpython3.10.so.1.0)
 COUT: ==6174==  Address 0xed71320 is 0 bytes inside data symbol "_ZNSs4_Rep20_S_empty_rep_storageE"
 COUT: ==6174==
```

A GDB backtrace looks identical, with slightly more symbol information:

```
 #0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
 #1  0x00007fe519095893 in __pthread_kill_internal (signo=6, threadid=<optimized out>) at pthread_kill.c:78
 #2  0x00007fe519042846 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
 #3  0x00007fe51902b81c in __GI_abort () at abort.c:79
 #4  0x00007fe5190889ae in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7fe5191b144f "%s\n") at ../sysdeps/posix/libc_fatal.c:155
 #5  0x00007fe5190a014c in malloc_printerr (str=str@entry=0x7fe5191af0a9 "free(): invalid pointer") at malloc.c:5660
 #6  0x00007fe5190a211c in _int_free (av=<optimized out>, p=<optimized out>, have_lock=have_lock@entry=0) at malloc.c:4435
 #7  0x00007fe5190a4b13 in __GI___libc_free (mem=<optimized out>) at malloc.c:3385
 #8  0x00007fe514f9ea91 in std::string::assign(std::string const&) ()
 from /home/philipp/src/cocotb-riviera-debug-3078-binary/.direnv/python-3.10.7/lib64/python3.10/site-packages/cocotb/libs/libgpilog.so
 #9  0x00007fe514fbe875 in std::string::operator= (__str=..., this=0x7fe50e0d2d58) at /opt/rh/devtoolset-10/root/usr/include/c++/10/bits/basic_string.h:3726
 #10 GpiObjHdl::initialise (this=0x7fe50e0d2d30, name=..., fq_name=...) at cocotb/share/lib/gpi/GpiCbHdl.cpp:74
 #11 0x00007fe514f761b3 in VhpiImpl::create_gpi_obj_from_handle (this=0x7fe50c17df00, new_hdl=<optimized out>, name=..., fq_name=...) at cocotb/share/lib/vhpi/VhpiImpl.cpp:462
 #12 0x00007fe514f796cb in VhpiImpl::native_check_create (this=0x7fe50c17df00, name=..., parent=<optimized out>) at cocotb/share/lib/vhpi/VhpiImpl.cpp:575
 #13 0x00007fe514fc1955 in gpi_get_handle_by_name_ (skip_impl=0x0, name=..., parent=0x7fe50c0b9ea0) at cocotb/share/lib/gpi/GpiCommon.cpp:297
 #14 gpi_get_handle_by_name_ (parent=0x7fe50c0b9ea0, name=..., skip_impl=0x0) at cocotb/share/lib/gpi/GpiCommon.cpp:260
 #15 0x00007fe514fc1b01 in gpi_get_handle_by_name (base=0x7fe50c0b9ea0, name=0x7fe5001192a0 "i_swapper_vhdl") at cocotb/share/lib/gpi/GpiCommon.cpp:339
 #16 0x00007fe5141136c6 in get_handle_by_name (self=0x7fe5000c07f0, args=<optimized out>) at cocotb/share/lib/simulator/simulatormodule.cpp:681
 #17 0x00007fe51471d51b in method_vectorcall_VARARGS (func=<method_descriptor at remote 0x7fe514193970>, args=0x7fe5000f4ee8, nargsf=<optimized out>, kwnames=0x0)
 at Objects/descrobject.c:311
```

This Valgrind report indicates that `free()` is called on an object which isn't
intended ot be freed, and that's
`std::basic_string<char, std::char_traits<char>, std::allocator<char>>::_Rep::_S_empty_rep_storage`
in this case.

This symbol within libstdc++ is documented as
(https://github.com/gcc-mirror/gcc/blob/releases/gcc-10/libstdc++-v3/include/bits/basic_string.h#L3230):

```
	// The following storage is init'd to 0 by the linker, resulting
        // (carefully) in an empty string with one reference.
        static size_type _S_empty_rep_storage[];
```

What seems to happen is that when we call

```
int GpiObjHdl::initialise(const std::string &name, const std::string &fq_name) {
    m_name = name;
    ...
}
```

`m_name` gets free()'d after it got the new `name` assigned. Since
`m_name` was empty/unset before, free() tries to access
`_S_empty_rep_storage`.

Assigning an explicit default value to `m_name` works around the problem
nicely.

That being said, it's unclear why free() is getting invoked on that symbol
at all. It's also unclear why the same code only causes issues for a
single mixed signal testcase. Finding out is unlikely to be trivial:
cocotb wheels link (parts of) a static libstdc++ from RHEL7, while
Riviera-PRO comes with its own dynamically-linked libstdc++. It's my
hope that the workaround in this commit serves us long enough before it
becomes a problem in itself.

Fixes #3078
imphil added a commit to imphil/cocotb that referenced this issue Nov 10, 2022
cocotb crashes when running the mixed language example with Riviera-PRO,
using a Verilog toplevel and the pre-built binary wheels (manylinux2014,
built on CentOS7).

Valgrind reports

```
 COUT: ==6174== Invalid free() / delete / delete[] / realloc()
 COUT: ==6174==    at 0xC78271B: operator delete(void*) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
 COUT: ==6174==    by 0x1CE1AC80: std::string::assign(std::string const&) (in /home/philipp/src/cocotb-riviera-debug-3078-binary/.direnv/python-3.10.7/lib/python3.10/site-packages/cocotb/libs/libgpilog.so)
 COUT: ==6174==    by 0x1CDE56C6: GpiObjHdl::initialise(std::string const&, std::string const&) (GpiCbHdl.cpp:74)
 COUT: ==6174==    by 0x1CE3D587: VhpiObjHdl::initialise(std::string const&, std::string const&) (VhpiCbHdl.cpp:258)
 COUT: ==6174==    by 0x1CE46932: VhpiImpl::create_gpi_obj_from_handle(unsigned int*, std::string&, std::string&) (VhpiImpl.cpp:462)
 COUT: ==6174==    by 0x1CE470E5: VhpiImpl::native_check_create(std::string&, GpiObjHdl*) (VhpiImpl.cpp:575)
 COUT: ==6174==    by 0x1CDE6E1E: gpi_get_handle_by_name_(GpiObjHdl*, std::string, GpiImplInterface*) (GpiCommon.cpp:297)
 COUT: ==6174==    by 0x1CDE711C: gpi_get_handle_by_name (GpiCommon.cpp:339)
 COUT: ==6174==    by 0x14BF9595: get_handle_by_name((anonymous namespace)::gpi_hdl_Object<GpiObjHdl*>*, _object*) (simulatormodule.cpp:681)
 COUT: ==6174==    by 0x1CF7351A: ??? (in /usr/lib64/libpython3.10.so.1.0)
 COUT: ==6174==    by 0x1CF77976: _PyEval_EvalFrameDefault (in /usr/lib64/libpython3.10.so.1.0)
 COUT: ==6174==    by 0x1CF76492: ??? (in /usr/lib64/libpython3.10.so.1.0)
 COUT: ==6174==  Address 0xed71320 is 0 bytes inside data symbol "_ZNSs4_Rep20_S_empty_rep_storageE"
 COUT: ==6174==
```

A GDB backtrace looks identical, with slightly more symbol information:

```
 #0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
 #1  0x00007fe519095893 in __pthread_kill_internal (signo=6, threadid=<optimized out>) at pthread_kill.c:78
 cocotb#2  0x00007fe519042846 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
 cocotb#3  0x00007fe51902b81c in __GI_abort () at abort.c:79
 cocotb#4  0x00007fe5190889ae in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7fe5191b144f "%s\n") at ../sysdeps/posix/libc_fatal.c:155
 cocotb#5  0x00007fe5190a014c in malloc_printerr (str=str@entry=0x7fe5191af0a9 "free(): invalid pointer") at malloc.c:5660
 cocotb#6  0x00007fe5190a211c in _int_free (av=<optimized out>, p=<optimized out>, have_lock=have_lock@entry=0) at malloc.c:4435
 cocotb#7  0x00007fe5190a4b13 in __GI___libc_free (mem=<optimized out>) at malloc.c:3385
 cocotb#8  0x00007fe514f9ea91 in std::string::assign(std::string const&) ()
 from /home/philipp/src/cocotb-riviera-debug-3078-binary/.direnv/python-3.10.7/lib64/python3.10/site-packages/cocotb/libs/libgpilog.so
 cocotb#9  0x00007fe514fbe875 in std::string::operator= (__str=..., this=0x7fe50e0d2d58) at /opt/rh/devtoolset-10/root/usr/include/c++/10/bits/basic_string.h:3726
 cocotb#10 GpiObjHdl::initialise (this=0x7fe50e0d2d30, name=..., fq_name=...) at cocotb/share/lib/gpi/GpiCbHdl.cpp:74
 cocotb#11 0x00007fe514f761b3 in VhpiImpl::create_gpi_obj_from_handle (this=0x7fe50c17df00, new_hdl=<optimized out>, name=..., fq_name=...) at cocotb/share/lib/vhpi/VhpiImpl.cpp:462
 cocotb#12 0x00007fe514f796cb in VhpiImpl::native_check_create (this=0x7fe50c17df00, name=..., parent=<optimized out>) at cocotb/share/lib/vhpi/VhpiImpl.cpp:575
 cocotb#13 0x00007fe514fc1955 in gpi_get_handle_by_name_ (skip_impl=0x0, name=..., parent=0x7fe50c0b9ea0) at cocotb/share/lib/gpi/GpiCommon.cpp:297
 cocotb#14 gpi_get_handle_by_name_ (parent=0x7fe50c0b9ea0, name=..., skip_impl=0x0) at cocotb/share/lib/gpi/GpiCommon.cpp:260
 cocotb#15 0x00007fe514fc1b01 in gpi_get_handle_by_name (base=0x7fe50c0b9ea0, name=0x7fe5001192a0 "i_swapper_vhdl") at cocotb/share/lib/gpi/GpiCommon.cpp:339
 cocotb#16 0x00007fe5141136c6 in get_handle_by_name (self=0x7fe5000c07f0, args=<optimized out>) at cocotb/share/lib/simulator/simulatormodule.cpp:681
 cocotb#17 0x00007fe51471d51b in method_vectorcall_VARARGS (func=<method_descriptor at remote 0x7fe514193970>, args=0x7fe5000f4ee8, nargsf=<optimized out>, kwnames=0x0)
 at Objects/descrobject.c:311
```

This Valgrind report indicates that `free()` is called on an object which isn't
intended ot be freed, and that's
`std::basic_string<char, std::char_traits<char>, std::allocator<char>>::_Rep::_S_empty_rep_storage`
in this case.

This symbol within libstdc++ is documented as
(https://github.com/gcc-mirror/gcc/blob/releases/gcc-10/libstdc++-v3/include/bits/basic_string.h#L3230):

```
	// The following storage is init'd to 0 by the linker, resulting
        // (carefully) in an empty string with one reference.
        static size_type _S_empty_rep_storage[];
```

What seems to happen is that when we call

```
int GpiObjHdl::initialise(const std::string &name, const std::string &fq_name) {
    m_name = name;
    ...
}
```

`m_name` gets free()'d after it got the new `name` assigned. Since
`m_name` was empty/unset before, free() tries to access
`_S_empty_rep_storage`.

Assigning an explicit default value to `m_name` works around the problem
nicely.

That being said, it's unclear why free() is getting invoked on that symbol
at all. It's also unclear why the same code only causes issues for a
single mixed signal testcase. Finding out is unlikely to be trivial:
cocotb wheels link (parts of) a static libstdc++ from RHEL7, while
Riviera-PRO comes with its own dynamically-linked libstdc++. It's my
hope that the workaround in this commit serves us long enough before it
becomes a problem in itself.

Fixes cocotb#3078
imphil added a commit that referenced this issue Nov 12, 2022
cocotb crashes when running the mixed language example with Riviera-PRO,
using a Verilog toplevel and the pre-built binary wheels (manylinux2014,
built on CentOS7).

Valgrind reports

```
 COUT: ==6174== Invalid free() / delete / delete[] / realloc()
 COUT: ==6174==    at 0xC78271B: operator delete(void*) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
 COUT: ==6174==    by 0x1CE1AC80: std::string::assign(std::string const&) (in /home/philipp/src/cocotb-riviera-debug-3078-binary/.direnv/python-3.10.7/lib/python3.10/site-packages/cocotb/libs/libgpilog.so)
 COUT: ==6174==    by 0x1CDE56C6: GpiObjHdl::initialise(std::string const&, std::string const&) (GpiCbHdl.cpp:74)
 COUT: ==6174==    by 0x1CE3D587: VhpiObjHdl::initialise(std::string const&, std::string const&) (VhpiCbHdl.cpp:258)
 COUT: ==6174==    by 0x1CE46932: VhpiImpl::create_gpi_obj_from_handle(unsigned int*, std::string&, std::string&) (VhpiImpl.cpp:462)
 COUT: ==6174==    by 0x1CE470E5: VhpiImpl::native_check_create(std::string&, GpiObjHdl*) (VhpiImpl.cpp:575)
 COUT: ==6174==    by 0x1CDE6E1E: gpi_get_handle_by_name_(GpiObjHdl*, std::string, GpiImplInterface*) (GpiCommon.cpp:297)
 COUT: ==6174==    by 0x1CDE711C: gpi_get_handle_by_name (GpiCommon.cpp:339)
 COUT: ==6174==    by 0x14BF9595: get_handle_by_name((anonymous namespace)::gpi_hdl_Object<GpiObjHdl*>*, _object*) (simulatormodule.cpp:681)
 COUT: ==6174==    by 0x1CF7351A: ??? (in /usr/lib64/libpython3.10.so.1.0)
 COUT: ==6174==    by 0x1CF77976: _PyEval_EvalFrameDefault (in /usr/lib64/libpython3.10.so.1.0)
 COUT: ==6174==    by 0x1CF76492: ??? (in /usr/lib64/libpython3.10.so.1.0)
 COUT: ==6174==  Address 0xed71320 is 0 bytes inside data symbol "_ZNSs4_Rep20_S_empty_rep_storageE"
 COUT: ==6174==
```

A GDB backtrace looks identical, with slightly more symbol information:

```
 #0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
 #1  0x00007fe519095893 in __pthread_kill_internal (signo=6, threadid=<optimized out>) at pthread_kill.c:78
 #2  0x00007fe519042846 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
 #3  0x00007fe51902b81c in __GI_abort () at abort.c:79
 #4  0x00007fe5190889ae in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7fe5191b144f "%s\n") at ../sysdeps/posix/libc_fatal.c:155
 #5  0x00007fe5190a014c in malloc_printerr (str=str@entry=0x7fe5191af0a9 "free(): invalid pointer") at malloc.c:5660
 #6  0x00007fe5190a211c in _int_free (av=<optimized out>, p=<optimized out>, have_lock=have_lock@entry=0) at malloc.c:4435
 #7  0x00007fe5190a4b13 in __GI___libc_free (mem=<optimized out>) at malloc.c:3385
 #8  0x00007fe514f9ea91 in std::string::assign(std::string const&) ()
 from /home/philipp/src/cocotb-riviera-debug-3078-binary/.direnv/python-3.10.7/lib64/python3.10/site-packages/cocotb/libs/libgpilog.so
 #9  0x00007fe514fbe875 in std::string::operator= (__str=..., this=0x7fe50e0d2d58) at /opt/rh/devtoolset-10/root/usr/include/c++/10/bits/basic_string.h:3726
 #10 GpiObjHdl::initialise (this=0x7fe50e0d2d30, name=..., fq_name=...) at cocotb/share/lib/gpi/GpiCbHdl.cpp:74
 #11 0x00007fe514f761b3 in VhpiImpl::create_gpi_obj_from_handle (this=0x7fe50c17df00, new_hdl=<optimized out>, name=..., fq_name=...) at cocotb/share/lib/vhpi/VhpiImpl.cpp:462
 #12 0x00007fe514f796cb in VhpiImpl::native_check_create (this=0x7fe50c17df00, name=..., parent=<optimized out>) at cocotb/share/lib/vhpi/VhpiImpl.cpp:575
 #13 0x00007fe514fc1955 in gpi_get_handle_by_name_ (skip_impl=0x0, name=..., parent=0x7fe50c0b9ea0) at cocotb/share/lib/gpi/GpiCommon.cpp:297
 #14 gpi_get_handle_by_name_ (parent=0x7fe50c0b9ea0, name=..., skip_impl=0x0) at cocotb/share/lib/gpi/GpiCommon.cpp:260
 #15 0x00007fe514fc1b01 in gpi_get_handle_by_name (base=0x7fe50c0b9ea0, name=0x7fe5001192a0 "i_swapper_vhdl") at cocotb/share/lib/gpi/GpiCommon.cpp:339
 #16 0x00007fe5141136c6 in get_handle_by_name (self=0x7fe5000c07f0, args=<optimized out>) at cocotb/share/lib/simulator/simulatormodule.cpp:681
 #17 0x00007fe51471d51b in method_vectorcall_VARARGS (func=<method_descriptor at remote 0x7fe514193970>, args=0x7fe5000f4ee8, nargsf=<optimized out>, kwnames=0x0)
 at Objects/descrobject.c:311
```

This Valgrind report indicates that `free()` is called on an object which isn't
intended ot be freed, and that's
`std::basic_string<char, std::char_traits<char>, std::allocator<char>>::_Rep::_S_empty_rep_storage`
in this case.

This symbol within libstdc++ is documented as
(https://github.com/gcc-mirror/gcc/blob/releases/gcc-10/libstdc++-v3/include/bits/basic_string.h#L3230):

```
	// The following storage is init'd to 0 by the linker, resulting
        // (carefully) in an empty string with one reference.
        static size_type _S_empty_rep_storage[];
```

What seems to happen is that when we call

```
int GpiObjHdl::initialise(const std::string &name, const std::string &fq_name) {
    m_name = name;
    ...
}
```

`m_name` gets free()'d after it got the new `name` assigned. Since
`m_name` was empty/unset before, free() tries to access
`_S_empty_rep_storage`.

Assigning an explicit default value to `m_name` works around the problem
nicely.

That being said, it's unclear why free() is getting invoked on that symbol
at all. It's also unclear why the same code only causes issues for a
single mixed signal testcase. Finding out is unlikely to be trivial:
cocotb wheels link (parts of) a static libstdc++ from RHEL7, while
Riviera-PRO comes with its own dynamically-linked libstdc++. It's my
hope that the workaround in this commit serves us long enough before it
becomes a problem in itself.

Fixes #3078
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:bug a bug in existing functionality
Projects
None yet
Development

No branches or pull requests

1 participant