Internet of Tomohiro
If you found my articles interesting or useful, please donate using following links:
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.
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.
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
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}
(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.
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
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
skip
You can use this command so that step command don't step into specified procedure. With -file or -gfile option, all procedures defined in specified source code will be skipped. For example, all procedures in system module will be skipped with following commands.
skip -gfile lib/system.nim skip -gfile lib/system/*.nim
rbreak regex
Set breakpoints on all procedures matching the regular expression regex.
save breakpoints filename
Saves all current breakpoint definitions to filename. Use the source command to read the saved breakpoints.
by Tomohiro