Upgrade to Byteman (debug/verbose) tracing [BYTEMAN-294]
adinn Oct 2, 2015 6:18 AMIntro
I have had a few thoughts about what is needed to improve the behaviour of Byteman debug and verbose mode tracing (the task covered by BYTEMAN-249) which I want to sumamrize in this discussion. None of thsi is cast in stone -- it's just ideas.
There are two aspects to this when it comes to designing an improved tracing mechanism:
- how to provide a better way for users to select which trace messages they see
- how to implement that selective tracing.
The 3rd stage of the task will then involve employing the new APIs, firstly to change over all existing trace statements to use it and then to reorganise and extend the existing trace to improve the quality of the trace messgages that are generated and ensure that they actually conform to the user's selection criteria.
A key goal of the design task is to retain some sort fo compatibility with the existing configuration model i.e. to continue to support debug and verbose tracing levels using some simple switch which enable either debug trace on its own or both debug and verbose trace.
That is not to imply that all trace messages currently output in accordance with those two switches should continue to be output nor that new messages should not be included. The mechanism shoud be retained but the trace which results will almost certainly require dropping some messages genertaed when debug or verbose is set and adding some other new ones when verbose is set. For example, configuring debug is only supposed to enable messages generated when a rule includes a call to the Helper debug method but, at present, the agent outputs a few messages of its own when debug is enabled. Similarly, the agent does not always print trace when parse, typecheck or compileor failures occur even when verbose is set; these really should be notified (with stack trace) without the need for the user to employ any more noissy trace level.
What I am proposing is that we keep the existing 2 levels of trace and supplement them with a 3rd level which can be configured to be selective. It should work roughly as follows:
debug
verbose
noisy
debug trace
debug trace is enabled by setting org.jboss.byteman.debug. Setting this property enables output of debug statements from method Helper.debug It does not enable any other trace output. Helper.debug is responsible for checking whether to print any output by calling Transformer.isDebug().
Currently Helper debug() prints directly to System.out. It shoud be modified so that the print operation is performed by calling traceln("dbg", "DBG: " + message). This variant of method Helper.traceln looks for for a trace stream (a PrintStream instance in the trace stream hash table) identified by the key supplied in the fist argument and uses it do the actual printing. If no such stream exists it uses System.out.
This will allows all debug messages to be redirected simply by adding an entry to the trace stream hash map with key "dbg". By default System.out will be the target for this trace stream but we can also allow it to be configured to System.error or an actual file using System property org.jboss.byteman.debug.tracestream. If this is set to "out" or "err" then the trace stream "dbg" will be set to idenitfy System.out or System.err. If set to any other string that will be used as a file name to which trace will be redirected (Assuming the file can be opened).
verbose trace
verbose trace is enabled by setting org.jboss.byteman.verbose. Setting this property should continue to enable output of all existing verbose trace statements which are currently implemented by testing Transformer.isVerbose() and then calling System.out.println. The use of isVerbose() to guard the calls to print the messages should still be retained. However, the actual printing should be implemented by planting a call to Helper.verbose(message). This shoud redirect to call Helper.traceln("vrb", "VRB: " + message). As with debug the trace stream "vrb" wil idenitfy System.out by default but can be redirected by calling traceOpen. We can also allow it to be configured using system property org.jboss.byteman.verbose.tracestream.
noisy trace
noisy trace is enabled by setting org.jboss.byteman.noisy. This switches on highly detailed tracing of operation of a range of different Byteman operations. Tracing can be filtered by selecting trace messages which fall into one or more trace categories. Enabling noisy trace also enables verbose and debug trace.
Setting the noisy property with no other configuration will enable trace for a default set of trace categories. Other categories may be selected by setting org.jboss.byteman.noisy.categories to a string value idenitifyng the desired trace categories. An empty string will configure the default set. A string containing one or more of the following categories (using | as a separator) will enable trace output for any trace operation which is relevant to that category:
PARSE|TYPE|INTERP|COMP|MATCH|INJECT|CFG|CFGPART|GEN|GENPART|HELPER||LOAD|LISTEN|INVALID|WTF|???
The default set of categories will include LOAD|LISTEN|MATCH|PARSE|TYPE|HELPER|???
n.b. these are just rough guesses at what sort of categories are needed. It may be that they need revising and/or supplementing with other categories. Also, note that the trace categories are not meant to be distinct, non-overlapping groups. If a message is relevant to more than one trace category then it shoudl be output whenever any of the relevant categories is enabled.
At the point where noisy trace is generated the trace operation will be guarded by an if whose condition is a call to Transformer.isNoisy(int). The argument will be constructed by ORing in flags which identify the trace categories to which the trace message belongs. The same flags should be passed in a call to Helper.noisyln which is used to print the trace message e.g.
if (isNoisy(Trace.HELPER|Trace.INVALID)) {
Helper.noisy(Trace.HELPER|Trace.INVALID,
"Helper class has lifecycle method with wrong signature " +
helperClassName + "." +
helperMethodName + helperMethodSignature);
}
Helper.isNoisy will return true if any of the trace categories identified in the input mask are enabled. If none of them are enabled it wil lreturn falseHelper.noisy(inputMask, message) will redirect to a call to Helper.traceln("nzy", "NZY: " + flagString(inputMask) + message). The first argument directs the trace to the "nzy" trace stream. This will default to System.out but can be reset by means of a call to traceOpen. We can also allow it to be configured using system property org.jboss.byteman.noisy.tracestream. flagString is method which constructs a string containing a label for each trace categories mentioned in inputMask which is actually enabled. For exmaple, if inputMask is Trace.HELPER|Trace.INVALID and the HELPER category has been enabled but the INVALID category has not been enabled the flagmask will simply be "HELPER: ". If both flags are enabled then it will be "HELPER: INVALID: ".
Trace messages at the noisy level will mostly need to be added as new. One or two of the existing verbose trace messages and information generated via other trace mechanisms (such as dumping of CFG info or generated bytecode) may also need to be configured and generated under control of the noisy trace mechanism.
What next
So, I think what is needed now is this
- check that this will actually operate in a useful way
- check that my vague guesses about how to implement this via Helper.traceln and using trace streams actually makes sense
- implement the relevant behaviour in class Helper
- modify existing System.out.println statements guarded by isDebug or isVerbose to use the new Helper.verbose
n.b. Helper.debug is only meant to be called from user supplied rule code so any calls internal to Byteman need to use Helper.verbose() - idenitfy any verbose messages which really ought to be output as noisy messages and modify the trace guards/calls to use the isNoisy() & noisy()
- convert the trace output by the CFG classes to use noisy trace.
This is currently controlled by setting the system properties o.j.b.dump.cfg and o.j.b.dump.cfg.partial so we need to make sure that when these properties are set we enable noisy trace for (at least) the CFG/CFGPART trace classes) - see what we can do to configure bytecode dumping using the noisy trace settings (DUMP and DUMPPART)
obviously we won't use Helper.noisy() to dump bytecode in classfile format but we can still use isNoisy(DUMP|DUMPPART) and isNoisy(DUMPPART) to test whether or not we should dump bytecode.
This is controlled by setting the system properties o.j.b.dump.generated.classes and o.j.b.dump.generated.classes.intermediate so we need to make sure that when these properties are set we enable noisy trace for (at least) the GEN|GENPART trace classes)