Блог пользователя andrewtam

Автор andrewtam, история, 3 года назад, По-английски

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
  • Проголосовать: не нравится

»
3 года назад, # |
  Проголосовать: нравится +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.

»
3 года назад, # |
  Проголосовать: нравится +36 Проголосовать: не нравится

Personally, printing stuff has always been sufficient for spotting bugs.

»
3 года назад, # |
  Проголосовать: нравится +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...

»
3 года назад, # |
  Проголосовать: нравится +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

»
3 года назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

what do you mean you got java'ed

  • »
    »
    3 года назад, # ^ |
      Проголосовать: нравится 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.

  • »
    »
    3 года назад, # ^ |
      Проголосовать: нравится +13 Проголосовать: не нравится

    Look at their submissions:

    this in Java (in-contest, TLE)
    this in C++ (after the contest, AC)

»
3 года назад, # |
  Проголосовать: нравится +46 Проголосовать: не нравится

I always use the debugger.

»
3 года назад, # |
  Проголосовать: нравится +56 Проголосовать: не нравится

I don't use a debugger.

»
3 года назад, # |
  Проголосовать: нравится +3 Проголосовать: не нравится

I use debugger very rarely. Most error can be caught with printing values of variables.

»
3 года назад, # |
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.

  • »
    »
    3 года назад, # ^ |
      Проголосовать: нравится 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.

    • »
      »
      »
      3 года назад, # ^ |
        Проголосовать: нравится +8 Проголосовать: не нравится

      binary search applied! assert(0) after including the cassert library also works for this purpose.

  • »
    »
    3 года назад, # ^ |
      Проголосовать: нравится +14 Проголосовать: не нравится

    Do you not get a nice stack backtrace pointing to the exact source code line when debugging code locally on your computer?

    • »
      »
      »
      3 года назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      I'm pretty sure you can only get that with compiler arguments that enable debugging things (see comments above).

    • »
      »
      »
      3 года назад, # ^ |
        Проголосовать: нравится +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.

      • »
        »
        »
        »
        3 года назад, # ^ |
          Проголосовать: нравится +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

        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

        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.

        • »
          »
          »
          »
          »
          3 года назад, # ^ |
            Проголосовать: нравится +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

          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!

          • »
            »
            »
            »
            »
            »
            3 года назад, # ^ |
              Проголосовать: нравится +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
            Output

            That's a lot of noise on the console, but at least the offending line "testvect.cpp:10" is present in the backtrace.

»
3 года назад, # |
  Проголосовать: нравится +20 Проголосовать: не нравится

no need for debuggers when you can just write cout << "haha penis\n"; :)

»
3 года назад, # |
  Проголосовать: нравится +8 Проголосовать: не нравится

No

»
3 года назад, # |
  Проголосовать: нравится +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.

»
3 года назад, # |
  Проголосовать: нравится +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.

»
3 года назад, # |
  Проголосовать: нравится +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

»
3 года назад, # |
  Проголосовать: нравится +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.

»
3 года назад, # |
  Проголосовать: нравится +9 Проголосовать: не нравится

cout is your best debugger

»
3 года назад, # |
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
2nd define
»
3 года назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

No.

»
3 года назад, # |
  Проголосовать: нравится 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 ;)

»
3 года назад, # |
  Проголосовать: нравится 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.

»
3 года назад, # |
  Проголосовать: нравится 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.