240 lines
6.7 KiB
Text
240 lines
6.7 KiB
Text
# debug.nas -- debugging helpers
|
|
#------------------------------------------------------------------------------
|
|
#
|
|
# debug.color(<enabled>); ... turns terminal colors on (1) or off (0)
|
|
#
|
|
# debug.dump(<variable>) ... dumps contents of variable to terminal;
|
|
# abbreviation for print(debug.string(v))
|
|
#
|
|
# debug.local([<frame:int>]) ... dump local variables of current
|
|
# or given frame
|
|
#
|
|
# debug.backtrace([<comment:string>]} ... writes backtrace with local variables
|
|
# (similar to gdb's "bt full)
|
|
#
|
|
# debug.tree([<property> [, <mode>]) ... dump property tree under property path
|
|
# or props.Node hash (default: root). If
|
|
# <mode> is unset or 0, use flat mode
|
|
# (similar to props.dump()), otherwise
|
|
# use space indentation
|
|
#
|
|
# debug.exit() ... exits fgfs
|
|
#
|
|
# debug.bt ... abbreviation for debug.backtrace
|
|
#
|
|
# debug.string(<variable>) ... returns contents of variable as string
|
|
#
|
|
# debug.load_xml_nasal(<file>) ... load and run XML embedded Nasal
|
|
|
|
var _c = func {}
|
|
|
|
var color = func(enabled) {
|
|
if (enabled) {
|
|
_c = func(color, s) { "\x1b[" ~ color ~ "m" ~ s ~ "\x1b[m" }
|
|
} else {
|
|
_c = func(dummy, s) { s }
|
|
}
|
|
}
|
|
|
|
|
|
# for color codes see $ man console_codes
|
|
#
|
|
var _title = func(s) { _c("33;42;1", s) } # backtrace header
|
|
var _section = func(s) { _c("37;41;1", s) } # backtrace frame
|
|
var _error = func(s) { _c("31;1", s) } # internal errors
|
|
|
|
var _nil = func(s) { _c("32", s) } # nil
|
|
var _string = func(s) { _c("31", s) } # "foo"
|
|
var _num = func(s) { _c("31", s) } # 0.0
|
|
var _bracket = func(s) { _c("", s) } # [ ]
|
|
var _brace = func(s) { _c("", s) } # { }
|
|
var _angle = func(s) { _c("", s) } # < >
|
|
var _vartype = func(s) { _c("33", s) } # func ghost
|
|
var _proptype = func(s) { _c("34", s) } # BOOL INT LONG DOUBLE ...
|
|
var _path = func(s) { _c("36", s) } # /some/property/path
|
|
var _internal = func(s) { _c("35", s) } # me parents
|
|
var _varname = func(s) { _c("1", s) } # variable_name
|
|
|
|
|
|
var tree = func(p = "", graph = 1) {
|
|
var n = isa(p, props.Node) ? p : props.globals.getNode(p, 0);
|
|
if (n == nil)
|
|
dump(n);
|
|
else
|
|
_tree(n, graph);
|
|
}
|
|
|
|
|
|
var _tree = func(n, graph = 1, prefix = "", level = 0) {
|
|
var path = n.getPath();
|
|
var children = n.getChildren();
|
|
var s = "";
|
|
|
|
if (graph) {
|
|
s = prefix ~ n.getName();
|
|
var index = n.getIndex();
|
|
if (index)
|
|
s ~= "[" ~ index ~ "]";
|
|
} else {
|
|
s = n.getPath();
|
|
}
|
|
|
|
if (size(children)) {
|
|
s ~= "/";
|
|
if (n.getType() != "NONE")
|
|
s ~= " = " ~ string(n.getValue()) ~ " " ~ _attrib(n)
|
|
~ " " ~ _section(" PARENT-VALUE ");
|
|
} else {
|
|
s ~= " = " ~ string(n.getValue()) ~ " " ~ _attrib(n);
|
|
}
|
|
print(s);
|
|
|
|
if (n.getType() != "ALIAS")
|
|
forindex (var i; children)
|
|
_tree(children[i], graph, prefix ~ ". ", level + 1);
|
|
}
|
|
|
|
|
|
var _attrib = func(p) {
|
|
var r = p.getAttribute("read") ? "" : "r";
|
|
var w = p.getAttribute("write") ? "" : "w";
|
|
var R = p.getAttribute("trace-read") ? "R" : "";
|
|
var W = p.getAttribute("trace-write") ? "W" : "";
|
|
var A = p.getAttribute("archive") ? "A" : "";
|
|
var U = p.getAttribute("userarchive") ? "U" : "";
|
|
var T = p.getAttribute("tied") ? "T" : "";
|
|
var attr = r ~ w ~ R ~ W ~ A ~ U ~ T;
|
|
var type = "(" ~ _proptype(p.getType());
|
|
if (size(attr))
|
|
type ~= ", " ~ attr;
|
|
if (var l = p.getAttribute("listeners"))
|
|
type ~= ", L" ~ l;
|
|
return type ~ ")";
|
|
}
|
|
|
|
|
|
var _dump_prop = func(p) {
|
|
_path(p.getPath()) ~ " = " ~ string(p.getValue()) ~ " " ~ _attrib(p);
|
|
}
|
|
|
|
|
|
var _dump_var = func(v) {
|
|
if (v == "me" or v == "parents")
|
|
return _internal(v);
|
|
else
|
|
return _varname(v);
|
|
}
|
|
|
|
|
|
var string = func(o) {
|
|
var t = typeof(o);
|
|
if (t == "nil") {
|
|
return _nil("nil");
|
|
} elsif (t == "scalar") {
|
|
return num(o) == nil ? _string('"' ~ o ~ '"') : _num(o);
|
|
} elsif (t == "vector") {
|
|
var s = "";
|
|
forindex (var i; o)
|
|
s ~= (i == 0 ? "" : ", ") ~ string(o[i]);
|
|
|
|
return _bracket("[") ~ " " ~ s ~ " " ~ _bracket("]");
|
|
} elsif (t == "hash") {
|
|
if (contains(o, "parents") and typeof(o.parents) == "vector"
|
|
and size(o.parents) == 1 and o.parents[0] == props.Node) {
|
|
return _angle("<") ~ _dump_prop(o) ~ _angle(">");
|
|
}
|
|
var k = keys(o);
|
|
var s = "";
|
|
forindex (var i; k)
|
|
s ~= (i == 0 ? "" : ", ") ~ _dump_var(k[i]) ~ " : " ~ string(o[k[i]]);
|
|
|
|
return _brace("{") ~ " " ~ s ~ " " ~ _brace("}");
|
|
} else {
|
|
return _angle("<") ~ _vartype(t) ~ _angle(">");
|
|
}
|
|
}
|
|
|
|
|
|
var dump = func { size(arg) ? print(string(arg[0])) : local(1) }
|
|
|
|
|
|
var local = func(frame = 0) {
|
|
var v = caller(frame + 1);
|
|
print(v == nil ? _error("<no such frame>") : string(v[0]));
|
|
return v;
|
|
}
|
|
|
|
|
|
var backtrace = func(desc = nil, l = 0) {
|
|
var v = caller(l);
|
|
if (v == nil)
|
|
return;
|
|
|
|
if (l == 0) {
|
|
var d = desc == nil ? "" : " '" ~ desc ~ "'";
|
|
print("\n" ~ _title("\n### backtrace" ~ d ~ " ###"));
|
|
} else {
|
|
print(_section(sprintf("#%-2d called from %s, line %s:", l - 1, v[2], v[3])));
|
|
dump(v[0]);
|
|
}
|
|
backtrace(nil, l + 1);
|
|
}
|
|
var bt = backtrace;
|
|
|
|
|
|
var exit = func { fgcommand("exit", props.Node.new()) }
|
|
|
|
|
|
if (getprop("/sim/logging/priority") != "alert") {
|
|
_setlistener("/sim/signals/nasal-dir-initialized", func { print(_c("32", "** NASAL initialized **")) });
|
|
_setlistener("/sim/signals/fdm-initialized", func { print(_c("36", "** FDM initialized **")) });
|
|
}
|
|
|
|
|
|
|
|
##
|
|
# Loads embedded Nasal from an XML file into a Nasal namespace.
|
|
# The namespace is by default the file's basename. Optionally,
|
|
# a module can be defined in a <module> entry. The file name
|
|
# doesn't have to carry an *.xml extension. It may even be
|
|
# desirable to use *.nas, so that editors pick up Nasal syntax
|
|
# coloring.
|
|
#
|
|
# Usage: debug.load_xml_nasal(<filename>);
|
|
#
|
|
# Example:
|
|
#
|
|
# debug.load_xml_nasal("Aircraft/mine/test.nas");
|
|
#
|
|
# contents of test.nas:
|
|
#
|
|
# <PropertyList>
|
|
# <module>test</module> <!-- optional -->
|
|
# <script><![CDATA[
|
|
#
|
|
# print("I'm being reloaded.");
|
|
#
|
|
# ]]></script>
|
|
# </PropertyList>
|
|
#
|
|
var load_xml_nasal = func(file) {
|
|
var n = props.globals.getNode("/tmp/nasal", 1);
|
|
n.getNode("filename", 1).setValue(file);
|
|
n.getNode("targetnode", 1).setValue(n.getPath());
|
|
if (n.getNode("module", 0) == nil) {
|
|
var basename = split(".", split("/", file)[-1])[0];
|
|
n.getNode("module", 1).setValue(basename);
|
|
}
|
|
fgcommand("loadxml", n);
|
|
fgcommand("nasal", n);
|
|
}
|
|
|
|
|
|
|
|
_setlistener("/sim/signals/nasal-dir-initialized", func {
|
|
setlistener("/sim/startup/terminal-ansi-colors", func {
|
|
color(cmdarg().getBoolValue());
|
|
}, 1);
|
|
});
|
|
|
|
|