knazarov.com/content/posts/backtraces_in_valeri/note.md

46 lines
1.8 KiB
Markdown
Raw Permalink Normal View History

2024-09-15 03:38:24 +00:00
X-Date: 2024-09-15T03:18:50Z
X-Note-Id: f53b7c27-df57-4ede-8950-2b55cbb8031f
Subject: Backtraces in Valeri
X-Slug: backtraces_in_valeri
At the time of writing this post, I've finished implementing the first version of
backtraces in [Valeri](https://git.knazarov.com/knazarov/valeri). In case of
runtime errors, you'll now get the call stack printed to stdout, pretty much
as you would expect in other programming languages. Let's look at an example:
```
(fn get-foo (obj)
(get obj :foo)
2024-09-15 03:38:24 +00:00
)
;; This will fail becaue :foo is not present
;; in the dictionary
(get-foo {:bar 42})
```
When executed, this code will result in:
```
Error KeyError at /home/knazarov/dev/lisp/src/common.cpp:735
Function get
Function get-foo
```
The backtrace should look familiar, with the only possible problem being lack
of source location information except in the last frame. This is because I don't
yet produce debug information with line number / filename mappings.
What may not be immediately obvious is that `get` is a "native" function that
is implemented in C++. The reason it appears in the backtrace at all is due
to how native functions are called. This is not just an FFI call, but such functions
actually use the normal VM stack, as regular byte-compiled functions do.
This means that if I implement something like `map` or `fold` in C++ and put it into
the standard library, debugging code that uses them is going to be quite easy, even
if the C++ routines are nested in the middle of the callstack.
Overall, backtraces and the transition to tree-structured stack were the hardest
parts of the language so far. Not in terms of the implementation, but just design-wise.
The new stack structure is going to be used in lots of places starting from exceptions,
and ending with the coroutines, effect system and the debugger.