80fd0740e5
debug.color(bool) turns terminal colors on/off debug.tree(property="", mode=1); dumps property tree in flat mode (0) or space indented mode (1). Flat tree mode is similar to props.debug(), but colored, and it outputs all attribute flags and the number of attached listeners.
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 = nil;
|
|
|
|
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);
|
|
});
|
|
|
|
|