Skip to content

Commit 73958d3

Browse files
authored
Add section on debugging (#50)
* Add draft on Debugging * Add orange highlighting for debugging prompts
1 parent 234fc1a commit 73958d3

File tree

4 files changed

+202
-7
lines changed

4 files changed

+202
-7
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ package-lock.json
55
.vscode
66
Manifest.toml
77

8-
MyPackage/
8+
MyPackage/
9+
MyProject/

_css/franklin.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,9 @@ code {
312312

313313
.hljs-meta.shell_ {color: crimson;}
314314
.hljs-meta.prompt_ {color: rgb(25, 179, 51);}
315+
.hljs-meta.infiltrator_ {color: rgb(240, 150, 30);}
316+
.hljs-meta.debugger_ {color: rgb(240, 150, 30);}
317+
.hljs-meta.debuggereval_ {color: rgb(240, 150, 30);}
315318

316319
.code-output {
317320
background: var(--output-background);

_libs/highlight/highlight.min.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,9 @@ className:"attr",starts:{end:/$/,contains:[s,l,t,i,r,a]}}]}}})()
613613
subLanguage:"julia"}},{className:"meta.pkg",begin:/^\(.*\) pkg>/,relevance:10,starts:{end:/^(?![ ]{6})/,
614614
subLanguage:"julia"}},{className:"meta.shell",begin:/^shell>/,relevance:10,starts:{end:/^(?![ ]{6})/,
615615
subLanguage:"julia"}},{className:"meta.help",begin:/^help\?>/,relevance:10,starts:{end:/^(?![ ]{6})/,
616+
subLanguage:"julia"}},{className:"meta.infiltrator",begin:/^infil>/,relevance:10,starts:{end:/^(?![ ]{6})/,
617+
subLanguage:"julia"}},{className:"meta.debugger",begin:/^1\|debug>/,relevance:10,starts:{end:/^(?![ ]{6})/,
618+
subLanguage:"julia"}},{className:"meta.debuggereval",begin:/^1\|julia>/,relevance:10,starts:{end:/^(?![ ]{6})/,
616619
subLanguage:"julia"}}],aliases:["jldoctest"]})})()
617620
;hljs.registerLanguage("julia-repl",a)})();/*! `xml` grammar compiled for Highlight.js 11.5.1 */
618621
(()=>{var e=(()=>{"use strict";return e=>{

pages/writing.md

Lines changed: 194 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -312,13 +312,191 @@ More generally, the startup file allows you to define your own favorite helper f
312312
* [Suppressor.jl](https://github.com/JuliaIO/Suppressor.jl)
313313

314314
## Debugging
315+
> TLDR:
316+
> Use the VSCode debugger or Infiltrator.jl.
317+
> Use logging instead of printing.
315318
316-
* [InteractiveCodeSearch.jl](https://github.com/tkf/InteractiveCodeSearch.jl)
317-
* [InteractiveErrors.jl](https://github.com/MichaelHatherly/InteractiveErrors.jl)
318-
* [CodeTracking.jl](https://github.com/timholy/CodeTracking.jl)
319-
* [Infiltrator.jl](https://github.com/JuliaDebug/Infiltrator.jl)
320-
* [Debugger.jl](https://github.com/JuliaDebug/Debugger.jl)
321-
* [debugging in VSCode](https://www.julia-vscode.org/docs/stable/userguide/debugging/)
319+
* [Logging][julia-docs-logging]
320+
* [Debugging in VSCode][vscode-debugger]
321+
* [Debugger.jl][debugger-repo]
322+
* [Infiltrator.jl][infiltrator-repo]
323+
324+
<!-- Undocumented packages
325+
* [InteractiveCodeSearch.jl][interactivesearch-repo]
326+
* [InteractiveErrors.jl][interactiveerrors-repo]
327+
* [CodeTracking.jl][codetracking-repo]
328+
-->
329+
330+
### Logging
331+
332+
Assume you want to debug the following function:
333+
```julia
334+
function sum_of_divisors(n)
335+
divisors = filter(x -> n % x == 0, 1:n)
336+
return sum(divisors)
337+
end
338+
```
339+
```>
340+
sum_of_divisors(6) # should return 1 + 2 + 3
341+
```
342+
343+
Using `@show` or `println`, you can print local variables inside of a function:
344+
345+
```julia:debugshow
346+
function sum_of_divisors(n)
347+
divisors = filter(x -> n % x == 0, 1:n)
348+
@show divisors
349+
return sum(divisors)
350+
end
351+
```
352+
```>
353+
sum_of_divisors(6)
354+
```
355+
356+
> The problem with `sum_of_divisors` is the range `1:n`,
357+
> which includes `n` in the list of computed divisors.
358+
> We can fix the function by changing the range to `1:n-1`.
359+
360+
While printing might suffice to debug simple problems, we can do better.
361+
Julia offers the logging macros `@debug`, `@info`, `@warn` and `@error` that have several advantages over printing. They:
362+
- show the line number they were called from
363+
- label arguments, similar to `@show`
364+
- can be disabled and filtered according to their source module and severity level
365+
- work well in multithreaded code
366+
- can be written to a file
367+
368+
By default, `@debug` messages are suppressed.
369+
You can enable them through the `JULIA_DEBUG` environment variable
370+
by specifying the source module name, e.g. `Main`.
371+
372+
```julia:debuglogging
373+
function sum_of_divisors(n)
374+
divisors = filter(x -> n % x == 0, 1:n)
375+
@debug "sum_of_divisors" n divisors
376+
return sum(divisors)
377+
end
378+
```
379+
380+
<!-- Live REPL mode doesn't print debug output -->
381+
```julia-repl
382+
julia> ENV["JULIA_DEBUG"] = Main # enable @debug logs
383+
Main
384+
385+
julia> sum_of_divisors(6)
386+
┌ Debug: sum_of_divisors
387+
│ n = 6
388+
│ divisors =
389+
│ 4-element Vector{Int64}:
390+
│ 1
391+
│ 2
392+
│ 3
393+
│ 6
394+
└ @ Main REPL[1]:3
395+
12
396+
```
397+
398+
For scripts, you can prefix your command-line call to `julia` with environment variables,
399+
e.g. `JULIA_DEBUG=Main julia myscript.jl`.
400+
Refer the [Julia documentation on logging][julia-docs-logging] for more information.
401+
402+
### VSCode Debugger
403+
404+
Using the [Julia VSCode extension][julia-vscode-repo],
405+
click left of a line number in a VSCode editor pane to add a *breakpoint*,
406+
which is visualized by a red circle.
407+
In the debugging pane of the Julia VSCode extension,
408+
click *Run and Debug* to start the debugger.
409+
The program will automatically halt when it hits a breakpoint.
410+
411+
Using the toolbar at the top of the editor, you can
412+
*continue*, *step over*, *step into* and *step out* of your code.
413+
The debugger will open a pane showing information about the code
414+
such as local variables inside of the current function,
415+
their current values and the call stack.
416+
417+
For more information including explanatory screenshots,
418+
refer to the [Julia VSCode documentation][vscode-debugger].
419+
420+
### Infiltrator.jl
421+
422+
[Infiltrator.jl's][infiltrator-repo] `@infiltrate` macro allows you to directly set breakpoints in your code.
423+
Calling a function which hits a breakpoint will activate the Infiltrator REPL-mode
424+
and change the prompt to `infil>`.
425+
426+
Typing `?` in this mode will summarize available commands.
427+
For example, typing `@locals` in Infiltrator-mode will print local variables:
428+
429+
```julia
430+
using Infiltrator
431+
432+
function sum_of_divisors(n)
433+
divisors = filter(x -> n % x == 0, 1:n)
434+
@infiltrate
435+
return sum(divisors)
436+
end
437+
```
438+
```julia-repl
439+
julia> sum_of_divisors(6)
440+
Infiltrating (on thread 1) sum_of_divisors(n::Int64)
441+
at REPL[4]:3
442+
443+
infil> @locals
444+
- n::Int64 = 6
445+
- divisors::Vector{Int64} = [1, 2, 3, 6]
446+
```
447+
448+
What makes Infiltrator powerful is the `@exfiltrate` macro,
449+
which allows you to move local variables into a global storage called the `safehouse`.
450+
451+
```julia-repl
452+
infil> @exfiltrate divisors
453+
Exfiltrating 1 local variable into the safehouse.
454+
455+
infil> @continue
456+
457+
12
458+
459+
julia> safehouse.divisors
460+
4-element Vector{Int64}:
461+
1
462+
2
463+
3
464+
6
465+
```
466+
467+
### Debugger.jl
468+
469+
Using [Debugger.jl][debugger-repo]'s `@enter` macro, we can enter a function call and step through it.
470+
The prompt changes to `1|debug>`, allowing you to use [Debugger.jl's commands][debugger-commands]
471+
to step into and out of function calls, show local variables and set breakpoints.
472+
473+
Typing `` ` `` will change the prompt to `1|julia>`, indicating evaluation mode. Any expression typed in this mode will be evaluated in the local context.
474+
This is useful to show local variables, as demonstrated in the following example:
475+
476+
```julia-repl
477+
julia> @enter sum_of_divisors(6)
478+
In sum_of_divisors(n) at REPL[3]:1
479+
1 function sum_of_divisors(n)
480+
>2 divisors = filter(x -> n % x == 0, 1:n)
481+
3 return sum(divisors)
482+
4 end
483+
484+
About to run: (typeof)(6)
485+
1|debug> n # n: step to next line
486+
In sum_of_divisors(n) at REPL[3]:1
487+
1 function sum_of_divisors(n)
488+
2 divisors = filter(x -> n % x == 0, 1:n)
489+
>3 return sum(divisors)
490+
4 end
491+
492+
About to run: (sum)([1, 2, 3, 6])
493+
1|julia> divisors # type `, then variable name
494+
4-element Vector{Int64}:
495+
1
496+
2
497+
3
498+
6
499+
```
322500

323501
## Other languages
324502

@@ -332,3 +510,13 @@ More generally, the startup file allows you to define your own favorite helper f
332510
* [cheatsheet](https://cheatsheet.juliadocs.org/)
333511
* [help](https://julialang.org/about/help/)
334512
* [community](https://julialang.org/community/)
513+
514+
[julia-vscode-repo]: https://github.com/julia-vscode/julia-vscode
515+
[vscode-debugger]: https://www.julia-vscode.org/docs/stable/userguide/debugging/
516+
[julia-docs-logging]: https://docs.julialang.org/en/v1/stdlib/Logging/
517+
[infiltrator-repo]: https://github.com/JuliaDebug/Infiltrator.jl
518+
[debugger-repo]: https://github.com/JuliaDebug/Debugger.jl
519+
[debugger-commands]: https://github.com/JuliaDebug/Debugger.jl#debugger-commands
520+
[codetracking-repo]: https://github.com/timholy/CodeTracking.jl
521+
[interactiveerrors-repo]: https://github.com/MichaelHatherly/InteractiveErrors.jl
522+
[interactivesearch-repo]: https://github.com/tkf/InteractiveCodeSearch.jl

0 commit comments

Comments
 (0)