@@ -18,17 +18,17 @@ knitr::opts_chunk$set(
18
18
19
19
# Debugging Zig applications
20
20
21
- Being able to debug your programs is essential to any programmer who wants to
21
+ Being able to debug your applications is essential for any programmer who wants to
22
22
do serious programming in any language. That is why, in this chapter, we are going to talk about the
23
23
available strategies and tools to debug applications written in Zig.
24
24
25
25
26
- ## Printing debugging
26
+ ## Print debugging
27
27
28
28
We begin with the classic and battle-tested * print debugging* strategy.
29
29
The key advantage that debugging offers you is * visibility* .
30
30
With * print statements* you can easily see what results and objects
31
- that are being generated within your functions .
31
+ are being produced by your application .
32
32
33
33
That is the essence of * print debugging* . Is to use
34
34
print expressions to see the values that are being generated by your program,
@@ -37,20 +37,20 @@ is behaving.
37
37
38
38
Many programmers often resort to the print functions in Zig, such as the ` stdout.print() ` ,
39
39
or, the ` std.debug.print() ` , to get a better understanding of their programs.
40
- This is an known and old strategy that is very simple and effective, and it is better known within
40
+ This is a known and old strategy that is very simple and effective, and it is better known within
41
41
the programming community as * print debugging* .
42
- In Zig, you can either print information to the ` stdout ` or ` stderr ` streams of your system.
42
+ In Zig, you can print information to the ` stdout ` or ` stderr ` streams of your system.
43
43
44
44
Let's begin with ` stdout ` . First, you
45
45
need to get access to the ` stdout ` , by calling the ` getStdOut() ` method, from
46
- the Zig standard library . This method returns a * file descriptor* object,
46
+ the Zig Standard Library . This method returns a * file descriptor* object,
47
47
and, through this object you can read/write to the ` stdout ` .
48
48
I recommend you to check out all methods available in this object, by [ checking the page in
49
49
the Zig Standard Library Official Reference for the type ` File ` ] ( https://ziglang.org/documentation/master/std/#std.fs.File ) [ ^ zig-fiile-reference ] .
50
50
51
51
[ ^ zig-fiile-reference ] : < https://ziglang.org/documentation/master/std/#std.fs.File > .
52
52
53
- For our purpose here, which is to write something to the ` stdout ` , specially to debug our
53
+ For our purpose here, which is to write something to the ` stdout ` , especially to debug our
54
54
program, I recommend you to use the ` writer() ` method, which gives your a * writer* object.
55
55
This * writer* object offers some helper methods to write stuff into the file descriptor object
56
56
that represents the ` stdout ` stream. In special, the ` print() ` method.
@@ -64,7 +64,7 @@ in the second argument, you provide a list of values (or objects) that you want
64
64
into your template message.
65
65
66
66
Ideally, the template string in the first argument should contain some format specifier.
67
- Each format specifier is matched to a value (or object) that you listed in the second argument.
67
+ Each format specifier is matched to a value (or object) that you have listed in the second argument.
68
68
So, if you provided 5 different objects in the second argument, then, the template string
69
69
should contain 5 format specifiers, one for each object provided.
70
70
@@ -80,7 +80,7 @@ Here is a quick list of the most used format specifiers:
80
80
- ` x ` : for printing hexadecimal values.
81
81
- ` any ` : use any compatible format specifier (i.e. it automatically selects a format specifier for you).
82
82
83
- The code example below, gives you an example of use of this ` print() ` method
83
+ The code example below gives you an example of use of this ` print() ` method
84
84
with the ` d ` format specifier.
85
85
86
86
``` {zig}
@@ -122,11 +122,23 @@ pub fn main() !void {
122
122
Result: 50
123
123
```
124
124
125
+ You could also achieve the exact same result by getting a file descriptor object to ` stderr ` ,
126
+ then, creating a * writer* object to ` stderr ` , then, using the ` print() ` method of this
127
+ * writer* object, like in the example below:
128
+
129
+ ``` {zig}
130
+ #| eval: false
131
+ const std = @import("std");
132
+ const stderr = std.io.getStdErr().writer();
133
+ // some more lines ...
134
+ _ = try stderr.print("Result: {d}", .{result});
135
+ ```
136
+
125
137
126
138
127
139
## Debugging through debuggers
128
140
129
- Although * print debugging* is a valid and very useful strategy,
141
+ Although * print debugging* being a valid and very useful strategy,
130
142
most programmers prefer to use a debugger to debug their programs.
131
143
Since Zig is a low-level language, you can use either GDB (GNU Debugger),
132
144
or LLDB (LLVM Project Debugger) as your debugger.
@@ -136,28 +148,28 @@ You choose the debugger of your preference, and you work with it.
136
148
In this book, I will use LLDB as my debugger on the examples.
137
149
138
150
139
- ### Compile your source code in Debug mode {#sec-compile-debug-mode}
151
+ ### Compile your source code in debug mode {#sec-compile-debug-mode}
140
152
141
153
In order to debug your program through a debugger, you must compile
142
- your source code in Debug mode. Because when you compile your
143
- source code in other modes (such as Release), the compiler usually
154
+ your source code in ` Debug ` mode. Because when you compile your
155
+ source code in other modes (such as ` Release ` ), the compiler usually
144
156
strips out some essential information that is used by the debugger
145
157
to read and track your program, like PDB (* Program Database* ) files.
146
158
147
- By compiling your source code in Debug mode, you ensure that the debugger
159
+ By compiling your source code in ` Debug ` mode, you ensure that the debugger
148
160
will find the necessary information in your program to debug it.
149
- By default, the compiler uses the Debug mode. Having this in mind,
150
- when you compile your program with the ` build-exe `
151
- command (that we exposed at @sec-compile-code ), if you don't specify an explicit mode through the ` -O ` command-line [ ^ oargument ]
152
- argument, then, the compiler will compile your code in Debug mode.
161
+ By default, the compiler uses the ` Debug ` mode when compiling your code.
162
+ Having this in mind, when you compile your program with the ` build-exe `
163
+ command (which was described at @sec-compile-code ), if you don't specify
164
+ an explicit mode through the ` -O ` command-line [ ^ oargument ]
165
+ argument, then, the compiler will compile your code in ` Debug ` mode.
153
166
154
167
[ ^ oargument ] : See < https://ziglang.org/documentation/master/#Debug > .
155
168
156
169
157
170
### Let's debug a program
158
171
159
- As an example, let's debug some Zig code, and demonstrate
160
- how can we use LLDB to navigate and check the following
172
+ As an example, let's use LLDB to navigate and investigate the following
161
173
piece of Zig code:
162
174
163
175
``` {zig}
@@ -182,7 +194,7 @@ pub fn main() !void {
182
194
There is nothing wrong with this program. But it is
183
195
a good start for us. First, we need to compile
184
196
this program with the ` zig build-exe ` command.
185
- For this example, suppose that I compiled the above
197
+ For this example, suppose that I have compiled the above
186
198
Zig code into a binary executable called ` add_program ` .
187
199
188
200
``` bash
@@ -226,12 +238,12 @@ Process 8654 stopped
226
238
14 }
227
239
` ` `
228
240
229
- I can start to navigate through the code, and investigating the variables
230
- that are being generated. If you not familiar with the commands
241
+ I can start navigating through the code, and checking the objects
242
+ that are being generated. If you are not familiar with the commands
231
243
available in LLDB, I recommend you to read the official documentation
232
244
of the project[^lldb].
233
- You can also look for cheat sheets. Which quickly describes all commands
234
- available for you, and, as a result, are also good resources for you [^lldb-quick-list].
245
+ You can also look for cheat sheets, which quickly describes all commands
246
+ available for you[^lldb-quick-list].
235
247
236
248
[^lldb]: < https://lldb.llvm.org/>
237
249
[^lldb-quick-list]: < https://gist.github.com/ryanchang/a2f738f0c3cc6fbd71fa> .
@@ -271,11 +283,11 @@ Process 4798 stopped
271
283
272
284
Now, on the next line of code, we are executing the `add_and_increment()` function once again.
273
285
Why not step inside this function? Shall we? We can do that, by executing the `s` LLDB command.
274
- Notice in the example below that, after executing this command, we entered inside the context of the
286
+ Notice in the example below that, after executing this command, we have entered into the context of the
275
287
`add_and_increment()` function.
276
288
277
- Also notice in the example below that, I walked two more lines in the functions body, then,
278
- I executed the `frame variable` LLDB command, to see at once, the value stored in each of the variables
289
+ Also notice in the example below that, I have walked two more lines in the function ' s body, then,
290
+ I execute the ` frame variable` LLDB command, to see at once, the value stored in each of the variables
279
291
that were created inside the current scope.
280
292
281
293
You can see in the output below that, the object ` sum` stores the value ` \f ` ,
@@ -337,3 +349,4 @@ pub fn main() !void {
337
349
338
350
This function is similar to the `type ()` built-in function from Python,
339
351
or, the ` typeof` operator in Javascript.
352
+
0 commit comments