index 日本語

Internet of Tomohiro

If you found my articles interesting or useful, please donate using following links:


Debug Nim with GDB

  1. Install GDB
  2. Compile your program so that you can debug it with GDB
  3. Debug simple program with GDB
  4. GDB Text User Interface
  5. Debug other simple program with GDB
  6. Debug Nim compiler with GDB
  7. Other useful GDB commands

GDB is a command line debug tool. You can stop your program at specified point and print a value of variable or run it line by line. If you want to learn more about GDB, check GDB User Manual.

Install GDB

You can check if GDB is installed on your PC with following command.

On Linux:

which gdb

On Windows:

where gdb

If GDB is not installed on your PC, install it using your package manager (apt, pacman, emerge, etc). GDB is included in MinGW. If you use package manager Scoop on Windows, GDB is installed when you install gcc with scoop install gcc. You can also install it alone with scoop install gdb command, but it is older than the GDB included in gcc. Or you can download it from TDM-GCC.

Compile your program so that you can debug it with GDB

You need to add --debugger:native option when you compile your program like following example command so that you can debug your program with GDB.

nim c --debugger:native test.nim

Debug simple program with GDB

Let's debug following code with GDB. Save it as filename test.nim.

type
  TestObj = object
    num: int
    val: float
    str: string

proc initTestObj(num: int): TestObj =
  TestObj(num: num, val: 3.141, str: "TestObj")

proc foo(x: int): int =
  let y = x + 2
  return y * 10

proc bar(x: int): int =
  if x == 3:
    return foo(x)
  return x * 100

proc main =
  var a = 1
  a += 3
  let str = "foobar"
  var seq1 = @[0, 1, 2, 3, 4]
  a = bar(1)
  a = bar(2)
  a = bar(3)
  let tobj = initTestObj(11)

main()

Compile this code with --debugger:native and load it to nim-gdb.

nim c --debugger:native test.nim
nim-gdb test

If you cannot find nim-gdb, you can download bin/nim-gdb (or bin/nim-gdb.bat for Windows) and tools/debug/nim-gdb.py from Nim repository. If you don't use nim-gdb, execute source tools/debug/nim-gdb.py command on gdb. nim-gdb is a bash script that execute GDB and let GDB load tools/debug/nim-gdb.py. And nim-gdb.py is a Python script that make GDB print Nim variables nicely.

On Windows, nim-gdb is a bash script and you cannot use it without bash. So you need to load tools\debug\nim-gdb.py using source command. Please run following command on GDB:

source {path-to-nim}\tools\debug\nim-gdb.py

Or execute GDB like this:

gdb -eval-command "source {path-to-nim}\tools\debug\nim-gdb.py" test

Following message will be displayed after run nim-gdb. If a executable file contains information that help debuging, GDB prints Reading symbols from test...done. If you compiled a code without --debugger:native options, it will print Reading symbols from test...(no debugging symbols found)...done.

GNU gdb (Gentoo 8.1 p1) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from test...done.
(gdb)

You can quit GDB with the quit or q command or Ctrl-d key.

let's set a break point. If you set a break point inside souce code, the program will pause when it reached to that point. You can make a break point by executing b or break command with procedure name. But you cannot specify a procedure name as is to break command. You have to specify a procedure name with hash value. For example, when you make a break point to procedure foo, Type break foo_ and push a tab key. Then, hash value added to that procedure name will be complemented.

(gdb) break foo_yBfYXi2FBVfUOybMAWEXjA_2
Breakpoint 1 at 0x10544: file /tmp/tmp/test.nim, line 10.

You can also make a break point with filename and line number.

(gdb) break test.nim:21
Breakpoint 2 at 0x1088a: file /tmp/tmp/test.nim, line 21.

run command start your program under GDB. Then, the program stop at the break point in line 21 of test.nim. It stops right before the a += 3 is executed.

Starting program: /tmp/tmp/test

Breakpoint 2, main_Ak9bvQf5hr5bnkcYq9cDinOw ()
    at /tmp/tmp/test.nim:21
21        a += 3

You can see the value of an variable using print or p command.

(gdb) print a
$1 = 1

You can execute only 1 line with next or n command. Then, a += 3 is executed.

(gdb) next
22        let str = "foobar"
(gdb) print a
$2 = 4

You can specify a number of lines to execute to next command.

(gdb) next 3
25        a = bar(2)
(gdb) print a
$3 = 100

You can enter a procedure that will be called in next line with step or s command.

(gdb) step
bar_yBfYXi2FBVfUOybMAWEXjA (x=2) at /tmp/tmp/test.nim:14
14      proc bar(x: int): int =

finish or fin command run the program until it exits currently executing procedure.

(gdb) finish
Run till exit from #0  bar_yBfYXi2FBVfUOybMAWEXjA (x=2)
    at /tmp/tmp/test.nim:14
0x000055555556494b in main_Ak9bvQf5hr5bnkcYq9cDinOw ()
    at /tmp/tmp/test.nim:25
25        a = bar(2)
Value returned is $4 = 200
(gdb) next
26        a = bar(3)
(gdb) print a
$5 = 200

continue or c command resume program execution util the program ends or it reach to a breakpoint.

(gdb) continue
Continuing.

Breakpoint 1, foo_yBfYXi2FBVfUOybMAWEXjA_2 (x=3)
    at /tmp/tmp/test.nim:10
10      proc foo(x: int): int =

backtrace or bt command display a backtrace. You can see bar(3) procedure was called from main procedure at line 26, and foo(3) procedure was called from bar procedure at line 16.

(gdb) backtrace
#0  foo_yBfYXi2FBVfUOybMAWEXjA_2 (x=3) at /tmp/tmp/test.nim:10
#1  0x0000555555564697 in bar_yBfYXi2FBVfUOybMAWEXjA (x=3)
    at /tmp/tmp/test.nim:16
#2  0x000055555556496c in main_Ak9bvQf5hr5bnkcYq9cDinOw ()
    at /tmp/tmp/test.nim:26
#3  0x0000555555564b38 in NimMainModule ()
    at /tmp/tmp/test.nim:29
#4  0x0000555555564a46 in NimMainInner ()
    at /tmp/tmp/Nim/lib/system.nim:3154
#5  0x0000555555564a82 in NimMain ()
    at /tmp/tmp/Nim/lib/system.nim:3162
#6  0x0000555555564ad0 in main (argc=1, args=0x7fffffffde58,
    env=0x7fffffffde68) at /tmp/tmp/Nim/lib/system.nim:3169

list or l command print lines from source code.

(gdb) list
5           str: string
6
7       proc initTestObj(num: int): TestObj =
8         TestObj(num: num, val: 3.141, str: "TestObj")
9
10      proc foo(x: int): int =
11        let y = x + 2
12        return y * 10
13
14      proc bar(x: int): int =

info breakpoints or info break command prints a table of all breakpoints.

(gdb) info breakpoints
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000555555564544 in foo_yBfYXi2FBVfUOybMAWEXjA_2 at /tmp/tmp/test.nim:10
        breakpoint already hit 1 time
2       breakpoint     keep y   0x000055555556488a in main_Ak9bvQf5hr5bnkcYq9cDinOw at /tmp/tmp/test.nim:21
        breakpoint already hit 1 time

You can delete a breakpoint by specifying a number on above list to delete or d command.

(gdb) delete 2
(gdb) info breakpoints
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000555555564544 in foo_yBfYXi2FBVfUOybMAWEXjA_2 at /tmp/tmp/test.nim:10
        breakpoint already hit 1 time

info locals command prints all local variables of a procedure.

(gdb) next
11        let y = x + 2
(gdb) next
12        return y * 10
(gdb) info locals
result = 0
y = 5
TM_ipcYmBC9bj9a1BW35ABoB1Kw_6 = 5
TM_ipcYmBC9bj9a1BW35ABoB1Kw_7 = 93824992304216
FR_ = {prev = 0x7fffffffdc20,
  procname = 0x555555565d58 "foo", line = 11,
  filename = 0x555555565d5c "test.nim", len = 0,
  calldepth = 3}
(gdb) finish
Run till exit from #0  foo_yBfYXi2FBVfUOybMAWEXjA_2 (x=3)
    at /tmp/tmp/test.nim:12
0x0000555555564697 in bar_yBfYXi2FBVfUOybMAWEXjA (x=3)
    at /tmp/tmp/test.nim:16
16          return foo(x)
Value returned is $2 = 50
(gdb) finish
Run till exit from #0  0x0000555555564697 in bar_yBfYXi2FBVfUOy
bMAWEXjA (x=3) at /tmp/tmp/test.nim:16
0x000055555556496c in main_Ak9bvQf5hr5bnkcYq9cDinOw ()
    at /tmp/tmp/test.nim:26
26        a = bar(3)
Value returned is $3 = 50
(gdb) next
27        let tobj = initTestObj(11)
(gdb) next
28
(gdb) info locals
a = 50
TM_ipcYmBC9bj9a1BW35ABoB1Kw_2 = 4
str = "foobar"
seq1 = seq(5, 5) = {0, 1, 2, 3, 4}
tobj = {num = 11, val = 3.141, str = "TestObj"}
FR_ = {prev = 0x7fffffffdce0,
  procname = 0x555555565d75 "main", line = 27,
  filename = 0x555555565d5c "test.nim", len = 0,
  calldepth = 1}

GDB Text User Interface

(This mode is not available on windows?)

In GDB Text User Interface(TUI) mode, screen is split and it can show source code, assembly, or regisers. You can enable TUI mode with tui enable command, and disable with tui disable. You can also change mode by pushing a key after Ctrl-a key. TUI mode is enabled by default by adding -tui option when you execute GDB command.

If the screen messed up, you can refresh it with Ctrl + L key.

Debug other simple program with GDB

Let's debug following code with GDB. Save it as filename test2.nim.

import os, strutils

proc main =
  let params = commandLineParams()
  if params.len == 0:
    return
  
  let count = parseInt(params[0])
  
  var x = 0
  var sum = 0
  for i in 0..count:
    inc sum
    if sum == 10:
      inc x
  
  echo sum

main()

You can specify the arguments to your program by adding --args option to nim-gdb and append the arguments after the program filenae.

nim-gdb --args test2 1000 foo
(gdb) break test2.nim:11
Breakpoint 1 at 0x11b35: file /tmp/tmp/test2.nim, line 11.
(gdb) run
Starting program: /tmp/tmp/test2 1000 foo
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, main_3iLAKFrCD49cjgkzKbLZt2A () at /tmp/tmp/test2.nim:11
11        var sum = 0
(gdb) print params
$1 = seq(2, 2) = {"1000", "foo"}

You can also specify the arguments to your program to run command.

nim-gdb test2
(gdb) break test2.nim:11
Breakpoint 1 at 0x11b35: file /tmp/tmp/test2.nim, line 11.
(gdb) run 1000 foo
Starting program: /tmp/tmp/test2 1000 foo
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, main_3iLAKFrCD49cjgkzKbLZt2A () at /tmp/tmp/test2.nim:11
11        var sum = 0
(gdb) print params
$1 = seq(2, 2) = {"1000", "foo"}

Watchpoint stop your program whenever the value of the specified variable changed.

(gdb) watch x
Hardware watchpoint 2: x
(gdb) continue
Continuing.

Hardware watchpoint 2: x

Old value = 0
New value = 1
0x0000555555565c3b in main_3iLAKFrCD49cjgkzKbLZt2A () at /tmp/tmp/test2.nim:15
15            inc x
(gdb) print sum
$1 = 10
(gdb) print x
$2 = 1

You can change the value of the specified variable using print command.

(gdb) print sum = 0
$3 = 0
(gdb) print sum
$4 = 0
(gdb) continue
Continuing.

Hardware watchpoint 2: x

Old value = 1
New value = 2
0x0000555555565c3b in main_3iLAKFrCD49cjgkzKbLZt2A () at /tmp/tmp/test2.nim:15
15            inc x
(gdb) print sum
$5 = 10
(gdb) print x
$6 = 2

You can execute only 1 line with until or u command and it can be used to exit loop.

(gdb) until
2166            inc(res)
(gdb) until
2164          while res <= int(b):
(gdb) until
17        echo sum

Debug Nim compiler with GDB

Nim compiler is written with Nim language and compiled with Nim compiler. So you can debug it with nim-gdb. At first, compile Nim as written on readme.md.

git clone https://github.com/nim-lang/Nim.git
cd Nim

On linux:

sh build_all.sh

On windows:

build_all.bat

Compile Nim compiler for debug. nim_temp will be created in bin directory.

koch temp

Debug nim_temp.

nim-gdb --args bin/nim_temp c ../test.nim

Other useful GDB commands


by Tomohiro

index