Bela Ban wrote:
I frequently see byteman drop rules when there is an error in them. I'd prefer this to actually throw an exception, so that I know a given rule is not executed, rather than silently dropping the rule. E.g.
is_address=arg instanceof Address; ## incorrect, instanceof is not supported
The bind clause setting is_address will cause the rule to be dropped.
Yes, I know, I can probably get a TRACE statement somewhere using a system variable (-Dorg.jboss.byteman.verbose etc), but I think this is a critical error and should cause an exception that I see, so I can correct that.
Since byteman parses expressions, it shouldn't be too difficult to spit out the parse error in the form of an exception, should it ?
That last part is easy because it is already done :-) Parse errors, type errors and execute errors generate Byteman exceptions which include details of the error in question (ParseException, TypeException and ExecuteException, respectively, all of which are subclasses of RuntimeException). Execution exceptions always propagate up out of the trigger method -- that's not necessarily very useful given that normally it either just kills the thread with an uncaught exception or else gets caught by a catch-all handler which then attempts to carry on basedon a wrong assumption abnout what could possibly have gonw wrong. Neither of these is guaranteed to stop a test and both are usually dreadful outcomes whenf you use Byteman in a running app. However, there seems to be little else that can be done beyond i) forcing the JVM to nose dive -- the nuclear option which probably will be just as unlikley to satisfy the majority of needs -- or ii) trapping the exception, disabling the rule and carrying on, quite possibly with a half-completed rule action. Note that this explains why ExecuteException is a RuntimeException. It has to be throwable from an arbitrary trigger method. Of course, that also implies it can be thrown in contexts which may take an arbitrary uninformed action in response and,even in the best case, don't do anything beyond logging and rethrowing the error. All options here are bad.
Punting the other two exceptions out of the transformer suffers from these same issues but is fraught with yet more ambiguity. First, there is the question of when the error gets thrown. Parse errors only happen during injection which is scheduled by the JVM transformer either i) following a request by the agent thread or ii) when a potential target class for the rule is loaded. In the latter case the injection may occur in a different thread to the load request (actually performing the transform in a different thread is possible in the former case and, indeed, the transform may be asynchronous with the request but I don't think that happens in OpenJDK/Oracle's JVM). Type errors only occur the first time control reaches the injection point in the target method (the rule is locked during type checking so only one thread may throw an error and disable the rule). Of course, a rule may never be triggered so may never generate a type error even if it is wrong (hence why I provide an offline type checker).
Secondly, there is the usual trade-off between stopping at the first error and carrying on to reveal other errors. Throwing an exception during parsing or type checking may stop other rules from being parsed or type checked so you only find your errors one at a time. That's ok with simple tests but is a real issue if your test takes a lto fo time to set up (hence why I provide an offline type checker).
Thirdly, if you don't throw an exception during parsing and type checking then this means you can load and unload rules into a running (possibly live) application without breaking the app if you accidenlty install a rule with a typo or some other silly error. All errors are recorded and can be reviewed with the bmsubmit script so they are not least even if they do not appear in console output as happens when verbose logging is off or, even, when it is enabled but output has not yet been written to disk (the usual annoying behaviour when a test hangs under JUnit/TestNG test). Of course, it is better to be careful not to make such mistakes although they are bound to happen sometimes (hence why I provide an offline type checker).
So, in summary I don't believe propagating parse and type errors on the assumption that your code or the JVM will display them better than the current verbose trace mechanism is actually the right solution here. If you want to see errors in System.out then for the present you can switch on verbose mode and errors will be printed as they arise. That works and is guaranteed to make every error (and warning -- did I enntion TypeWarningException?) actually appear in System.out. If you dislike exactly how this works (e.g. you have a problem with the level of control over error printing vs other tracing, the directing of errors to System.out vs some other error stream or even the fact that error printing is off rather than on by default) then I'll be happy to think about providing a better error/tracing system in Byteman (n.b. Scott Stark already had a look at this and we drew up some tentative plans but never got to a satisfactory spec). Your problem appears to me to be visibility of Byteman errors vs other Byteman status information, not whether exceptions get pushed out into arbitrary bits of your app or the runtime. So, let's look at that problem.
thanks for the reply. I'm sure I saw incorrect Java code, e.g. a missing semicolon terminating a string drop a rule and *not* throw a parse exception. I'll make sure to report this when it happens again.
But perhaps this was an Intellij/TestNG issue. I've had other issues before, e.g. the program running in the debugger not stopping at breakpoints when running with byteman, especially when @Test(invocationCount=X) was used. Perhaps the code didn't get reloaded, so the old (correct) code without the typo was run...