Skip to content

Conversation

@quinox
Copy link
Contributor

@quinox quinox commented Jan 26, 2025

  • Feel free to close this PR if you're not interested: I made it for fun while playing around with gdb, just to see what's possible.
  • The WIP part: I only hooked it into one of the assert failures, I think you have a few other roads how testcases could fail. I have no desire to polish this further, take it or leave it 😄

This is a separate implementation but the same idea as #134.

This implementation is more useful but also less clean. It provides you with coredumps but in order to do so there are a few different bits of GDB usage, and it always outputs 4 thrown signals on startup (you can probably fix this with some more changes to the code if you wanted to).

Originally I implemented this with a simple straight-forward gdb script. That didn't work well enough: generate-core-file can only dump to a fixed filename or to core.$PID, there's no other dynamic option. This meant only 1 testcase could create a dump, not great. Then I used a Python breakpoint class to run gdb.execute("create-core-file " + timestamp) within the stop() method but that didn't work because gdb always switched over to interactive mode after dumping, breaking everything.

Long story short... I found workarounds, this does what I originally envisioned.


This PR will save a corefile when an assertion failure is triggered. An example output:


Tests run: 1. Passed: 0. Failed: 1 (of which 0 exceptions). Total assertions: 1.


Failed tests:
 - testStringDistances

TESTS FAILED
[Inferior 1 (process 18883) exited with code 01]
Bummer


Tail of stderr:


warning: Memory read failed for corefile section, 4096 bytes at 0xffffffffff600000.

,#############################################,
| core.1737886617.2025-26-01T111658.902354803 |
'#############################################'

warning: Can not parse XML target description; XML support was disabled at compile time
[New LWP 18883]
[New LWP 18905]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib64/libthread_db.so.1".
Core was generated by `/home/quinox/projects/FlashMQ/FlashMQTests/buildtests/flashmq-tests testStringDi'.
Program terminated with signal SIGVTALRM, Virtual timer expired.
#0  0x00007ffff754804c in ?? () from /usr/lib64/libc.so.6
[Current thread is 1 (Thread 0x7ffff74b58c0 (LWP 18883))]
#0  0x00007ffff754804c in ?? () from /usr/lib64/libc.so.6
No symbol table info available.
#1  0x00007ffff74f6976 in raise () from /usr/lib64/libc.so.6
No symbol table info available.
#2  0x00005555558a4fc7 in fmq_assert (b=false, failmsg=0x55555594de32 "Values aren't the same", actual=0x555555951218 "distanceBetweenStrings(\"\", \"mistakes_were_made\")", expected=0x555555951202 "(unsigned int)0", file=0x555555951018 "/home/quinox/projects/FlashMQ/FlashMQTests/configtests.cpp", line=123) at /home/quinox/projects/FlashMQ/FlashMQTests/testhelpers.cpp:19
No locals.
#3  0x00005555558e54b0 in fmq_compare<unsigned int, unsigned int> (t1=@0x7fffffffdd38: 18, t2=@0x7fffffffdd3c: 0, actual=0x555555951218 "distanceBetweenStrings(\"\", \"mistakes_were_made\")", expected=0x555555951202 "(unsigned int)0", file=0x555555951018 "/home/quinox/projects/FlashMQ/FlashMQTests/configtests.cpp", line=123) at /home/quinox/projects/FlashMQ/FlashMQTests/testhelpers.h:55
No locals.
#4  0x0000555555913b9b in MainTests::testStringDistances (this=0x7fffffffe100) at /home/quinox/projects/FlashMQ/FlashMQTests/configtests.cpp:123
No locals.
#5  0x00005555558a4e65 in std::__invoke_impl<void, void (MainTests::*&)(), MainTests*&> (__f=@0x555555a135a0: (void (MainTests::*)(MainTests * const)) 0x555555913ac4 <MainTests::testStringDistances()>, __t=@0x555555a135b0: 0x7fffffffe100) at /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:74
No locals.
#6  0x00005555558a4d8f in std::__invoke<void (MainTests::*&)(), MainTests*&> (__fn=@0x555555a135a0: (void (MainTests::*)(MainTests * const)) 0x555555913ac4 <MainTests::testStringDistances()>) at /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:96
No locals.
#7  0x00005555558a4c59 in std::_Bind<void (MainTests::*(MainTests*))()>::__call<void, , 0ul>(std::tuple<>&&, std::_Index_tuple<0ul>) (this=0x555555a135a0, __args=...) at /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/functional:506
No locals.
#8  0x00005555558a4b29 in std::_Bind<void (MainTests::*(MainTests*))()>::operator()<, void>() (this=0x555555a135a0) at /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/functional:591
No locals.
#9  0x00005555558a474e in std::__invoke_impl<void, std::_Bind<void (MainTests::*(MainTests*))()>&>(std::__invoke_other, std::_Bind<void (MainTests::*(MainTests*))()>&) (__f=...) at /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:61
No locals.
#10 0x00005555558a4062 in std::__invoke_r<void, std::_Bind<void (MainTests::*(MainTests*))()>&>(std::_Bind<void (MainTests::*(MainTests*))()>&) (__fn=...) at /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/invoke.h:111
No locals.
#11 0x00005555558a367b in std::_Function_handler<void (), std::_Bind<void (MainTests::*(MainTests*))()> >::_M_invoke(std::_Any_data const&) (__functor=...) at /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_function.h:290
No locals.
#12 0x000055555587727e in std::function<void()>::operator() (this=0x555555a13570) at /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/std_function.h:591
No locals.
#13 0x00005555558a2231 in MainTests::test (this=0x7fffffffe100, skip_tests_with_internet=false, skip_server_tests=false, tests=std::vector of length 1, capacity 1 = {...}) at /home/quinox/projects/FlashMQ/FlashMQTests/maintests.cpp:385
        testInitializer = {_vptr.TestInitializer = 0x5555559f7030 <vtable for TestInitializer+16>, tests = 0x7fffffffe100}
        failCountBefore = 0
        assertCountBefore = 0
        failCountAfter = -9433
        assertCountAfter = 32767
        tf = @0x555555a13570: {f = {<std::_Maybe_unary_or_binary_function<void>> = {<No data fields>}, <std::_Function_base> = {static _M_max_size = 16, static _M_max_align = 8, _M_functor = {_M_unused = {_M_object = 0x555555a135a0, _M_const_object = 0x555555a135a0, _M_function_pointer = 0x555555a135a0, _M_member_pointer = (void (std::_Undefined_class::*)(std::_Undefined_class * const)) 0x555555a135a0}, _M_pod_data = "\2405\241UUU\000\000\000\000\000\000\000\000\000"}, _M_manager = 0x5555558a367e <std::_Function_handler<void (), std::_Bind<void (MainTests::*(MainTests*))()> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)>}, _M_invoker = 0x5555558a3657 <std::_Function_handler<void (), std::_Bind<void (MainTests::*(MainTests*))()> >::_M_invoke(std::_Any_data const&)>}, requiresServer = false, requiresInternet = false}
        pair = {first = "testStringDistances", second = {f = {<std::_Maybe_unary_or_binary_function<void>> = {<No data fields>}, <std::_Function_base> = {static _M_max_size = 16, static _M_max_align = 8, _M_functor = {_M_unused = {_M_object = 0x555555a135a0, _M_const_object = 0x555555a135a0, _M_function_pointer = 0x555555a135a0, _M_member_pointer = (void (std::_Undefined_class::*)(std::_Undefined_class * const)) 0x555555a135a0}, _M_pod_data = "\2405\241UUU\000\000\000\000\000\000\000\000\000"}, _M_manager = 0x5555558a367e <std::_Function_handler<void (), std::_Bind<void (MainTests::*(MainTests*))()> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)>}, _M_invoker = 0x5555558a3657 <std::_Function_handler<void (), std::_Bind<void (MainTests::*(MainTests*))()> >::_M_invoke(std::_Any_data const&)>}, requiresServer = false, requiresInternet = false}}
        __for_range = std::map with 1 element = {["testStringDistances"] = {f = {<std::_Maybe_unary_or_binary_function<void>> = {<No data fields>}, <std::_Function_base> = {static _M_max_size = 16, static _M_max_align = 8, _M_functor = {_M_unused = {_M_object = 0x555555a135a0, _M_const_object = 0x555555a135a0, _M_function_pointer = 0x555555a135a0, _M_member_pointer = (void (std::_Undefined_class::*)(std::_Undefined_class * const)) 0x555555a135a0}, _M_pod_data = "\2405\241UUU\000\000\000\000\000\000\000\000\000"}, _M_manager = 0x5555558a367e <std::_Function_handler<void (), std::_Bind<void (MainTests::*(MainTests*))()> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)>}, _M_invoker = 0x5555558a3657 <std::_Function_handler<void (), std::_Bind<void (MainTests::*(MainTests*))()> >::_M_invoke(std::_Any_data const&)>}, requiresServer = false, requiresInternet = false}}
        __for_begin = {first = "testStringDistances", second = {f = {<std::_Maybe_unary_or_binary_function<void>> = {<No data fields>}, <std::_Function_base> = {static _M_max_size = 16, static _M_max_align = 8, _M_functor = {_M_unused = {_M_object = 0x555555a135a0, _M_const_object = 0x555555a135a0, _M_function_pointer = 0x555555a135a0, _M_member_pointer = (void (std::_Undefined_class::*)(std::_Undefined_class * const)) 0x555555a135a0}, _M_pod_data = "\2405\241UUU\000\000\000\000\000\000\000\000\000"}, _M_manager = 0x5555558a367e <std::_Function_handler<void (), std::_Bind<void (MainTests::*(MainTests*))()> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)>}, _M_invoker = 0x5555558a3657 <std::_Function_handler<void (), std::_Bind<void (MainTests::*(MainTests*))()> >::_M_invoke(std::_Any_data const&)>}, requiresServer = false, requiresInternet = false}}
        __for_end = {first = "", second = {f = {<std::_Maybe_unary_or_binary_function<void>> = {<No data fields>}, <std::_Function_base> = {static _M_max_size = 16, static _M_max_align = 8, _M_functor = {_M_unused = {_M_object = 0x0, _M_const_object = 0x0, _M_function_pointer = 0x0, _M_member_pointer = NULL}, _M_pod_data = "\000\000\000\000\000\000\000\000P\343\377\377\377\177\000"}, _M_manager = 0x5555558957c1 <main(int, char**)+851>}, _M_invoker = 0x7fffffffe468}, requiresServer = 114, requiresInternet = 39}}
        testCount = 1
        testPassCount = 0
        testFailCount = 0
        testExceptionCount = 0
        selectedTests = 0x7fffffffe030
        subset = std::map with 1 element = {["testStringDistances"] = {f = {<std::_Maybe_unary_or_binary_function<void>> = {<No data fields>}, <std::_Function_base> = {static _M_max_size = 16, static _M_max_align = 8, _M_functor = {_M_unused = {_M_object = 0x555555a135a0, _M_const_object = 0x555555a135a0, _M_function_pointer = 0x555555a135a0, _M_member_pointer = (void (std::_Undefined_class::*)(std::_Undefined_class * const)) 0x555555a135a0}, _M_pod_data = "\2405\241UUU\000\000\000\000\000\000\000\000\000"}, _M_manager = 0x5555558a367e <std::_Function_handler<void (), std::_Bind<void (MainTests::*(MainTests*))()> >::_M_manager(std::_Any_data&, std::_Any_data const&, std::_Manager_operation)>}, _M_invoker = 0x5555558a3657 <std::_Function_handler<void (), std::_Bind<void (MainTests::*(MainTests*))()> >::_M_invoke(std::_Any_data const&)>}, requiresServer = false, requiresInternet = false}}
        failedTests = std::vector of length 0, capacity 0
#14 0x00005555558957c1 in main (argc=2, argv=0x7fffffffe468) at /home/quinox/projects/FlashMQ/FlashMQTests/main.cpp:54
        skip_tests_with_internet = false
        skip_server_tests = false
        tests = std::vector of length 1, capacity 1 = {"testStringDistances"}
        option_list_terminated = false
...etc...

@quinox
Copy link
Contributor Author

quinox commented Jan 26, 2025

image

Neat to see this in action. Fixed.

@jhofstee
Copy link
Contributor

jhofstee commented May 1, 2025

@quinox within a signal handler you have to be really careful, you can't reliably use printf e.g. I would expect you shouldn't call std::cout there either.

@halfgaar
Copy link
Owner

halfgaar commented May 1, 2025

I hadn't seen that yet. To clarify: printf allocates memory, and likely iostreams too. Signal handlers interrupt your program on the spot, even when there is an event loop. This means it could already be inside another malloc call or something similar. Then the heap is messed up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants