Debugging compiled languages

Compile-time errors

  • Make the compiler say what you need to know
  • Learn to understand what the compiler tries to tell you

GCC C++ compiler examples

int main() {
   return 0
}



Compiling with

gcc test.cpp -o test

test.cpp: In function ‘int main()’:
test.cpp:2:12: error: expected ‘;’ before ‘}’ token
    2 |    return 0
      |            ^
      |            ;
    3 | }
      | ~












GCC C++ compiler examples

int main() {
   using namespace std;
   cout << "Hello" << endl;
   return 0;
}

Compiling with

gcc test.cpp -o test

test.cpp: In function ‘int main()’:
test.cpp:3:4: error: ‘cout’ was not declared in this scope
    3 |    cout << "Hello" << endl;
      |    ^~~~
test.cpp:1:1: note: ‘std::cout’ is defined in header ‘<iostream>’; did you forget to ‘#include <iostream>’?
  +++ |+#include <iostream>
    1 | int main() {
test.cpp:3:23: error: ‘endl’ was not declared in this scope
    3 |    cout << "Hello" << endl;
      |                       ^~~~
test.cpp:1:1: note: ‘std::endl’ is defined in header ‘<ostream>’; did you forget to ‘#include <ostream>’?
  +++ |+#include <ostream>
    1 | int main() {





GCC C++ compiler examples

#include <iostream>

int main {
   using namespace std;
   cout << "Hello" << endl;
   return 0;
}

Compiling with

gcc test.cpp -o test

test.cpp:3:5: error: cannot declare ‘::main’ to be a global variable
    3 | int main {
      |     ^~~~
test.cpp:4:4: error: expected primary-expression before ‘using’
    4 |    using namespace std;
      |    ^~~~~
test.cpp:4:4: error: expected ‘}’ before ‘using’
test.cpp:3:10: note: to match this ‘{’
    3 | int main {
      |          ^
test.cpp:5:4: error: ‘cout’ does not name a type
    5 |    cout << "Hello" << endl;
      |    ^~~~
test.cpp:6:4: error: expected unqualified-id before ‘return’
    6 |    return 0;
      |    ^~~~~~
test.cpp:7:1: error: expected declaration before ‘}’ token
    7 | }
      | ^

GCC C++ compiler examples

#include <iostream>

int main() {
   using namespace std;
   cout << "Hello" << endl;
   return 0;
}

Compiling with

gcc test.cpp -o test

/usr/bin/ld: /tmp/ccrbNm7k.o: warning: relocation against `_ZSt4cout' in read-only section `.text'
/usr/bin/ld: /tmp/ccrbNm7k.o: in function `main':
test.cpp:(.text+0x15): undefined reference to `std::cout'
/usr/bin/ld: test.cpp:(.text+0x1d): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)'
/usr/bin/ld: test.cpp:(.text+0x24): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)'
/usr/bin/ld: test.cpp:(.text+0x2f): undefined reference to `std::ostream::operator<<(std::ostream& (*)(std::ostream&))'
/usr/bin/ld: /tmp/ccrbNm7k.o: in function `__static_initialization_and_destruction_0(int, int)':
test.cpp:(.text+0x66): undefined reference to `std::ios_base::Init::Init()'
/usr/bin/ld: test.cpp:(.text+0x81): undefined reference to `std::ios_base::Init::~Init()'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status

GCC C++ compiler examples

#include <iostream>

int main() {
   using namespace std;
   cout << "Hello" << endl;
   return 0;
}

Compiling with

g++ test.cpp -o test

Compilation succeeded

Remarks

  • The issue is always at or before the first error
  • Most compilers give good feedback by default

Compiler flags for compilation output

Setting compiler flags for compiled languages like Fortran and C

Check the manuals

Also gcc compile time checks and options like

  • -Wall and
  • -Wextra

Run-time errors

  • Let the compiler help you if something goes wrong
  • Understand where to look for issues

Look out for compiler warnings

#include <stdlib.h>

int main() {
   int number = 1;
   int* ptr;
   ptr = &number;

   free(ptr);
   // ERROR: Trying to free
   // memory from stack

   return 0;
}

Compiling with

gcc inv_ptr.c -o inv_ptr

Compiler output

inv_ptr.c: In function ‘main’:
inv_ptr.c:8:4: warning: ‘free’ called on unallocated object ‘number’ [-Wfree-nonheap-object]
    8 |    free(ptr);
      |    ^~~~~~~~~
inv_ptr.c:4:8: note: declared here
    4 |    int number = 1;
      |        ^~~~~~

Run output e.g.

Segmentation fault

Look out for compiler warnings

#include <stdlib.h>

int main() {
   int number = 1;
   int* ptr;
   ptr = &number;

   free(ptr);
   // ERROR: Trying to free
   // memory from stack

   return 0;
}

Compiling with

gcc inv_ptr.c -Werror -o inv_ptr

Compiler output

inv_ptr.c: In function ‘main’:
inv_ptr.c:8:4: error: ‘free’ called on unallocated object ‘number’ [-Werror=free-nonheap-object]
    8 |    free(ptr);
      |    ^~~~~~~~~
inv_ptr.c:4:8: note: declared here
    4 |    int number = 1;
      |        ^~~~~~
cc1: all warnings being treated as errors

Compiler flags for run-time output (1/2)

Setting compiler flags for compiled languages like Fortran and C

Typically using -g for Intel, GCC and many other compilers

In the Fortran world, also

  • gfortran: -fbacktrace, -fbounds-check
  • intel (ifort): -traceback, -check bounds, -check all

Compiler flags for run-time output (2/2)

Try with more than just one compiler

\(\Rightarrow\) HPC systems usually provide native compiler plus gcc/gfortan. Try both variants.

gdb

Using gdb to inspect a core dump

  • to get a core dump
ulimit -c unlimited
a.out

For core dumps, also man 5 core

  • to inspect the core dump
gdb a.out core

gdb example

program loop_count_2d
  implicit none
  integer, parameter :: leni = 3
  integer, parameter :: lenj = 5
  integer, parameter :: len  = leni*leni
  ! ERROR: leni used twice for len

  integer :: my_ij(2,len)
  integer :: i, j, n, ii, jj

  n = 0
  do j = 1, lenj
    do i = 1, leni
       n = n+1
       my_ij(1,n) = i
       my_ij(2,n) = j
    enddo
  enddo
  do n = 1, len
     jj = ( n -     1) / leni + 1
     ii =   n - (jj-1) * leni

     if ( ii /= my_ij(1,n) ) then
        print *, ' wrong i ', n, &
            ii, my_ij(1,n)
     end if

     if ( jj /= my_ij(2,n) ) then
        print *, ' wrong j ', n, &
            jj, my_ij(2,n)
     end if
  enddo
end program loop_count_2d

Some gdb Commands

  • b <line>: Add breakpoint at given line number
  • info b: Show current breakpoints
  • clear: Clear current breakpoint
  • d: Clear all breakpoints
  • run: Run the program
  • l: Print code
  • info locals: Print value of local variables
  • p <var>: Print value of variable
  • up, down or frame <num>: Navigate stack
  • n: Next line (without descending)
  • step: Next command
  • h: Show help
  • bt: Create backtrace
  • c: Continue execution
  • q: Quit gdb

gdb Live Demo

Debugging Flowchart

  • Click on flowchart to enlarge
  • Intended as a guideline, not strict rule

Approach on locating Fortran bugs

For this exercise, check out the Fortran code schnecke_flt.f90. You can compile it on Levante e.g. by loading GCC 11.2.0 compilers with

module load gcc/11.2.0-gcc-11.2.0

and then run

gfortran schnecke_flt.f90

Task

  • Use additional compiler flags and gdb to locate the error. Document your approach.