Debugging

Tools for inspecting variables, tracing execution, and profiling performance.

Inspecting Variables

core::dumper() - Pretty Print Any Value

The core::dumper() function returns a human-readable string representation of any value — pass it to say() (or warn()) to print it. (The bare spellings dumper, refcount, stacktrace, ... remain as legacy aliases.)

my hash %user = {
    "name" => "Alice",
    "age" => 30,
    "tags" => ["admin", "developer"]
};

say(core::dumper(\%user));
# Output:
# {
#   "name" => "Alice",
#   "age" => 30,
#   "tags" => [
#     "admin",
#     "developer"
#   ]
# }

Works with all types:

say(core::dumper(42));              # 42
say(core::dumper("hello"));         # "hello"
say(core::dumper([1, 2, 3]));       # [1, 2, 3]
say(core::dumper(undef));           # undef

core::dumper_str() - Alias

core::dumper_str() is an alias for core::dumper() — both return the dump as a string:

my str $debug = core::dumper_str(\%data);
Log::debug("Request data: " . $debug);

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]));       # "ref" (all references report "ref")
say(typeof({"a" => 1}));   # "ref"
say(typeof(\$x));          # "ref"
say(typeof(undef));        # "undef"

core::refcount() - Check Reference Count

Useful for debugging memory issues:

my array @arr = [1, 2, 3];
say(core::refcount(\@arr));  # 1

my scalar $ref = \@arr;
say(core::refcount(\@arr));  # 2

Stack Traces

Automatic Stack Traces on Uncaught Exceptions

When an exception goes uncaught, Strada automatically prints a stack trace:

func deep_function() void {
    throw "something went wrong";
}

func middle_function() void {
    deep_function();
}

func main() int {
    middle_function();
    return 0;
}

# Output:
# Uncaught exception: something went wrong
# Stack trace:
#   at deep_function (test.strada)
#   at middle_function (test.strada)
#   at main (test.strada)

core::stack_trace() - Get Stack as String

Returns the current call stack as a string for debugging or logging:

func debug_location() void {
    my str $trace = core::stack_trace();
    say("Current location:\n" . $trace);
}

try {
    risky_operation();
} catch ($e) {
    my str $trace = core::stack_trace();
    log_error("Error: " . $e . "\nStack:\n" . $trace);
}

caller() - Get Caller Information

core::caller([level]) returns a hash describing a calling frame, with keys "function", "file", and "line". Level 0 is the immediate caller, 1 is the caller's caller:

func log_message(str $msg) void {
    my str $from = core::caller(0)->{"function"};  # Who called me?
    say("[" . $from . "] " . $msg);
}

func process_data() void {
    log_message("Processing started");
    # Output: [process_data] Processing started
}

Stack levels:

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.

Function Profiling (-p)

Compile with -p. The program prints a per-function timing report (call counts and self/total time) to stderr when it exits — no code changes needed:

./strada -p myprogram.strada
./myprogram   # prints the function profile on exit

Line-Level Profiling (--full-profile)

For line-by-line profiling, compile with --full-profile (writes strada-prof.out on exit), then render a report:

./strada --full-profile myprogram.strada && ./myprogram
strada-proftext strada-prof.out          # text report
strada-profhtml strada-prof.out profhtml/ # HTML report

To profile only a region programmatically, bracket it with the line-profiler API:

core::full_profile_start("region.prof");
# ... code to profile ...
core::full_profile_stop();

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

Line-Level Profiling (Full Profile)

Strada includes a comprehensive line-level profiler (similar to Perl's Devel::NYTProf) for detailed performance analysis.

Quick Start

# Compile with full profiling instrumentation
./strada --full-profile myprogram.strada

# Run the program (writes strada-prof.out on exit)
./myprogram

# Generate a text report
strada-proftext strada-prof.out

# Generate an HTML report with sortable tables and heat-colored source
strada-profhtml strada-prof.out profhtml/
open profhtml/index.html

Difference from -p / --profile

Flag Granularity Output Use Case
-p, --profileFunction-levelPrinted to stderr at exitQuick overview of hot functions
--full-profileLine-levelBinary file (strada-prof.out)Detailed analysis with report tools

Report Tools

strada-proftext generates a text report to stdout:

strada-proftext strada-prof.out           # Full report (functions + lines)
strada-proftext -f strada-prof.out        # Functions only
strada-proftext -l strada-prof.out        # Lines only
strada-proftext --top 20 strada-prof.out  # Show top 20 entries

strada-profhtml generates an HTML report directory:

strada-profhtml strada-prof.out              # Output to profhtml/ (default)
strada-profhtml strada-prof.out my-report/   # Custom output directory

Programmatic API

Enable and disable profiling at runtime for targeted analysis:

# Start profiling mid-program with a custom output file
core::full_profile_start("hotpath.prof");

# ... code to profile ...

# Stop profiling and flush data
core::full_profile_stop();

Memory Profiling

Track memory allocations by type:

func main() int {
    core::memprof_enable();

    # Your code here...
    process_data();

    core::memprof_report();  # Print memory stats
    return 0;
}

Memory Profiler Functions

Function Description
core::memprof_enable()Start tracking allocations
core::memprof_disable()Stop tracking allocations
core::memprof_report()Print allocation statistics
core::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 = core::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 . ":");
        core::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
GDB Tip Strada values are 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
core::dumper($val)Pretty-print value to stderr
core::dumper_str($val)Return dump as string
dump($val, $indent)Dump with indent level
typeof($val)Get type name as string
core::refcount($ref)Get reference count
core::stacktrace()Print call stack
core::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
core::full_profile_start($file)Start line-level profiling to file
core::full_profile_stop()Stop profiling and flush data