What are tracepoints?
Tracepoints are a type of breakpoint that allows you to run arbitrary code every time this breakpoint is hit. In most debuggers that support tracepoints they allow for efficient debugging of complex scenarios, like really deep recursion, or complex control flow.
In CodeTracer, however, they can also unlock a number of strategies for additional debugging, specifically in the realm of hotspot debugging.
Tracepoints usage guide
From the GUI:
- Right-click on a line
- Click on "Add tracepoint"
- The tracepoints popup should appear
- Write your tracepoint code in the text editor in the tracepoints popup
- Press "CTRL + Enter" to run the trace
- After running the trace, the output of your tracepoint will be listed in the tracepoint popup, and in the event log
From the TUI: Coming soon!
From the REPL: Coming soon!
Tracepoints language
Syntax
The syntax of the language is similar to Noir/Rust (and therefore most C-like languages). However it doesn't use semicolons.
In the future it is possible to be added language-specific features or dialects of the Tracepoint language.
Literals
Integer (1, 23, 12), Float
(1.23, .234, 123.), Bool
(true, false) and String
("some text") literals are supported.
log()
The log() statement is used to evaluate the argument
expression and add it as output from the current tracepoint. The
log() statement suppors multiple values that are
comma-separated: log(v1, v2, v3).
Example
fn test() {
let mut sum = 0;
for i in 0..3 {
sum += i;
--------------------------------------------------------------------------------
| log("I'm in the loop", i)
|
--------------------------------------------------------------------------------
-- Output:
--------------------------------------------------------------------------------
| "I'm in the loop" i=0
| "I'm in the loop" i=1
| "I'm in the loop" i=2
--------------------------------------------------------------------------------
}Accessing variables
The tracepoint for has access to all the variables, that are defined when the line on which the tracepoint is added is evaluated. You can reference them just by using their names in the expressions.
Example
fn add(a: i32, b: i32) -> i32 {
a + b
--------------------------------------------------------------------------------
| log(a)
| log(b)
--------------------------------------------------------------------------------
-- Output:
--------------------------------------------------------------------------------
| a=3 b=5
--------------------------------------------------------------------------------
}Comparison
== and !=
Two values are considered eqial iff their types are the same and their values are the same. Exception to this rule is comparing Int and Float, which are compared by their values, despite them being different type.
<, >, <=,
>=
These operators work only with numerical values (e.g Int and Float). If at least one of the values is of non-numerical type, then an Error is raised.
Example
| Expression | Value |
|---|---|
| 1 == 1 | true |
| 1 != 1 | false |
| 1 == 2 | false |
| 1 != 2 | true |
| "banana" == "banana" | true |
| "banana" != "banana" | false |
| "banana" == "apple" | false |
| "banana" != "apple" | true |
| "banana" == 1 | false |
| "banana" != 1 | true |
| 1.0 == 1 | true |
| 1.0 != 1 | false |
| 2.0 == 1 | false |
| 2.0 != 1 | true |
| "1" == 1 | false |
| "1" != 1 | true |
| 1 < 2 | true |
| 1 <= 2 | true |
| 1 > 2 | false |
| 1 >= 2 | false |
| 1 < 2.2 | true |
| 1.1 <= 2 | true |
| 1 > 2.2 | false |
| 1.1 >= 2 | false |
| 1 < "2" | ERROR |
| "0" < 1 | ERROR |
| "1" >= "2" | ERROR |
Arithmetic operations
The supported arithmetic operations are addition (+),
subtraction (-), multiplication (*), division
(/) and remainder (%). They work only on
numerical types (Int and Float).
When both arguments are Integer values, then the result is Integer
(for / the result is rounded toward 0). If at least one of
the arguments is Float, then the result is Float.
Example
| Expression | Value |
|---|---|
| 2 + 3 | 5 |
| 2 + 3.0 | 5.0 |
| 2.2 + 3.3 | 5.5 |
| 2 - 3 | -1 |
| 2 - 3.0 | -1.0 |
| 2.2 - 3.3 | -1.1 |
| 2 * 3 | 6 |
| 2 * 3.0 | 6.0 |
| 2.2 * 3.3 | 7.26 |
| 7 / 3 | 2 |
| 7 / 3.0 | 2.3333333 |
| 7.7 / 3.3 | 2.3333333 |
| 7 % 3 | 1 |
| 7 % 3.0 | 1.0 |
| 7.7 % 3.3 | 1.1 |
Conditional branching
(if-else)
The tracepoint language also supports conditional evaluation and branching. If the condition expression doesn't evaluate to boolean value, then an error is raised.
Example
fn test() {
let mut sum = 0;
for i in 0..4 {
sum += i;
--------------------------------------------------------------------------------
| log(i)
| if i % 2 == 0 {
| log("even")
| } else if i % 3 == 0 {
| log("div 3")
| } else {
| log("odd")
| }
--------------------------------------------------------------------------------
-- Output:
--------------------------------------------------------------------------------
| i=0 even
| i=1 odd
| i=2 even
| i=3 div 3
--------------------------------------------------------------------------------
}
}Array indexing
If a value is an array, you can index it using the []
operators. Indexes are 0 based.
<<<<<<< HEAD
Example
fn arr(a: i32, b: i32) -> i32 {
let a = [1, 2, 3, 4, 5];
let b = a[2];
--------------------------------------------------------------------------------
| log(a)
| log(a[0])
| log(a[1])
| log(a[4])
--------------------------------------------------------------------------------
-- Output:
--------------------------------------------------------------------------------
| a=[1, 2, 3, 4, 5] a[0]=1 a[1]=2 a[4]=5
--------------------------------------------------------------------------------
}Errors
When an error occurs, the execution of the tracepoint stops.
Example
fn arr(a: i32, b: i32) -> i32 {
let a = [1, 2, 3, 4, 5];
let b = a[2];
--------------------------------------------------------------------------------
| log(a[0])
| if a[1] { // This will cause error
| log("banana")
| }
| log(a[2]) // This won't be evaluated
--------------------------------------------------------------------------------
-- Output:
--------------------------------------------------------------------------------
| a[0]=1 Error=Non-boolean value on conditional jump
--------------------------------------------------------------------------------
}=======
Rust-specific extensions
We have some language-specific extensions in mind, but they're not ready yet.