You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implement stack traces
This PR adds support for stacktraces, both in the display of crash reports, as
well as programmatic access to stacktrace data as terms in catch clauses.
Stacktrace data is represented as a list of tuples, each of which represents a
stack “frame”. Each tuple is of the form:
```
[{Module :: module(), Function :: atom(), Arity :: non_neg_integer(), AuxData :: aux_data()}]
```
where `aux_data()` is a (possibly empty) properties list containing the following elements:
```
[{file, File :: string(), line, Line :: pos_integer()}]
```
Stack frames are ordered from the frame “closest“ to the point of failure
(the “top” of the stack) to the frame furthest from the point of failure
(the “bottom” of the stack).
Stack frames will contain file and line information in the `AuxData` list if the
BEAM files (typically embedded in AVM files) include `<<“Line”>>` chunks
generated by the compiler. Otherwise, the `AuxData` will be empty.
Note that adding line information to BEAM files not only increases the size of
BEAM files in storage, but calculation of file and line information can have a
non-negligible impact on memory usage. Memory-sensitive applications should
consider not including line information in BEAM files.
Addresses issue #254.
These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).
SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
Copy file name to clipboardExpand all lines: doc/src/atomvm-internals.md
+38Lines changed: 38 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -89,3 +89,41 @@ The relationship between the `GlobalContext` fields that manage BEAM processes a
89
89
## Exception Handling
90
90
91
91
## The Scheduler
92
+
93
+
## Stacktraces
94
+
95
+
Stacktraces are computed from information gathered at load time from BEAM modules loaded into the application, together with information in the runtime stack that is maintained during the execution of a program. In addition, if a BEAM file contains a `Line` chunk, additional information is added to stack traces, including the file name (as defined at compile time), as well as the line number of a function call.
96
+
97
+
> Note. Adding line information to a BEAM file adds non-trivial memory overhead to applications and should only be used when necessary (e.g., during the development process). For applications to make the best use of memory in tightly constrained environments, packagers should consider removing line information all together from BEAM files and rely instead on logging or other mechanisms for diagnosing problems in the field.
98
+
99
+
Newcomers to Erlang may find stacktraces slightly confusing, because some optimizations taken by the Erlang compiler and runtime can result in stack frames "missing" from stack traces. For example, tail-recursive function calls, as well as function calls that occur as the last expression in a function clause, don't involve the creation of frames in the runtime stack, and consequently will not appear in a stacktrace.
100
+
101
+
### Line Numbers
102
+
103
+
Including file and line number information in stacktraces adds considerable overhead to both the BEAM file data, as well as the memory consumed at module load time. The data structures used to track line numbers and file names are described below and are only created if the associated BEAM file contains a `Line` chunk.
104
+
105
+
#### The line-refs table
106
+
107
+
The line-refs table is an array of 16-bit integers, mapping line references (as they occur in BEAM instructions) to the actual line numbers in a file. (Internally, BEAM instructions do not reference line numbers directly, but instead are indirected through a line index). This table is stored on the `Module` structure.
108
+
109
+
This table is populated when the BEAM file is loaded. The table is created from information in the `Line` chunk in the BEAM file, if it exists. Note that if there is no `Line` chunk in a BEAM file, this table is not created.
110
+
111
+
The memory cost of this table is `num_line_refs * 2` bytes, for each loaded module, or 0, if there is no `Line` chunk in the associated BEAM file.
112
+
113
+
#### The filenames table
114
+
115
+
The filenames table is a table of (usually only 1?) file name. This table maps filename indices to `ModuleFilename` structures, which is essentially a pointer and a length (of type `size_t`). This table generally only contains 1 entry, the file name of the Erlang source code module from which the BEAM file was generated. This table is stored on the `Module` structure.
116
+
117
+
Note that a `ModuleFilename` structure points to data directly in the `Line` chunk of the BEAM file. Therefore, for ports of AtomVM that memory-map BEAM file data (e.g., ESP32), the actual file name data does not consume any memory.
118
+
119
+
The memory cost of this table is `num_filenames * sizeof(struct ModuleFilename)`, where `struct ModuleFilename` is a pointer and length, for each loaded module, or 0, if there is no `Line` chunk in the associated BEAM file.
120
+
121
+
#### The line-ref-offsets list
122
+
123
+
The line-ref-offsets list is a sequence of `LineRefOffset` structures, where each structure contains a ListHead (for list book-keeping), a 16-bit line-ref, and an unsigned integer value designating the code offset at which the line reference occurs in the code chunk of the BEAM file. This list is stored on the `Module` structure.
124
+
125
+
This list is populated at code load time. When a line reference is encountered during code loading, a `LineRefOffset` structure is allocated and added to the line-ref-offsets list. This list is used at a later time to find the line number at which a stack frame is called, in a manner described below.
126
+
127
+
The memory cost of this list is `num_line_refs * sizeof(struct LineRefOffset)`, for each loaded module, or 0, if there is no `Line` chunk in the associated BEAM file.
Copy file name to clipboardExpand all lines: doc/src/programmers-guide.md
+44Lines changed: 44 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -202,6 +202,7 @@ The `PackBEAM` tool is a command-line application that can be used to create Pac
202
202
shell$ PackBEAM -h
203
203
Usage: PackBEAM [-h] [-l] <avm-file> [<options>]
204
204
-h Print this help menu.
205
+
-i Include file and line information.
205
206
-l <input-avm-file> List the contents of an AVM file.
206
207
[-a] <output-avm-file> <input-beam-or-avm-file>+ Create an AVM file (archive if -a specified).
207
208
@@ -387,6 +388,49 @@ Use `base64:encode/1` and `base64:decode/1` to encode to and decode from Base64
387
388
388
389
You can Use `base64:encode_to_string/1` and `base64:decode_to_string/1` to perform the same encoding, but to return values as Erlang list structures, instead of as binaries.
389
390
391
+
### StackTraces
392
+
393
+
You can obtain information about the current state of a process via stacktraces, which provide information about the location of function calls (possibly including file names and line numbers) in your program.
394
+
395
+
Currently in AtomVM, stack traces can be obtained in one of following ways:
396
+
397
+
* via try-catch blocks
398
+
* via catch blocks, when an error has been raised via the `error` Bif.
399
+
400
+
> Note. AtomVM does not support `erlang:get_stacktrace/0` which was deprecated in Erlang/OTP 21 and 22, stopped working in Erlang/OTP 23 and was removed in Erlang/OTP 24. Support for accessing the current stacktrace via `erlang:process_info/2` may be added in the future.
401
+
402
+
For example a stack trace can be bound to a variable in the catch clause in a try-catch block:
403
+
404
+
try
405
+
do_something()
406
+
catch
407
+
_Class:_Error:Stacktrace ->
408
+
io:format("Stacktrace: ~p~n", [Stacktrace])
409
+
end
410
+
411
+
Alternatively, a stack trace can be bound to the result of a `catch` expression, but only when the error is raised by the `error` Bif. For example,
412
+
413
+
{'EXIT', {foo, Stacktrace}} = (catch error(foo)),
414
+
io:format("Stacktrace: ~p~n", [Stacktrace])
415
+
416
+
Stack traces are printed to the console in a crash report, for example, when a process dies unexpectedly.
417
+
418
+
Stacktrace data is represented as a list of tuples, each of which represents a stack “frame”. Each tuple is of the form:
where `aux_data()` is a (possibly empty) properties list containing the following elements:
423
+
424
+
[{file, File :: string(), line, Line :: pos_integer()}]
425
+
426
+
Stack frames are ordered from the frame “closest“ to the point of failure (the “top” of the stack) to the frame furthest from the point of failure (the “bottom” of the stack).
427
+
428
+
Stack frames will contain file and line information in the AuxData list if the BEAM files (typically embedded in AVM files) include <<“Line”>> chunks generated by the compiler. Otherwise, the AuxData will be an empty list.
429
+
430
+
> Note. Adding line information to BEAM files not only increases the size of BEAM files in storage, but calculation of file and line information can have a non-negligible impact on memory usage. Memory-sensitive applications should consider not including line information in BEAM files.
431
+
432
+
The PackBEAM tool does not include file and line information in the AVM files it creates, but file and line information can be included via a command line option. For information about the PackBEAM too, see the [`PackBEAM` tool](#Packbeam_tool).
433
+
390
434
### Math
391
435
392
436
AtomvVM supports the following standard functions from the OTP `math` module:
0 commit comments