Ticket UUID: | 1458266 | |||
Title: | cmd execution traces interfere with step execution traces | |||
Type: | Bug | Version: | obsolete: 8.4.13 | |
Submitter: | eriklns | Created on: | 2006-03-25 12:46:34 | |
Subsystem: | 46. Traces | Assigned To: | dgp | |
Priority: | 7 High | Severity: | ||
Status: | Closed | Last Modified: | 2006-04-11 21:38:36 | |
Resolution: | Fixed | Closed By: | dgp | |
Closed on: | 2006-04-11 14:38:36 | |||
Description: |
Execution traces of the enter type interfere with execution traces of the enterstep type. (the leave/leavestep case may behave similarly, but did not check that.) To appreciate the interference: Run1: source the test script below without modification. Run2: uncomment the clock command inside the interpTraceHandler and again source the test script (in a clean interpreter). Comparing the output of the 2 runs (also appended) shows that the stepTrace handler is called at additional deeper levels in run 2 as compared to run 1. It appears that the trigger of a command trace re-enables further step tracing, even while a step trace is in progress. % parray tcl_platform tcl_platform(byteOrder) = littleEndian tcl_platform(machine) = i686 tcl_platform(os) = Linux tcl_platform(osVersion) = 2.4.21-99-athlon tcl_platform(platform) = unix tcl_platform(user) = erik tcl_platform(wordSize) = 4 % info patch 8.4.12 % Erik Leunissen == test script == proc stepTraceHandler {cmdString args} { puts "level [expr {[info level] -1}]: [lindex [split $cmdString] 0] triggered a step trace handler" # clock clicks; # enabling/disabling this command causes a different behaviour of step traces isTracedInside_2 } proc cmdTraceHandler {cmdString args} { puts "level [expr {[info level] -1}]: [lindex [split $cmdString] 0] triggered a cmd trace handler" } proc isTracedInside_1 {} { isTracedInside_2 } proc isTracedInside_2 {} { set x 2 } trace add execution clock enter cmdTraceHandler trace add execution isTracedInside_1 enterstep stepTraceHandler trace add execution isTracedInside_2 enterstep stepTraceHandler isTracedInside_1; # start the execution # End of script Results of the two runs: ======================== Run1: level 1: isTracedInside_2 triggered a step trace handler level 2: set triggered a step trace handler level 2: set triggered a step trace handler Run2: level 1: isTracedInside_2 triggered a step trace handler level 2: clock triggered a cmd trace handler level 3: set triggered a step trace handler level 4: clock triggered a cmd trace handler level 2: set triggered a step trace handler level 3: clock triggered a cmd trace handler level 3: isTracedInside_2 triggered a step trace handler level 4: clock triggered a cmd trace handler level 4: set triggered a step trace handler level 5: clock triggered a cmd trace handler level 2: set triggered a step trace handler level 3: clock triggered a cmd trace handler level 3: isTracedInside_2 triggered a step trace handler level 4: clock triggered a cmd trace handler level 4: set triggered a step trace handler level 5: clock triggered a cmd trace handler == end of message == | |||
User Comments: |
dgp added on 2006-04-11 21:38:36:
Logged In: YES user_id=80530 ok, cmd traces and exec traces ought not interfere with each other, and the test case demos at least one circumstance where they do. the patch corrects that interference, and does not create any apparent new problems or bugs. The substance of the patch is sensible too, performing an explicit save/restore instead of a do/undo type maintenace of a value. On that basis, I'm accepting these patches. However, it sure looks to me like the results of these tests are just plain wrong. I haven't been able to come up with any interpretation of the exec trace documentation that would agree with the results generated. There's more bugs here to find. dgp added on 2006-04-11 21:22:39: File Added - 174199: 1458826-85.patch dgp added on 2006-04-11 21:22:36: Logged In: YES user_id=80530 and here's the corrsponding patch for HEAD (Tcl 8.5a4). dgp added on 2006-04-11 21:20:58: File Deleted - 174189: File Added - 174198: 1458266.patch dgp added on 2006-04-11 21:20:57: Logged In: YES user_id=80530 updated patch includes new test trace-34.6 derived from the submitted test case. dgp added on 2006-04-11 20:56:13: Logged In: YES user_id=80530 Is there a simple explanation why we need two commands with step traces on them to demo this bug? dgp added on 2006-04-11 19:47:39: File Deleted - 172709: File Added - 174189: 1458266.patch dgp added on 2006-04-11 19:47:37: Logged In: YES user_id=80530 Here's a revised patch to account for C (not C++) compilers. eriklns added on 2006-04-02 23:15:00: Logged In: YES user_id=113903 With respect to: "If there are several possible answers, because the docs are not specific enough, then which of those answers would be most useful to you? We can consider just "making" that the correct one." In the case of multiple simultaneous traces, that may interact in rather messy ways, I would suggest a conservative strategy: - start out with a rather restricted but clear and simple definition of the behaviour - leave room for more refined, diverse behaviour in specific cases only after these cases have been specified. Such a limited starting point *could* be: "While any step trace handler is executing, further invocations of Tcl commands should never trigger any step traces again (i.e. regardless the trace that was registered, regardless the command on which traces exist, or the trace handler)." But I would welcome a discussion among a wider audience about the exact formulation of such a limited starting point. Right now I just want to propose a strategy that leaves us more in control of what's right and wrong. eriklns added on 2006-04-02 18:01:25: File Added - 173113: interferenceTest.tcl eriklns added on 2006-04-02 18:01:24: Logged In: YES user_id=113903 OK, then how about me supplying a fully documented test script that you can use as a basis for the test suite? I took the liberty to do that (see uploaded file "interferenceTest.tcl"). The reason that I am particularly careful here, is because we're actually in one of the corner cases that I mentioned in the previous post: the observed bug emerges only when two step traces are registered, both using the same trace handler. The docs are not explicit about that case. Therefore, the test script does a differential diagnostic, i.e. comparing two runs, but not saying anything about the correct behaviour of any of the two runs. It does not test for a predefined "correct" result. If you insist on checking for a "correct" result of a single run, then indeed more reflection is needed. In order to keep the progress with this issue, I post the differential approach and await how useful that is to you. The comments to the test script explain in detail what should happen and what not, and why. You may use the test for its explanation alone, or use it as a basis for other tests; whatever you like. As for the supplied patch: I'm quite sure that I found the location which is responsible for the interference. That in itself is one value of the patch. However, this is the first time that I provided a patch at the C level. I may not have enough overview of the Tcl C library to know about possibly related areas that should change as well (byte compiled scripts are an example, because it is a sort of subsystem that I do not grasp very well). I've used a new libtcl8.4.so (that has my patch applied) in quite some tests. Among these are some that exercise traces. These tests all behaved as expected. Nevertheless, I'm curious whether the patch has any side effects in areas that were overlooked. Regards, Erik Leunissen dgp added on 2006-04-01 23:24:56: Logged In: YES user_id=80530 Somehow I think you've got the wrong impression about my question. As a first step toward fixing the bug, I'm trying to convert your report into additional tests in the test suite. To do that, I need to tell the test suite what correct answers to expect. Then I can use those tests to help me determine whether various changes correct the bug. Since it's your example, I assumed you would have an expected answer in mind and could easily tell me what it is. As you see, the docs are rather complex, and I don't want to put in the effort to decipher them if you've already done it. So, first request is, assuming everything in the documentation were telling the truth, what answer do you expect? If there are several possible answers, because the docs are not specific enough, then which of those answers would be most useful to you? We can consider just "making" that the correct one. If none of the answers consistent with the docs is useful to you, then we can even consider revising things, but that will take some more reflection. Traces are messy in concept (lots of potential re-entrancy to deal with), and much, much more messy in the implementation. You refer to a "design specification" for the trace mechanism. There really ain't one. Not beyond what's stated in either the docs or in TIP 62 (http://tip.tcl.tk/62). Any behavior you want that's consistent with those is possible, and if you can show that what they specify makes no sense, even revising them is possible. eriklns added on 2006-04-01 18:09:46: Logged In: YES user_id=113903 Don, I've been thinking about your question "what should the behaviour of the test script be and why?". I wil get to that matter shortly, but first off, I want to emphasize that (in my view) an answer to that question is irrelevant for the decision as to whether the observed behaviour is faulty. Do you agree on that? As to "what should the behaviour of the test script be and why?: Only if the following statement is true, I can give you an answer: statement ========= "While any step trace handler is executing, further invocations of Tcl commands should never trigger any step traces again (i.e. regardless the command or the trace handler)." So, can you (or another member of the core team) tell whether this statement is true? If you can't tell, or if it is not true, then I feel we're heading into a minefield of complicating cases that exist in the space spanned up by: - traces on different commands (regardless trace handler) - different trace handlers (regardless command that triggers them) - different types of step trace (enterstep/leavestep - step traces vs. (non-step) command traces - did I forget something ...? Personally, I'm not prepared to say anything about how traces should behave in degenerate cases in remote corners of that space, without fully understanding the design specification of the tracing mechanism. The manual page of the [trace] command is explicit about the behaviour in one specific complicating case: if a trace handler calls the command that triggered the trace in the first place, that command will not trigger a trace again. The issue that I reported appears to violate precisely that one case about which the manual page is explicit. Sincerely, Erik Leunissen ============== eriklns added on 2006-03-31 04:54:22: Logged In: YES user_id=113903 OK (then I misinterpreted your "why"). Tomorrow I can give additional explanation (too late now here, going to sleep). In the meantime, please let me know any specific points that are still unclear. As to "the correct behaviour" of Run2, that requires attention on my part also. It is easy to see that Run2 misbehaves (you can easily see that from additional triggers of the step trace handler at level 3). But the correct behaviour is another matter. I need to study the docs for that, and you might even be quicker finding that out. dgp added on 2006-03-31 03:13:44: Logged In: YES user_id=80530 To clarify, I'm not trying to defend the current behavior. I'm sincerely seeking guidance on what the correct behavior of your test script ought to be. eriklns added on 2006-03-30 23:50:17: Logged In: YES user_id=113903 The reproducible script shows that the triggering of a command trace (i.e. "enter" or "leave" types) affects the triggering of step traces (i.e. "enterstep" or "leavestep" types). That fact alone simply doesn't make sense (to me anyway). The expected output for run 2 is: the step trace handler should trigger for exactly the same commands as in run 1, i.e. once at level 1 (triggered by [isTracedInside_2] and twice at level 2 (both triggered by [set]). (The output from the cmd trace handler, triggered by [clock] , is only shown to enable tracking the path of execution, but is irrelevant for the comparison of the step trace behaviour. You may filter the extra verbosity out by making the command trace handler mute, i.e. an empty body.) Also, the man page for [trace] says: "While command is executing during an execution trace, traces on name are temporarily disabled. This allows the command to execute name in its body without invoking any other traces again." I believe that the script violates this rule. dgp added on 2006-03-30 23:03:50: Logged In: YES user_id=80530 Can you talk me through what you expect the correct result to be, and why? eriklns added on 2006-03-30 23:00:48: Logged In: YES user_id=113903 The patch was created against 8.4.12 dgp added on 2006-03-30 22:35:57: Logged In: YES user_id=80530 thanks for the patch; appears to be against 8.4.13? eriklns added on 2006-03-30 03:23:35: File Added - 172709: tclCmdMZ.c.diff eriklns added on 2006-03-30 03:22:32: File Deleted - 172706: eriklns added on 2006-03-30 02:56:49: File Added - 172706: tclCmdMZ.c.diff eriklns added on 2006-03-30 02:53:17: Logged In: YES user_id=113903 Uploaded a patch file, which also corrects the fact that the replacement in my previous post lacked a: tcmdPtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS; eriklns added on 2006-03-26 04:19:08: Logged In: YES user_id=113903 The following patch remedies the observed behaviour. It concerns the function TraceExecutionProc() in tclCmdMZ.c. Original code (Tcl8.4.12) ========================= tcmdPtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS; iPtr->flags |= INTERP_TRACE_IN_PROGRESS; tcmdPtr->refCount++; /* * This line can have quite arbitrary side-effects, * including deleting the trace, the command being * traced, or even the interpreter. */ traceCode = Tcl_Eval(interp, Tcl_DStringValue(&cmd)); tcmdPtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS; iPtr->flags &= ~INTERP_TRACE_IN_PROGRESS; Replacement =========== /* * Save the state regarding interpreter wide tracing to * restore it after invoking the callback. This prevents * any subsequent command traces at a deeper nesting level * from affecting the interp trace flags. */ int saveInterpFlags = iPtr->flags; iPtr->flags |= INTERP_TRACE_IN_PROGRESS; tcmdPtr->refCount++; /* * This line can have quite arbitrary side-effects, * including deleting the trace, the command being * traced, or even the interpreter. */ traceCode = Tcl_Eval(interp, Tcl_DStringValue(&cmd)); tcmdPtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS; /* * Restore the state regarding interpreter wide tracing */ iPtr->flags = saveInterpFlags; End of patch ============ |