Debugging
Tools for inspecting variables, tracing execution, and profiling performance.
Inspecting Variables
dumper() - Pretty Print Any Value
The dumper() function prints a human-readable representation of any value to stderr:
my hash %user = {
"name" => "Alice",
"age" => 30,
"tags" => ["admin", "developer"]
};
dumper(\%user);
# Output:
# {
# "name" => "Alice",
# "age" => 30,
# "tags" => [
# "admin",
# "developer"
# ]
# }
Works with all types:
dumper(42); # 42
dumper("hello"); # "hello"
dumper([1, 2, 3]); # [1, 2, 3]
dumper(undef); # undef
dumper_str() - Get Dump as String
Returns the dump as a string instead of printing:
my str $debug = dumper_str(\%data);
Log::debug("Request data: " . $debug);
dump() - Compact Output
Similar to dumper() but with an indent parameter for nested output:
dump(\%user, 0); # Start at indent level 0
dump(\%user, 2); # Start at indent level 2
typeof() - Get Type Name
Returns the type of a value as a string:
say(typeof(42)); # "int"
say(typeof(3.14)); # "num"
say(typeof("hello")); # "str"
say(typeof([1, 2])); # "array"
say(typeof({"a" => 1})); # "hash"
say(typeof(\$x)); # "ref"
say(typeof(undef)); # "undef"
refcount() - Check Reference Count
Useful for debugging memory issues:
my array @arr = [1, 2, 3];
say(refcount(\@arr)); # 1
my scalar $ref = \@arr;
say(refcount(\@arr)); # 2
Stack Traces
stacktrace() - Print Call Stack
Prints the current call stack to stderr:
func deep_function() void {
stacktrace();
}
func middle_function() void {
deep_function();
}
func main() int {
middle_function();
return 0;
}
# Output:
# Stack trace:
# deep_function (test.strada:2)
# middle_function (test.strada:6)
# main (test.strada:10)
backtrace() - Alias for stacktrace()
backtrace(); # Same as stacktrace()
stacktrace_str() - Get Stack as String
Returns the stack trace as a string for logging:
my str $trace = stacktrace_str();
Log::error("Error occurred at:\n" . $trace);
caller() - Get Caller Information
Returns the name of the calling function at a given stack level:
func log_message(str $msg) void {
my str $from = caller(1); # Who called me?
say("[" . $from . "] " . $msg);
}
func process_data() void {
log_message("Processing started");
# Output: [process_data] Processing started
}
Stack levels:
caller(0)- Current functioncaller(1)- Direct callercaller(2)- Caller's caller
Compile with Debug Symbols
For better stack traces and GDB debugging, compile with the -g flag:
# Include debug symbols
./strada -g myprogram.strada
# Run with GDB
gdb ./myprogram
Function Profiling
Strada includes a built-in profiler to measure function execution time and call counts.
Enabling the Profiler
func main() int {
sys::profile_init(); # Initialize profiler
# Your code here...
do_work();
sys::profile_report(); # Print profiling report
return 0;
}
Manual Function Instrumentation
For fine-grained control, manually instrument functions:
func expensive_operation() void {
sys::profile_enter("expensive_operation");
# ... do work ...
sys::profile_exit("expensive_operation");
}
Profiler Output
The profiler report shows:
# === Function Profile ===
# Function Calls Total(ms) Avg(ms)
# process_items 1000 2450.32 2.45
# parse_line 50000 1203.45 0.02
# write_output 1000 890.12 0.89
Compiler Timing
To profile the compiler itself, use the -t flag:
./stradac -t input.strada output.c
# Output:
# Lexing: 12.3ms
# Parsing: 45.6ms
# CodeGen: 23.4ms
# Total: 81.3ms
Memory Profiling
Track memory allocations by type:
func main() int {
sys::memprof_enable();
# Your code here...
process_data();
sys::memprof_report(); # Print memory stats
return 0;
}
Memory Profiler Functions
| Function | Description |
|---|---|
sys::memprof_enable() | Start tracking allocations |
sys::memprof_disable() | Stop tracking allocations |
sys::memprof_report() | Print allocation statistics |
sys::memprof_reset() | Clear all counters |
Memory Profile Output
# === Memory Profile ===
# Type Allocs Frees Current Bytes
# int 15000 14950 50 400
# str 8500 8400 100 12800
# array 200 195 5 240
# hash 150 148 2 128
Assertions
Use die() for assertion-style checks:
func divide(int $a, int $b) int {
if ($b == 0) {
die("Assertion failed: divisor cannot be zero");
}
return $a / $b;
}
Custom Assert Function
func assert(int $condition, str $msg) void {
if ($condition == 0) {
my str $trace = stacktrace_str();
die("Assertion failed: " . $msg . "\n" . $trace);
}
}
# Usage
assert(len(@items) > 0, "items array must not be empty");
Logging Patterns
Simple Debug Logger
my int $DEBUG = 1; # Set to 0 in production
func debug(str $msg) void {
if ($DEBUG) {
my str $caller = caller(1);
warn("[DEBUG] [" . $caller . "] " . $msg);
}
}
func process() void {
debug("Starting process");
# [DEBUG] [process] Starting process
}
Conditional Dumping
func debug_dump(str $label, scalar $value) void {
if ($DEBUG) {
warn($label . ":");
dumper($value);
}
}
debug_dump("Request", \%request);
Using GDB
For low-level debugging, use GDB with debug symbols:
# Compile with debug info
./strada -g myprogram.strada
# Start GDB
gdb ./myprogram
# Common GDB commands:
(gdb) break main # Set breakpoint at main
(gdb) run # Start execution
(gdb) bt # Show backtrace
(gdb) print variable # Print variable value
(gdb) next # Step over
(gdb) step # Step into
(gdb) continue # Continue execution
StradaValue* pointers. Use print *variable to see the struct contents, or call strada_to_str(variable) to get the string representation.
Quick Reference
| Function | Description |
|---|---|
dumper($val) | Pretty-print value to stderr |
dumper_str($val) | Return dump as string |
dump($val, $indent) | Dump with indent level |
typeof($val) | Get type name as string |
refcount($ref) | Get reference count |
stacktrace() | Print call stack |
stacktrace_str() | Return call stack as string |
caller($level) | Get caller function name |
warn($msg) | Print warning to stderr |
die($msg) | Print error and exit |