1
0
Fork 0

debug.nas add helpers for adding probes at runtime; add subnode to _debug for hit counters

This commit is contained in:
Henning Stahlke 2020-05-03 12:36:42 +02:00 committed by James Turner
parent 49874ceff3
commit bef5de6e81

View file

@ -66,6 +66,12 @@
# debug.Probe class ... base class to collect stats; details below # debug.Probe class ... base class to collect stats; details below
# debug.Breakpoint class ... conditional backtrace; details below # debug.Breakpoint class ... conditional backtrace; details below
# #
# debug.addProbeToFunc(label, func) ... wraps a function with a probe
# debug.findFunctions(ns, recursive=0) ... find all functions in a hash (namespace)
#
# debug.addProbesToNamespace(ns, label="", recursive=0)
# ... combines findFunctions and addProbeToFunc
#
# CAVE: this file makes extensive use of ANSI color codes. These are # CAVE: this file makes extensive use of ANSI color codes. These are
# interpreted by UNIX shells and MS Windows with ANSI.SYS extension # interpreted by UNIX shells and MS Windows with ANSI.SYS extension
# installed. If the color codes aren't interpreted correctly, then # installed. If the color codes aren't interpreted correctly, then
@ -272,12 +278,15 @@ var local = func(frame = 0) {
return v; return v;
} }
# According to the Nasal design doc funtions do not have unique internal names.
# Nevertheless you can sometimes find a matching symbol, so funcname may help to
# make debug output more helpful, but unfortunately you cannot rely on it.
var funcname = func(f) { var funcname = func(f) {
if (typeof(f) != "func") return ""; if (!isfunc(f)) return "";
var namespace = closure(f); var namespace = closure(f);
foreach (var k; keys(namespace)) { foreach (var k; keys(namespace)) {
if (typeof(namespace[k]) == "func") { if (isfunc(namespace[k])) {
if (namespace[k] == f) if (namespace[k] == f)
return k; return k;
} }
@ -331,7 +340,7 @@ var proptrace = func(root = "/", frames = 2) {
# Executes function fn "repeat" times and prints execution time in seconds. If repeat # Executes function fn "repeat" times and prints execution time in seconds. If repeat
# is an integer and an optional "output" argument is specified, each test's result # is an integer and an optional "output" argument is specified, each test's result
# is appended to that vector, then the vector is returned. If repeat is nil, then # is appended to that vector, then the vector is returned. If repeat is nil, then
# the funciton is run once and the result returned. Otherwise, the result is discarded. # the function is run once and the result returned. Otherwise, the result is discarded.
# Examples: # Examples:
# #
# var test = func { getprop("/sim/aircraft"); } # var test = func { getprop("/sim/aircraft"); }
@ -552,6 +561,9 @@ var Probe = {
append(obj._hitsN, obj.node.addChild("hits")); append(obj._hitsN, obj.node.addChild("hits"));
obj._hitsN[0].setIntValue(0); obj._hitsN[0].setIntValue(0);
# for live monitoring via prop browser, alias all hit counters in one place
props.globals.getNode("/_debug/nas/_stats/"~obj.uid, 1)
.alias(obj._hitsN[0]);
Probe._instances[obj.uid] = obj; Probe._instances[obj.uid] = obj;
return obj; return obj;
}, },
@ -747,12 +759,70 @@ var Breakpoint = {
}, },
}; };
# addProbeToFunc - wrap a function with a debug probe
# label: description, passed to probe
# f: function to wrap with a debug probe (hit counter)
#
# WARNING: call() currently breaks the call stack which affects backtrace and
# the use of caller(i>0). Do not use addProbeToFunc on functions which rely on
# caller (which is probably bad coding style, but allowed).
#
var addProbeToFunc = func (label, f) {
if (!isfunc(f)) {
logprint(DEV_ALERT, "wrapFunc() error: argument is not a function.");
return nil;
}
if (!isstr(label)) {
logprint(DEV_ALERT, "wrapFunc() error: argument is not a string.");
return nil;
}
var __probe = Probe.new(label).enable();
var wrapped = func() {
__probe.hit();
return call(f, arg, me, );
}
#return bind(wrapped, caller(0)[0], caller(1)[1]);
return wrapped;
}
# scan a hash for function references
# ns: the hash to be searched
# recursive: if you want to search sub hashes (e.g. classes), set this to 1
var findFunctions = func (ns, recursive=0) {
var functions = {};
foreach (var key; keys(ns)) {
if (recursive and ishash(ns[key]) and id(ns) != id(ns[key])) {
print(key);
var f = findFunctions(ns[key]);
foreach (var k; keys(f)) {
functions[key~"."~k] = f[k];
}
}
if (isfunc(ns[key])) {
functions[key] = ns[key];
}
}
return functions;
}
# add probes to all functions in a namespace for finding hotspots
# use property browser at runtime to check /_debug/nas/_stats/
# ns: hash
# label: description, passed to probe
# recursive: passed to findFunctions, see above
var addProbesToNamespace = func (ns, label="", recursive=0) {
var funcs = findFunctions(ns);
foreach (var key; keys(funcs)) {
ns[key] = addProbeToFunc(label~"-"~key, funcs[key]);
}
}
# --prop:debug=1 enables debug mode with additional warnings # --prop:debug=1 enables debug mode with additional warnings
# #
_setlistener("sim/signals/nasal-dir-initialized", func { _setlistener("sim/signals/nasal-dir-initialized", func {
# General purpose breakpoint for the lazy ones. # General purpose breakpoint for the lazy ones.
debug.bp = debug.Breakpoint.new("default"); debug.bp = debug.Breakpoint.new("default");
if (!getprop("debug")) if (!getprop("debug"))
return; return;
var writewarn = func(f, p, r) { var writewarn = func(f, p, r) {