Nasal library/debug

From FlightGear wiki
Jump to navigation Jump to search

This page contains documentation for the debug namespace in Nasal. This namespace provides various useful APIs for debugging Nasal code. The debug namespace is sourced from fgdata/Nasal/debug.nas.

Functions

attributes()

debug.attributes(p[, verbose[, color]]);

Returns a string showing the attributes of the node, in the form (type[, attr[, Lnum[, #refs]]). See the table below for explanation.

Data Meaning
type Type of node as returned by props.Node.getType() .
attr Various attribute flags, see props.Node.getAttribute() . "r" = read protected, "w" = write protected, "R" = trace read, "W" = trace write, "A" = archive, "U" = userarchive, "P" = preserve, and "T" = tied.
num Number of listeners, if any.
refs This argument will be shown if verbose is true. Tells the number of references to the node. Note that all nodes have two references by default, but this tells the number of extra references.
p
Mandatory props.Node object.
verbose
Optional bool specifying whether to show the number of times the node is referenced. Defaults to 1 (true).
color
Optional bool specifying whether to output the string with an ANSI color code This is a link to a Wikipedia article. Defaults to nil.

Examples

var node = props.globals.getNode("/sim/time/gmt");
print(debug.attributes(node)); # prints "(STRING, AT, #1)" - string, archive, tied, 1 extra ref
var node = props.Node.new();
node.setIntValue(12);
node.setAttribute(17);
print(debug.attributes(node)); # prints "(INT, wR)" - integer, write protected, trace read
var node = props.globals.getNode("/sim/signals/fdm-initialized");
print(debug.attributes(node)); # prints "(BOOL, L6, #16)" - bool, 6 listeners, 16 extra refs

backtrace()

debug.backtrace([desc]);

When called, this function prints the backtrace This is a link to a Wikipedia article, also printing the local variables at each level.

desc
Optional extra description to add.

Examples

var myFunc = func(a){
    multiply(a, 2);
}
var multiply = func(x, y){
    debug.backtrace();
}
myFunc(2);
var myFunc = func(a){
    multiply(a, 2);
}
var multiply = func(x, y){
    debug.backtrace("multiply() function");
}
myFunc(2);

bt()

debug.bt([desc]);

Shortcut for backtrace() . See doc there

benchmark()

debug.benchmark(label, fn[, repeat[, output]]);

Analyses the amount of time a function takes to run. If repeat is given and output is not, the time taken to repeat the function the given amount off times will be printed. If both are given, the return value of each repeat of the function will be returned in a vector output.

label
Label to add to the output.
fn
Function to call.
repeat
Optional integer specifying how many times the function is to be run. If not given, the function will be run once.
output
Optional vector that will be returned with the return value of each function.

Examples

var test = func(){
    var children = props.globals.getNode("sim").getChildren();
    var vec = [];
    foreach(var child; children){
        append(vec, child.getChildren());
    }
    return vec;
}
var result = debug.benchmark("test()", test);
debug.dump(result);
var test = func(){
    var children = props.globals.getNode("sim").getChildren();
    var vec = [];
    foreach(var child; children){
        append(vec, child.getChildren());
    }
    return vec;
}
debug.benchmark("test()", test, 10);
var test = func(){
    var children = props.globals.getNode("sim").getChildren();
    var vec = [];
    foreach(var child; children){
        append(vec, child.getChildren());
    }
    return vec;
}
var result = debug.benchmark("test()", test, 10, []); # this example may cause a small pause
debug.dump(result);

benchmark_time()

debug.benchmark_time(fn[, repeat[, output]]);

Behaves in exactly the same way as benchmark() , but returns the amount of time taken, rather than printing it out.

fn
Function to call.
repeat
Optional integer specifying how many times the function is to be run. If not given, the function will be run once.
output
Optional vector that will be returned with the return value of each function.

Examples

var test = func(){
    var children = props.globals.getNode("sim").getChildren();
    var vec = [];
    foreach(var child; children){
        append(vec, child.getChildren());
    }
    return vec;
}
print(debug.benchmark_time(test));
var test = func(){
    var children = props.globals.getNode("sim").getChildren();
    var vec = [];
    foreach(var child; children){
        append(vec, child.getChildren());
    }
    return vec;
}
print(debug.benchmark_time(test, 10));
var test = func(){
    return getprop("/sim/time/gmt");
}
print(debug.benchmark_time(test, 1000, var res = []));
debug.dump(res);

dump()

debug.dump([arg[, arg[, ...]]]);

Dumps the given arguments in the console. If no arguments of given, the local namespace will be dumped.

arg
Anything. Literally, anything. There may be as may arguments as you like. If more than one argument is given, each will be dumped separately with an index.

Examples

var x = nil;
debug.dump(); # prints "{ x: nil, arg: [] }"
debug.dump("Hello, World!"); # 'Hello, World!'
debug.dump(1234); # 1234
debug.dump(nil); # nil
debug.dump(["a", "b", "c"]); # ['a', 'b', 'c']
debug.dump({ a: 1, b: 2, c: 3 }); # { a: 1, b: 2, c: 3 }
debug.dump(props.Node.new()); # < = nil (NONE)>
debug.dump(airportinfo()); # <airport> 
debug.dump(func(a){ print(a); }); # <func>
debug.dump( # dump, showing the index of each argument
    "Hello, World!",
    1234,
    nil,
    ["a", "b", "c"],
    { a: 1, b: 2, c: 3 },
    props.Node.new(),
    airportinfo(),
    func(a){ print(a); },
);

isnan()

debug.isnan(num);

Checks whether a number actually is a valid number and returns 0 (false) if it is and 1 (true) if it is not.

num
Number to check.

Examples

print(debug.isnan(1.234)); # prints "0" (1.234 is valid)
print(debug.isnan(1/0)); # prints "1" (division by 0 is undefined)

local()

debug.local([frame]);

Prints and also returns a hash containing all the local variables.

frame
Optional integer specifying the frame. Corresponds to the argument given to caller() .

Examples

var n = 12;
debug.local(); # prints "{ n: 12, arg: [] }"
var sqr = func(a){
    debug.local(); # prints "{ a: 16 }"
    debug.local(1); # prints "{ sqr: <func>, arg: [] }"
    return a * a;
}
print(sqr(16));

print_rank()

debug.print_rank(label, list, names);

Formats and prints results from rank() . It will show them in order of fastest to slowest, the time taken in milliseconds, and what percentage of time each took relative to the slowest.

label
Label to add to the results header.
list
Results vector from rank() .
names
Either a vector or hash that will supply the names of the functions. If a hash, it must have the structure of { name: func, ... }. If a vector, its structure can be either [[name, func], ...] or [[func, name], ...]

Example

var getElev = func(){
    var pos = geo.aircraft_position();
    return geo.elevation(pos.lat(), pos.lon());
}
var getTime = func(){
    return getprop("/sim/time/gmt");
}
var list = [getElev, getTime];
var result = debug.rank(list, 1000);
var names = [["getElev", getElev], ["getTime", getTime]];
#var names = [[getElev, "getElev"], [getTime, "getTime"]]; # other option
#var names = { # third option
#    "getElev": getElev,
#    "getTime": getTime
#};
debug.print_rank("getElev() and getTime()", result, names);

printerror()

debug.printerror(err);

Prints out errors from call() .

err
Error vector from call() .

Example

call(func {
    print(math.ip); # should be math.pi, not math.ip
}, [], nil, nil, var err = []);
debug.printerror(err); # prints "no such member" error
print("Hello again"); # this will still print

propify()

debug.propify(p[, create]);

Attempts to turn its argument into a props.Node object and return it. If it can't, it will return nil.

p
Thing to turn into a node. May be a props.Node object, a prop ghost, or a string pointing to a property.
create
If the above argument is a string, this specifies whether to create the property. Defaults to 0 (false).

Examples

var p = canvas._newCanvasGhost()._node_ghost;
debug.dump(debug.propify(p));
var p = "/sim/time/gmt";
debug.dump(debug.propify(p));
var p = "/demo";
debug.dump(debug.propify(p, 1));
var p = func(){ print("Hello, World!"); }
debug.dump(debug.propify(p)); # prints "nil"

proptrace()

debug.proptrace([root[, frames]]);

Traces changes to a given part of the property tree. It flags up when a child node is added, the value of a node is set, and a node is deleted.

root
Optional props.Node object, a prop ghost, or a string, all pointing to a property. Defaults to the root of the property tree.
frames
Optional number of frames to trace for. Defaults to 2.

Example

var n = props.globals.getNode("/demo", 1);
debug.proptrace("/demo", 10);
var c = n.addChild("foo")
c.setIntValue(12)
c.remove();

rank()

debug.rank(list[, repeat]);

Benchmarks a given list of functions and returns them in a vector with them sorted from fastest to slowest. The result vector will be in the form of [[func, time], ...]. This result vector can be processed by print_rank() .

list
Vector containing the functions to benchmark() .
repeat
Number of times to repeat each function.

Example

var getElev = func(){
    var pos = geo.aircraft_position();
    return geo.elevation(pos.lat(), pos.lon());
}
var getTime = func(){
    return getprop("/sim/time/gmt");
}
var list = [getElev, getTime];
var result = debug.rank(list, 1000); # repeat 1,000 times
debug.dump(result); # getTime() will be first

string()

debug.string(o[, color]);

Converts its argument to a string and returns it.

o
Thing to return as a string.
color
Optional bool specifying whether to output the string with an ANSI color code This is a link to a Wikipedia article. Defaults to nil.

Example

print(debug.string(nil)); # prints "nil"
print(debug.string(1.25)); # prints "1.25"
print(debug.string("Hello, World!")); # prints 'Hello, World!'
print(debug.string(["a", "b", "c"])); # prints "['a', 'b', 'c']"
print(debug.string({ "a": 1, "b": 2, "c": 3 })); # prints "{ a: 1, c: 3, b: 2 }"
print(debug.string(props.globals.getNode("/sim/time/gmt"))); # prints "</sim/time/gmt = '2016-12-04T18:58:18' (STRING, AT, #1)>" (value may be different)
print(debug.string(airportinfo())); # prints "<airport>"
print(debug.string(func(){})); # prints "<func>"

tree()

debug.tree([n[, graph]);

Dumps all the nodes under a given property node into the console.

n
Optional node to dump from. May be a props ghost, string, or props.Node object. If not given, the default is the entire Property Tree.
graph
Optional boolean specifying whether to graph the properties in "flat" mode or using spaces. Defaults to 1 (use spaces).

Examples

debug.tree("/position");
debug.tree("/position", 0);

warn()

debug.warn(msg[, level]);

Similar to die() , but the execution of the code does not stop.

msg
Message to print
level
Optional number of calling levels to omit

Examples

var check = func(pressure){
    if (pressure > 120){
        debug.warn("Pressure above 120 may cause system failure");
    }
}
check(130);
var A = func(a){
    B(a);
}
var B = func(b){
    if (b > 120){
        debug.warn(b ~ " is not within limits", 2);
    }
}
A(130);

Class Probe (available since 2019.1, upgraded 2020.1)

The probe class can help to finde hot spots in Nasal code by collecting statistics. It is controlled via the property tree, so you can use it via the property browser. Data can be viewed / modified in the prop tree /_debug/nas/probe-<myLabel>/*

Note  New in 2020.1:

Tracing support allows you to see the backtrace in the property tree after a hit. You can see the filenames and line numbers an how often the were hit.

new()

debug.Probe.new(label, [class = "probe"]);

create a debug.Probe object

label
A string to name the probe, used in property path and in output.
class
Used to identify derived classes (see Breakpoint below), used in prop. path

Example

var myProbe = debug.Probe.new("myLabel");

enable()

myProbe.enable();

enable counting (and record start time

disable()

myProbe.disable();

Disable counting, record stop time and generate stats.

getHits()

myProbe.getHits();

Get total number of hits.

addCounter()

var id = myProbe.addCounter();

Creates another counter, selectable as hit(id)

hit()

myProbe.hit([counter_id=0][,callback = nil]);

Put this at the place in your code where you want to do the trace.

counter_id
Number of counter as returned by addCounter(), defaults to 0
callback
Optional: on hit call callback(hit_count);

reset()

myProbe.reset();

Reset counter to zero and start time current time.

setTimeout(seconds)

myProbe.setTimeout(seconds);

Set timeout. Next hit() after timeout will disable()

enableTracing()

myProbe.enableTracing();

Enable tracing.

disableTracing()

myProbe.disableTracing();

Disable tracing.

Class Breakpoint (available since 2019.1, upgraded 2020.1)

The breakpoint class is for selective backtracing. It is derived from the debug.Probe class (see above) and thus supports the same methods unless overridden (see below). Putting a debug.backtrace() into some nasal code could flood the console / log especially if you do not (yet) know where that code is used. Using a breakpoint gives you better control when to do backtraces and how often via the property browser at runtime. You have to give "tokens" to you breakpoint which are consumed on each hit of the breakpoint, one per trace. No more tokens, no more traces done until you give more tokens.

For example, if you wish to trace some API call, you can insert a breakpoint into the API code at the point of interest and monitor the number of hits in the property browser.

After FG has started, you can open a property browser and go to /_debug/nas/bp-myLabel

Set tokens to a positive integer to start tracing. Each hit will consume one token and perform one backtrace. To start tracing again, just set tokens again to the number of traces you wish to perform.

Note  Tracing support was added in version 2020.1, see Probe class above

new()

Breakpoint.new(label [,dump_locals = 1][,skip_level=0]);

create a breakpoint object

label
A string to name the breakpoint, used in property path and in output.
dump_locals
0 or 1, passed to backtrace to control printing of namespaces (variables). Warning: dumping big namespaces can cause a stack overflow and crash the nasal script.
skip_level
passed to debug.backtrace to skip the first n levels in the bt.

Example

var myBP = debug.Breakpoint.new("myLabel", 0);

enable()

myBP.enable([tokens = 1]);

Start tracing

tokens
Number of traces allowed; each hit will consume one token, tokens < 1 means no more traces. Tokens can be also set via the property browser at runtime.

hit()

myBP.hit([callback]);

Put this at the place in your code where you want to do the trace.

callback
Optional: function to call instread of debug.backtrace. It will be called as callback(number_of_hits, remaining_tokens);