### andrewtam's blog

By andrewtam, history, 4 months ago,

Context: On a recent contest I got Java'ed so I decided that I would permanently switch to C++. That being said, I found that generally C++ debuggers were less effective than the one I'm currently using for Java (this is my personal opinion, feel free to disagree). Fast forward to today, I was complaining on Discord that I couldn't get my debugger to work properly and in response, my friend told me that debuggers are "stupid" and that "nobody uses a debugger." This started a mini-debate about debuggers and who uses them which brings me back to my original question: do you use a debugger? Please let me know in the comments.

Have a great day!

• +40

 » 4 months ago, # |   +5 I don't use a debugger for simple code, but when the code gets complex and values in multiple data structures change, I find it great to use a debugger to understand at each major step whether everything's going correctly.
 » 4 months ago, # |   +36 Personally, printing stuff has always been sufficient for spotting bugs.
 » 4 months ago, # |   +39 I personally don't use a debugger, but passing -fsanitize=address -fsanitize=undefined while compiling has caught a lot of undefined behavior and logic errors.Although sometimes the errors still tend to take a while to debug, so maybe I should be using a debugger...
•  » » 4 months ago, # ^ |   0 I've never heard of this before, but it sounds interesting. Thanks for your feedback!
•  » » » 4 months ago, # ^ |   +8 I wrote a intro blog about this with an example an year back, you may like ithttps://mmpataki.github.io/programming/2020/10/31/insertion-sort-and-sanitizers.html
•  » » » » 4 months ago, # ^ |   0 really cool blog
•  » » » » » 4 months ago, # ^ |   0 Thanks, hope other posts were also interesting for you.https://mmpataki.github.io/
 » 4 months ago, # |   +12 I use -Wshadow -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -fsanitize=address -fsanitize=undefined to avoid some common mistakes I make.For debugging besides this, I just print stuff like everyone else
•  » » 4 months ago, # ^ |   0 Hi can you explain what does that line do ? is that useful in debugging runtime errors ?
•  » » » 4 months ago, # ^ |   +3 This explains them nicely.And yea this helps in runtime errors but you have to use STL containers for it.
 » 4 months ago, # |   0 what do you mean you got java'ed
•  » » 4 months ago, # ^ |   0 Getting Java'ed is when you write a correct solution that passes in C++ or any fast language but fails in java because java is slower.
•  » » 4 months ago, # ^ |   +13 Look at their submissions:this in Java (in-contest, TLE)this in C++ (after the contest, AC)
 » 4 months ago, # |   +46 I always use the debugger.
 » 4 months ago, # |   +56 I don't use a debugger.
•  » » 4 months ago, # ^ |   -39 Probably because you are too smart and have never made a mistake in your code before.
•  » » » 4 months ago, # ^ |   +27 No, because it's slow, output debug works better for me.
 » 4 months ago, # |   +3 I use debugger very rarely. Most error can be caught with printing values of variables.
 » 4 months ago, # | ← Rev. 2 →   +53 A funny debugging trick if you don't like to use a debugger like me:When having a runtime error, I used to do a binary seach to find the line with the error, by putting cout << "Ending..." << endl; exit(0); in the middle of the code, if my code reached it I search the second half, otherwise I search the first half and I repeat until I find the line.
•  » » 4 months ago, # ^ |   0 Even better: It's faster to copy and paste that line (without the exit(0);) onto several lines where you think the runtime error could occur. Basically an N-ary search where you put N-1 copies of that line.
•  » » » 4 months ago, # ^ |   +8 binary search applied! assert(0) after including the cassert library also works for this purpose.
•  » » 4 months ago, # ^ |   +14 Do you not get a nice stack backtrace pointing to the exact source code line when debugging code locally on your computer?
•  » » » 4 months ago, # ^ |   0 I'm pretty sure you can only get that with compiler arguments that enable debugging things (see comments above).
•  » » » 4 months ago, # ^ |   +13 Is there a way to do this when the code segfaults? I had tried to find a solution that doesn't involve using GDB, but failed. I know there are a couple of ways using some libraries but I prefer not to use them in a competitive programming context.
•  » » » » 4 months ago, # ^ |   +4 Is there a way to do this when the code segfaults? Compiling a program with "-g" option adds debugging information to it. This extra debugging information does not make the program slower. It's basically just an extra appended data structure, which allows to map address ranges in the executable to the lines in the source code. Plus some information about how to decipher stack at any point in order to be able to extract return addresses and the values of local variables.Now what to do if the program segfaults? In Linux it's possible to enable creation of core dumps via "ulimit -c unlimited" command. A core dump file is a snapshot of memory at the time of the crash, which can be later inspected by GDB to see what was going on. Here is an example: Spoiler# cat test.cpp #include int main() { int *p = 0; memset(p, 0, 100); } # g++ -O3 -g test.cpp # ./a.out Segmentation fault # ulimit -c unlimited # ./a.out Segmentation fault (core dumped) # gdb -batch -ex "bt" -c core ./a.out [New LWP 25097] Core was generated by ./a.out'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x00007fc10148f218 in __memset_sse2_unaligned_erms () from /lib64/libc.so.6 #0 0x00007fc10148f218 in __memset_sse2_unaligned_erms () from /lib64/libc.so.6 #1 0x0000557637d7a062 in memset (__len=100, __ch=0, __dest=0x0) at /usr/include/bits/string_fortified.h:59 #2 main () at test.cpp:6 GDB was executed non-interactively in batch mode to just decipher and print a backtrace. It didn't execute the program and didn't require any user input. I'm not a big fan of interactive single step debugging myself. I had tried to find a solution that doesn't involve using GDB, but failed. As the others already mentioned, it's also possible to use "-fsanitize=address -fsanitize=undefined" options in the GCC command line. And we get a backtrace for free without any extra GDB invocation gymnastics: Spoiler# g++ -O3 -g -fsanitize=address -fsanitize=undefined test.cpp # ./a.out test.cpp:6:9: runtime error: null pointer passed as argument 1, which is declared to never be null AddressSanitizer:DEADLYSIGNAL ================================================================= ==25525==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f1976f5e218 bp 0x55f7f7901200 sp 0x7fff5506bad8 T0) ==25525==The signal is caused by a WRITE memory access. ==25525==Hint: address points to the zero page. #0 0x7f1976f5e218 in __memset_sse2_unaligned_erms (/lib64/libc.so.6+0xa4218) #1 0x55f7f79010ba in memset /usr/include/bits/string_fortified.h:59 #2 0x55f7f79010ba in main /tmp/segfault/test.cpp:6 #3 0x7f1976edd78f in __libc_start_main (/lib64/libc.so.6+0x2378f) #4 0x55f7f7901139 in _start (/tmp/segfault/a.out+0x1139) AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV (/lib64/libc.so.6+0xa4218) in __memset_sse2_unaligned_erms ==25525==ABORTING These options slow down the program because a lot of extra safety checks are added in the generated code. But this is a small price if it helps to find a bug.GDB, sanitizer options and tools like valgrind help to deal with segfaults without having to resort to bisecting via debugging prints. I believe that debugging prints are a much slower method in this particular situation.
•  » » » » » 4 months ago, # ^ |   +11 Thanks for the nice and detailed explanation! I follow a pretty similar debugging pattern too. I have "-fsanitize=address -fsanitize=undefined" in my debug configuration as well, but it comes into play very rarely (and it indeed shows the given backtrace). I might have been ambiguous about the previous question, and I apologize for not having clarified my issue earlier.I believe that the reason it is not super useful for me (but useful at appropriate times because getting such errors is usually a signature of a much bigger problem) is because mostly the errors I am concerned with are out of bounds errors, and I like using vectors rather than arrays, so when I compile my code with _GLIBCXX_DEBUG and _GLIBCXX_DEBUG_PEDANTIC, it throws an exception rather than give me a nice backtrace. For instance, an out of bounds error gives me something like: Spoiler/usr/include/c++/11.1.0/debug/vector:438: In function: std::__debug::vector<_Tp, _Allocator>::reference std::__debug::vector<_Tp, _Allocator>::operator[](std::__debug::vector<_Tp, _Allocator>::size_type) [with _Tp = int; _Allocator = std::allocator; std::__debug::vector<_Tp, _Allocator>::reference = int&; std::__debug::vector<_Tp, _Allocator>::size_type = long unsigned int] Error: attempt to subscript container with out-of-bounds index 100000, but container only holds 1 elements. Objects involved in the operation: sequence "this" @ 0x0x7ffd4c593c40 { type = std::__debug::vector >; } For this exception, I much rather prefer having a backtrace than this isolated piece of information. I prefer using _GLIBCXX_DEBUG and _GLIBCXX_DEBUG_PEDANTIC since they also add in checks for preconditions on algorithms, which is quite handy (even if my binary search might become $O(n)$, which I don't really care about while debugging). To get the backtrace, I just use GDB to look at where the exception has been thrown. This isn't too big of an inconvenience for me, but it would be pretty instructive to get around this.I think my original question should rather have been this:Is there a way to print a backtrace for exceptions thrown by the code?Please let me know if this can be done cleanly (I think there's no obvious solution clean solution to this, but just in case). Thanks!
•  » » » » » » 4 months ago, # ^ |   +11 Compiling with _GLIBCXX_DEBUG and _GLIBCXX_DEBUG_PEDANTIC results in a call of "abort" function in the end (at least on my system). It is possible to set a breakpoint on "abort" function in GDB, run program and then ask for a backtrace when the breakpoint is reached. Or alternatively, maybe try something like this: Source code#include #include int main() { /* set abort handler, which triggers segfault */ signal(SIGABRT, [](int x) { *(int *)0 = 0; }); std::vector a(1); a[2] = 1; /* out of bounds access here */ }  Output# g++ -g -O3 -fsanitize=address -fsanitize=undefined -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC testvect.cpp # ./a.out /usr/lib/gcc/x86_64-pc-linux-gnu/11.2.0/include/g++-v11/debug/vector:438: In function: std::__debug::vector<_Tp, _Allocator>::reference std::__debug::vector<_Tp, _Allocator>::operator[](std::__debug::vector<_Tp, _Allocator>::size_type) [with _Tp = int; _Allocator = std::allocator; std::__debug::vector<_Tp, _Allocator>::reference = int&; std::__debug::vector<_Tp, _Allocator>::size_type = long unsigned int] Error: attempt to subscript container with out-of-bounds index 2, but container only holds 1 elements. Objects involved in the operation: sequence "this" @ 0x0x7fff4edf5540 { type = std::__debug::vector >; } testvect.cpp:7:41: runtime error: store to null pointer of type 'int' AddressSanitizer:DEADLYSIGNAL ================================================================= ==4126==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x55dfb9446972 bp 0x7fff4edf51e0 sp 0x7fff4edf4b30 T0) ==4126==The signal is caused by a WRITE memory access. ==4126==Hint: address points to the zero page. #0 0x55dfb9446972 in operator() /tmp/segfault/testvect.cpp:7 #1 0x7f9a83c2540f (/lib64/libc.so.6+0x3840f) #2 0x7f9a83c2538a in gsignal (/lib64/libc.so.6+0x3838a) #3 0x7f9a83c0f535 in abort (/lib64/libc.so.6+0x22535) #4 0x7f9a8491701c in __gnu_debug::_Error_formatter::_M_error() const /var/tmp/portage/sys-devel/gcc-11.2.0/work/gcc-11.2.0/libstdc++-v3/src/c++11/debug.cc:1123 #5 0x55dfb944757c in std::__debug::vector >::operator[](unsigned long) /usr/lib/gcc/x86_64-pc-linux-gnu/11.2.0/include/g++-v11/debug/vector:438 #6 0x55dfb9446583 in main /tmp/segfault/testvect.cpp:10 #7 0x7f9a83c1078f in __libc_start_main (/lib64/libc.so.6+0x2378f) #8 0x55dfb9446889 in _start (/tmp/segfault/a.out+0x5889) AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV /tmp/segfault/testvect.cpp:7 in operator() ==4126==ABORTING That's a lot of noise on the console, but at least the offending line "testvect.cpp:10" is present in the backtrace.
 » 4 months ago, # |   +20 no need for debuggers when you can just write cout << "haha penis\n"; :)
•  » » 4 months ago, # ^ |   0 avoid using medical terms bro xD
 » 4 months ago, # |   +8 No
 » 4 months ago, # |   +12 I sometimes use the debugger. I believe it is quite useful in case you have a runtime error or some random wa on a big sample test. You however need to take care as debugging with the debugger takes a lot of time and you can easily spend half of the contest on it.
 » 4 months ago, # |   +16 I use a hybrid of print debugging (for unexpected outputs) and GDB (for segfaults or other issues where the program dies, and I need a backtrace). This tends to be pretty fast for me.
 » 4 months ago, # |   +1 This is what I use: cf comment.These are my debug flags:g++ -std=c++1z -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC -g -fsanitize=undefined
 » 4 months ago, # |   +3 Yes (but on a case-by-case basis, it's not hard to decide whether print statemets or gdb is more efficient).One thing that is really great about gdb: you can instantly tell exactly where your code got a segfault. No need for some binary searching with print statements. In a sense, it also works for figuring out where your code gets stuck in an infinite loop.
 » 4 months ago, # |   +9 cout is your best debugger
•  » » 4 months ago, # ^ |   0 cerr*
 » 4 months ago, # | ← Rev. 3 →   +3 i use CLion and it's easy to debug here.I also switched from java to c++ and here are two defines to debug your code: First define#define trace(x) cout << #x << " = " << (x) << endl; int x = 3; trace(x) //output: x = 3 int a[3]{1, 2, 3}; trace(a[0]) //output: a[0] = 1  2nd define#define GLIBCXX_DEBUG don't forget to comment this define when you submit your code! 
 » 4 months ago, # | ← Rev. 2 →   0 I use debugger only if I had worked hard to find the worng in code but could not....sometimes debugger can not help and U need to write code again so in C++ U can not relies on debugger. another way to find wrong is to primt any char in some place of the code to know if it work just " cout<<"d1";" and know if the code worked from begin to this place .
 » 4 months ago, # |   0 No.
 » 4 months ago, # |   0 I started it using a few weeks ago and dropped it about a week ago, because it makes the code slow and sometimes leads to TLE :(And so I realised "cout" orz ;)
 » 4 months ago, # |   0 I use a debugger only to obtain stack traces after program crashes.And yes, -D_GLIBCXX_DEBUG` (as mentioned above many times) is a very useful compiler option.
 » 4 months ago, # |   0 When you have a bug, its better to check the code once before starting to print out everything; but if you didn't find the bug, using a debugger can save lots of time.