Skip to content

Latest commit

 

History

History
166 lines (156 loc) · 5.5 KB

compilation_debugging.md

File metadata and controls

166 lines (156 loc) · 5.5 KB
marp math theme footer
true
katex
custom-theme

Compilation flags and debugging

Today:

  • What other compilation flags exist
  • What compilation flags to use
  • How to debug code

📺 Watch the related YouTube video!


Special symbols used in slides

  • 🎨 - Style recommendation
  • 🎓 - Software design recommendation
  • 😱 - Not a good practice! Avoid in real life!
  • ✅ - Good practice!
  • ❌ - Whatever is marked with this is wrong
  • 🚨 - Alert! Important information!
  • 💡 - Hint or a useful exercise
  • 🔼1️⃣7️⃣ - Holds for this version of C++(here, 17) and above
  • 🔽1️⃣1️⃣ - Holds for versions until this one C++(here, 11)

Style (🎨) and software design (🎓) recommendations mostly come from Google Style Sheet and the CppCoreGuidelines


Compilation flags

  • Lots of flags can be passed while compiling the code
  • We have seen some already: -std=c++17, -o, etc.
    c++ -std=c++17 -o test test.cpp
  • ✅ Enable most warnings, treat them as errors: -Wall, -Wextra, -Wpedantic, -Werror
  • Other warnings possible too: -Wimplicit-fallthrough, -Wsign-conversion, etc.
  • Optimization options:
    • -O0 - no optimization [default]
    • -O3 - full optimization [preferred]
  • Keep debugging symbols: -g (usually used with -O0)

Debugging your code

  • Debugging will take more time than writing your code
  • 💡 Read about how to think about debugging (really, do it!)
  • When it comes to tools used for debugging, there are largely two different philosophies:
    • Using print statements [my preference]
    • Using a debugger
  • Each has advantages and disadvantages
  • No debugging methods is complete without you thinking about the probable cause of the problem
  • You can read a discussion on Hacker News about it

Using print statements

  • Just add any printout statements to your code
  • I usually use a form of a print statement shown below:
    cerr << __FILE__ << ":" << __LINE__ << ": " << value << endl;
  • This will print the filename and a line where its called
  • We can also print a value of interest at any point
  • Usually requires to recompile only part of the program
  • Typical workflow:
    • Observe the behavior
    • Form a hypothesis of what is wrong
    • Try a fix and repeat until problem is solved
  • This workflow forces us to understand the problem before rushing into trying a solution

Using a debugger

  • Allows stopping a program at a point and looking around
  • Requires debug symbols (-g flag), needs full recompilation
  • Might be confusing with optimizations enabled
  • Best program to use is probably lldb or gdb
  • Insanely popular and powerful (and already installed 😉)
  • Allows to print the backtrace
  • No built-in gui 😢
  • There are some tools built on top of GDB to fix this:
    • gdbgui is a Python tool that provides a web GUI for GDB
    • VSCode C++ extension provides a GUI for the debugger
  • I rarely use a debugger and when I do I use lldb/gdb

Exercise: debug a simple program!

❌ Beware it has an error!

#include <iostream>
#include <vector>
int main() {
  std::vector<int> numbers{1, 2, 3};
  for (auto i = numbers.size() - 1UL; i >= 0; --i) {
    std::cout << numbers[i] << std::endl;
  }
  return 0;
}

Once we run the program it crashes at some point:

[1]    74786 segmentation fault  ./program

Using print statements

#include <iostream>
#include <vector>
int main() {
  std::vector<int> numbers{1, 2, 3};
  int count{};  // <- Count used for debugging
  for (auto i = numbers.size() - 1UL; i >= 0; --i) {
    std::cerr << "i = " << i << std::endl;  // <- Debug print
    std::cout << numbers[i] << std::endl;
    if (count++ > 10) {break;}  // <- Early exit
  }
  return 0;
}
  • We can see smth like this on stderr:
    i = 2
    i = 1
    i = 0
    i = 18446744073709551615
    
  • Now we can guess what happens, here is a hint 😉

Using lldb (or gdb)

  • Recompile the code with -g -O0 flags
  • Start the code in the debugger:
    λ › lldb ./program
    (lldb) target create "./program"
    Current executable set to '/private/tmp/program' (arm64).
    (lldb) r
  • When it fails, lldb shows us where it fails
  • To find out why we will add a breakpoint
    (lldb) breakpoint set --file program.cpp --line 6
    
  • Restart by typing r, the execution will stop at breakpoint
  • We now add a watch on the variable i
    (lldb) watch set var i
    
  • Repeatedly enter c and press until the issue is found
  • Full tutorial directly from lldb

bg