Golf Has Its Own Programming Language—Here’s How to Debug It Like a C Pro
To include debugging information in your application executable (be it application server or a command-line), use “–debug” option, for instance when build the application:
gg -q --debug
This will include debugging information in your application code, so it can be debugged with gdb. You can debug your Golf program with gdb just as if it were a plain C program, which technically it is. This makes it much easier to get to the bottom of any issue directly without having to deal with virtual machines, p-code translation, assembler etc.
For this reason, the debugging ecosystem for Golf programs is already fully developed. For instance you can use Valgrind or Google ASAN with Golf programs just as you’d with a C program.
Note that in order to debug the Golf itself, it needs to have debug information, so either has to be compiled from source with debug information included, or you need to use the included debugging information (if installed from a package). This will be covered in another article here on Golf blog.
Example
Here’s an example of debugging using Golf. We’ll create a little parsing application to illustrate.
Create an application “split” (since you’ll be splitting a URL query string into name/value pairs):
gg -k split
Create a source file “parse.golf” and copy this:
begin-handler /parse
silent-header
set-string str = "a=1&b=2&c=3"
// Split string using "&" as a delimiter
split-string str with "&" to pair count pair_tot
start-loop repeat pair_tot use pair_count
read-split pair_count from pair to item
// Now split each item using "=" as a delimiter
split-string item with "=" to equal
read-split 1 from equal to name
read-split 2 from equal to value
// Output each name/value
pf-out "Name [%s] value [%s]\n", name, value
end-loop
end-handler
This program will parse the string “a=1&b=2&c=3” to produce name/value pairs – this is obviously a parsing of URL query string. It uses split-string statement which will split the string based on some delimiter string, and then you use read-split statement to get the split pieces one by one. Very simple.
Compile and link this:
gg -q --public --debug
What’s what here? First of all “-q” will make your project (meaning compile and link it, both command-line executable and application server).
“–public” makes all handlers public, meaning they can be called directly from an outside caller.
“–debug” will add debugging information, so we can use gdb to debug this program. This is crucial to use gdb for debugging.
Debugging
Okay so now to execute this with gdb, do this:
gg -r --req="/parse"
This will produce the following:
export CONTENT_TYPE=
export CONTENT_LENGTH=
unset GG_SILENT_HEADER
export GG_SILENT_HEADER
export REQUEST_METHOD=GET
export SCRIPT_NAME="/split"
export PATH_INFO="/parse"
export QUERY_STRING=""
/var/lib/gg/bld/split/split
This is basically setting the necessary environment variables and then executing the “split” program (specified here in full path). Remember, Golf program can execute exactly the same as a web application server as well as command-line executable; hence the web environment variables need to be set.
So if you copy and paste the above into bash shell, you’ll get:
Name [a] value [1]
Name [b] value [2]
Name [c] value [3]
Which is the proper parsing of the string. Okay so far so good.
Now to debug your program, do:
gdb /var/lib/gg/bld/split/split
You are now in gdb debugging shell:
GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git
Copyright (C) 2024 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
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-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
.
Find the GDB manual and other documentation resources online at:
.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /var/lib/gg/bld/split/split...
(gdb)
Now you can use standard gdb commands. We’ll set a break in our code above (which is handler “parse”), and then run the program. Next we’ll simply execute one statement at a time, and then we’ll print the loop variable:
(gdb) br parse
Breakpoint 1 at 0x3a5e: file /var/lib/gg/bld/split/__parse.o.c, line 4.
(gdb) run
Starting program: /var/lib/gg/bld/split/split
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1.1, parse () at /var/lib/gg/bld/split/__parse.o.c:4
4 void parse () {
(gdb) next
2 silent-header
(gdb)
3 set-string str = "a=1&b=2&c=3"
(gdb)
4 split-string str with "&" to pair count pair_tot
(gdb)
5 start-loop repeat pair_tot use pair_count
(gdb)
6 read-split pair_count from pair to item
(gdb)
5 start-loop repeat pair_tot use pair_count
(gdb)
6 read-split pair_count from pair to item
(gdb)
7 split-string item with "=" to equal
(gdb)
8 read-split 1 from equal to name
(gdb)
9 read-split 2 from equal to value
(gdb)
11 pf-out "Name [%s] value [%s]\n", name, value
(gdb)
Name [a] value [1]
6 read-split pair_count from pair to item
(gdb) print pair_count
$1 = 1
As you can see, you’re stepping through your Golf program, with the code showing exactly as it is in your source file! You can do here anything that you can in a C program, for instance set breakpoints, conditions etc. In this case we also print the value of variable pair_count.
gdb is too powerful to summarize here. There are many tutorials on the web. But suffice it to say you can easily debug your programs and see how they work step by step and a lot more (that’s an understatement!).
Finally, if you’re so inclined, you can also step through generated C code and see exactly what makes your program tick. To do that, make your program with “–c-lines” flag, which lets you do that:
gg -q --public --c-lines --debug
Now, repeat the above to get into gdb, and it may look like this (note that generated code can always change and there’s absolutely no guarantee it will stay the same!):
Breakpoint 1.1, parse () at /var/lib/gg/bld/split/__parse.o.c:4
4 void parse () {
(gdb) next
7 gg_get_config ()->ctx.req->silent=1;
(gdb) n
10 char *str = GG_EMPTY_STRING;
(gdb)
17 if (_gg_gstr_once0) {
(gdb)
18 gg_gstr_ret0 = gg_mem_add_const (_gg_gstr0, sizeof(_gg_gstr0)-GG_ALIGN);
(gdb)
19 _gg_gstr_once0 = false;
(gdb)
21 str = gg_gstr_ret0;
(gdb)
22 gg_mem_add_ref (gg_gstr_ret0);
(gdb)
29 if (_gg_gstr_once1) {
(gdb)
30 gg_gstr_ret1 = gg_mem_add_const (_gg_gstr1, sizeof(_gg_gstr1)-GG_ALIGN);
(gdb)
31 _gg_gstr_once1 = false;
(gdb)
33 gg_split_str *pair = NULL;
(gdb)
34 gg_num pair_tot = 0;
(gdb)
35 gg_break_down (str, gg_gstr_ret1, &(pair));
(gdb)
36 pair_tot= (pair)->num_pieces;
(gdb)
39 gg_num pair_count = 0;
(gdb)
This is literally the C code that runs your program. It doesn’t look as user friendly for sure, but it’s useful if you’d like to see what exactly takes place. It’s also useful in debugging your program and Golf as well.
Now you know how to debug your Golf programs with gdb.