RESOLVED FIXED 155325
Web Inspector: Debugger: Better expression / statement stepping in the debugger
https://bugs.webkit.org/show_bug.cgi?id=155325
Summary Web Inspector: Debugger: Better expression / statement stepping in the debugger
Keith Miller
Reported 2016-03-10 14:21:41 PST
If I have code like: 1) ... 2) foo(0, bar(0)); 3) return 1; If I am on line 1 in the inspector then step to line 2 and step into bar when I step out from inside bar the inspector returns to line 3. It would be nice if the inspector return to line 2 after bar has been executed rather than to line 3.
Attachments
Testcase (108 bytes, text/html)
2016-03-15 09:05 PDT, Antoine Quint
no flags
Screen recording showing the issue with the attached test case (5.25 MB, video/quicktime)
2016-03-15 09:07 PDT, Antoine Quint
no flags
[PATCH] Proposed Fix (306.02 KB, patch)
2016-09-14 00:05 PDT, Joseph Pecoraro
no flags
[TXT] Benchmark Results - Neutral (80.88 KB, text/plain)
2016-09-14 00:08 PDT, Joseph Pecoraro
no flags
[PATCH] Proposed Fix (307.03 KB, patch)
2016-09-14 00:18 PDT, Joseph Pecoraro
buildbot: commit-queue-
Archive of layout-test-results from ews103 for mac-yosemite (853.59 KB, application/zip)
2016-09-14 01:16 PDT, Build Bot
no flags
Archive of layout-test-results from ews105 for mac-yosemite-wk2 (986.98 KB, application/zip)
2016-09-14 01:21 PDT, Build Bot
no flags
Archive of layout-test-results from ews113 for mac-yosemite (1.56 MB, application/zip)
2016-09-14 01:24 PDT, Build Bot
no flags
[PATCH] Proposed Fix (318.64 KB, patch)
2016-09-14 15:41 PDT, Joseph Pecoraro
no flags
[PATCH] Proposed Fix (318.52 KB, patch)
2016-09-14 15:52 PDT, Joseph Pecoraro
bburg: review-
bburg: commit-queue-
[PATCH] Proposed Fix (317.85 KB, patch)
2016-09-26 20:12 PDT, Joseph Pecoraro
buildbot: commit-queue-
Archive of layout-test-results from ews102 for mac-yosemite (1.25 MB, application/zip)
2016-09-26 20:56 PDT, Build Bot
no flags
Archive of layout-test-results from ews106 for mac-yosemite-wk2 (1.37 MB, application/zip)
2016-09-26 20:57 PDT, Build Bot
no flags
[PATCH] Proposed Fix (317.85 KB, patch)
2016-09-26 21:48 PDT, Joseph Pecoraro
mark.lam: review+
Radar WebKit Bug Importer
Comment 1 2016-03-10 14:23:06 PST
Keith Miller
Comment 2 2016-03-10 14:23:34 PST
This same behavior happens if I were to step over from the return of bar as well.
Antoine Quint
Comment 3 2016-03-15 09:05:17 PDT
Created attachment 274096 [details] Testcase
Antoine Quint
Comment 4 2016-03-15 09:07:46 PDT
Created attachment 274097 [details] Screen recording showing the issue with the attached test case
Timothy Hatcher
Comment 5 2016-03-15 12:28:42 PDT
Joseph Pecoraro
Comment 6 2016-09-12 12:31:10 PDT
I am using this bug for better debugger stepping in general. Since addressing this and testing it exposed numerous issues the code changes are very small once some general fixes were applied: - introduce pausing at certain expression locations (call sites) - this will fix step-in/out and stepping through lines with multiple function calls - correct, and make consistent, stepping behavior all over the place: - if/while/for pause locations (e.g. pause before condition, not before "if") - function stepping pause locations (e.g. remove entry pause, consistent exit pause) - remove unnecessary pauses (function entry, program entry, throw statement double pause) - add tests for this area of code that is severely lacking in tests!
Joseph Pecoraro
Comment 7 2016-09-14 00:05:05 PDT
Created attachment 288776 [details] [PATCH] Proposed Fix The actual patch to JavaScriptCore is rather small. The bulk of this patch is LayoutTests.
Joseph Pecoraro
Comment 8 2016-09-14 00:08:55 PDT
Created attachment 288777 [details] [TXT] Benchmark Results - Neutral This adds a new debug hook for function calls. So I measured before and after the change forcing debugger bytecodes enabled to see if the extra op_debugs would affect performance. The results were completely neutral. Full results attached. Geomean of preferred means: <scaled-result> 111.1668+-0.2292 ? 111.2490+-0.0709 ? might be 1.0007x slower
Joseph Pecoraro
Comment 9 2016-09-14 00:18:11 PDT
Created attachment 288778 [details] [PATCH] Proposed Fix
WebKit Commit Bot
Comment 10 2016-09-14 00:20:23 PDT
Attachment 288778 [details] did not pass style-queue: ERROR: LayoutTests/TestExpectations:135: "webkit.org/b/161951" is not at the start of the line. [test/expectations] [5] Total errors found: 1 in 44 files If any of these errors are false positives, please file a bug against check-webkit-style.
Build Bot
Comment 11 2016-09-14 01:16:21 PDT
Comment on attachment 288778 [details] [PATCH] Proposed Fix Attachment 288778 [details] did not pass mac-ews (mac): Output: http://webkit-queues.webkit.org/results/2070645 New failing tests: inspector/debugger/tail-deleted-frames-from-vm-entry.html inspector/debugger/regress-133182.html
Build Bot
Comment 12 2016-09-14 01:16:25 PDT
Created attachment 288782 [details] Archive of layout-test-results from ews103 for mac-yosemite The attached test failures were seen while running run-webkit-tests on the mac-ews. Bot: ews103 Port: mac-yosemite Platform: Mac OS X 10.10.5
Build Bot
Comment 13 2016-09-14 01:21:42 PDT
Comment on attachment 288778 [details] [PATCH] Proposed Fix Attachment 288778 [details] did not pass mac-wk2-ews (mac-wk2): Output: http://webkit-queues.webkit.org/results/2070656 New failing tests: inspector/debugger/tail-deleted-frames-from-vm-entry.html inspector/debugger/regress-133182.html
Build Bot
Comment 14 2016-09-14 01:21:46 PDT
Created attachment 288783 [details] Archive of layout-test-results from ews105 for mac-yosemite-wk2 The attached test failures were seen while running run-webkit-tests on the mac-wk2-ews. Bot: ews105 Port: mac-yosemite-wk2 Platform: Mac OS X 10.10.5
Build Bot
Comment 15 2016-09-14 01:24:11 PDT
Comment on attachment 288778 [details] [PATCH] Proposed Fix Attachment 288778 [details] did not pass mac-debug-ews (mac): Output: http://webkit-queues.webkit.org/results/2070649 New failing tests: inspector/debugger/tail-deleted-frames-from-vm-entry.html inspector/debugger/stepping/stepping-function-calls.html inspector/debugger/regress-133182.html
Build Bot
Comment 16 2016-09-14 01:24:15 PDT
Created attachment 288785 [details] Archive of layout-test-results from ews113 for mac-yosemite The attached test failures were seen while running run-webkit-tests on the mac-debug-ews. Bot: ews113 Port: mac-yosemite Platform: Mac OS X 10.10.5
Joseph Pecoraro
Comment 17 2016-09-14 15:41:46 PDT
Created attachment 288876 [details] [PATCH] Proposed Fix Rebaselined the other tests.
Joseph Pecoraro
Comment 18 2016-09-14 15:52:01 PDT
Created attachment 288877 [details] [PATCH] Proposed Fix
Blaze Burg
Comment 19 2016-09-15 13:04:30 PDT
Comment on attachment 288877 [details] [PATCH] Proposed Fix View in context: https://bugs.webkit.org/attachment.cgi?id=288877&action=review > LayoutTests/inspector/debugger/regress-133182.html:66 OK. > LayoutTests/inspector/debugger/stepping/stepOut-expected.txt:21 > +--- Source Unavailable --- I would have expected this to stop at the statement after eval, since in a function context we stop at the end of the scope before returning. Wouldn't step-in into an eval stop on the eval'd statement(s)? It seems asymmetrical. > LayoutTests/inspector/debugger/stepping/stepOut-expected.txt:70 > + 32 })(); This seems to not match behavior of pausing at end of scope in innerFunction. > LayoutTests/inspector/debugger/stepping/stepOver-expected.txt:168 > + -> 34 }|)(); ...I expected stepOut to pause here, like for stepIn/stepOver. Maybe I'm not remembering correctly what I expect to happen. > LayoutTests/inspector/debugger/stepping/stepping-arrow-functions-expected.txt:103 > +EXPRESSION: setTimeout(entryArrowFunctionParenthesis) Does this test use template strings? I don't see any... > LayoutTests/inspector/debugger/stepping/stepping-classes-expected.txt:254 > +--- Source Unavailable --- Why is the source unavailable? > LayoutTests/inspector/debugger/stepping/stepping-classes-expected.txt:350 > + OK.
Joseph Pecoraro
Comment 20 2016-09-15 13:21:30 PDT
Comment on attachment 288877 [details] [PATCH] Proposed Fix View in context: https://bugs.webkit.org/attachment.cgi?id=288877&action=review >> LayoutTests/inspector/debugger/stepping/stepOut-expected.txt:21 >> +--- Source Unavailable --- > > I would have expected this to stop at the statement after eval, since in a function context we stop at the end of the scope before returning. Wouldn't step-in into an eval stop on the eval'd statement(s)? It seems asymmetrical. I agree. I recall there being something weird about this particular case. I'll file a follow-up. >> LayoutTests/inspector/debugger/stepping/stepOut-expected.txt:70 >> + 32 })(); > > This seems to not match behavior of pausing at end of scope in innerFunction. step-out never pauses when leaving a function. The other steps would (step-in or step-over). >> LayoutTests/inspector/debugger/stepping/stepOver-expected.txt:168 >> + -> 34 }|)(); > > ...I expected stepOut to pause here, like for stepIn/stepOver. Maybe I'm not remembering correctly what I expect to happen. step-out should require two clicks to leave a function. Its normal behavior is one click takes you out. >> LayoutTests/inspector/debugger/stepping/stepping-arrow-functions-expected.txt:103 >> +EXPRESSION: setTimeout(entryArrowFunctionParenthesis) > > Does this test use template strings? I don't see any... Copy&paste error! Good catch. There might be more. Each test name should be unique though. >> LayoutTests/inspector/debugger/stepping/stepping-classes-expected.txt:254 >> +--- Source Unavailable --- > > Why is the source unavailable? This is the default constructor JavaScriptCore internally creates: function() { super(...arguments); } In a real browser we would show that small snippet and "step through it" but that is kinda lame. I have FIXMEs below for this.
Joseph Pecoraro
Comment 21 2016-09-15 13:22:10 PDT
Comment on attachment 288877 [details] [PATCH] Proposed Fix View in context: https://bugs.webkit.org/attachment.cgi?id=288877&action=review >>> LayoutTests/inspector/debugger/stepping/stepOver-expected.txt:168 >>> + -> 34 }|)(); >> >> ...I expected stepOut to pause here, like for stepIn/stepOver. Maybe I'm not remembering correctly what I expect to happen. > > step-out should require two clicks to leave a function. Its normal behavior is one click takes you out. Correction: *shouldn't* require two clicks
Joseph Pecoraro
Comment 22 2016-09-15 14:20:38 PDT
Comment on attachment 288877 [details] [PATCH] Proposed Fix View in context: https://bugs.webkit.org/attachment.cgi?id=288877&action=review > LayoutTests/inspector/debugger/stepping/stepping-control-flow-expected.txt:26 > +ACTION: step-over > +PAUSE AT entryIfSingleStatement:17:9 > + 13 > + 14 function entryIfSingleStatement() { > + 15 debugger; > + -> 16 if (|true) > + 17 a(); > + 18 if (false) > + 19 a(); I just realized that moving breakpoints from the start of the statement to the condition is an issue with how we handle breakpoints right now. The frontend sets the breakpoint at the line:column of the start of the statement ("|if") here. By changing the pause locations, JSC doesn't see that, it sees (|true) as the pause location. There are a few possible solutions: 1. Quick hack, emit a "WillExecuteStatementForBreakpointCheck" at the start of the statements where breakpoints could be set (if, while, for, throw, label, ...) 2. Frontend solution: Find an appropriate location for a breakpoint on the line and send it to the backend. 3. Backend solution: Build up a list of breakpoint, when the frontend says (line:column) slide to the appropriate (realLine:realColumn) If (1) works I'll do it now and start working on one of the better fixes. (2) has risk because it can go out of date if the backend changes locations. (3) is probably ideal. Note that (2-3) would also solve problems such as "set a breakpoint on an empty line, or a line with comments => real breakpoint is set on the next statement.
Blaze Burg
Comment 23 2016-09-15 15:16:49 PDT
Comment on attachment 288877 [details] [PATCH] Proposed Fix View in context: https://bugs.webkit.org/attachment.cgi?id=288877&action=review Test code looks good. There are some surprising steps that I noted but nothing that should be addressed here. Holding r? for someone to look at JSC changes. > LayoutTests/inspector/debugger/stepping/stepping-function-calls-expected.txt:763 > + -> 46 add(a(), x = 1, |b()); Oh, we skipped the assignment. Could that trigger a getter we need to step into? > LayoutTests/inspector/debugger/stepping/stepping-function-calls-expected.txt:994 > + -> 51 |a(), b(), a(); Oh, that's weird. I guess it steps over the statement, but that might not be what people want here. > LayoutTests/inspector/debugger/stepping/stepping-loops-expected.txt:167 > + -> 27 for (|let i = 0; i < 2; ++i) Hmmm, don't we evaluate i < 2 before evaluating loop body? > LayoutTests/inspector/debugger/stepping/stepping-loops-expected.txt:187 > + -> 27 for (let i = 0; i < 2; ++i|) Hmm, is the column correct here? I would expect stop before ++i > LayoutTests/inspector/debugger/stepping/stepping-loops-expected.txt:274 > + -> 27 for (let i = 0; |i < 2; ++i) I see, step-in will evaluate this. Definitely should revisit whether this is expected. > LayoutTests/inspector/debugger/stepping/stepping-template-string-expected.txt:70 > + -> 7 |return 1; Errr, what is this stepping into? What's the evaluation order for template strings anyway? > LayoutTests/inspector/debugger/stepping/stepping-try-catch-finally-expected.txt:174 > + -> 41 throw new Error|; Hmm, interesting that this is its own step. I would have expected to go straight to the catch.
Blaze Burg
Comment 24 2016-09-15 15:17:14 PDT
Comment on attachment 288877 [details] [PATCH] Proposed Fix Bugzilla has a tiny brain.
Joseph Pecoraro
Comment 25 2016-09-15 17:52:36 PDT
Comment on attachment 288877 [details] [PATCH] Proposed Fix View in context: https://bugs.webkit.org/attachment.cgi?id=288877&action=review >> LayoutTests/inspector/debugger/stepping/stepping-function-calls-expected.txt:763 >> + -> 46 add(a(), x = 1, |b()); > > Oh, we skipped the assignment. Could that trigger a getter we need to step into? Getters / setters are already a beast to be tackled separately. We will need to specifically handle it in a few ways (step out of a setter seems different from stepping out of a function). That said, no debugger seems to pause on simple assignment right now. I had FIXMEs in the tests themselves saying "opportunity to pause at assignments", but given no debuggers do that it would probably be unexpected for most developers. >> LayoutTests/inspector/debugger/stepping/stepping-function-calls-expected.txt:994 >> + -> 51 |a(), b(), a(); > > Oh, that's weird. I guess it steps over the statement, but that might not be what people want here. "step-over" should step over the entire statement. FWIW, I have an ongoing discussion that "step-over" should step over the current LINE (even if there are multiple statements on the line). That is not the current behavior (step-over is statement level, not line level), but step-over going by line is what many users expect because of lldb, gdb. I will look into that later. But I think the behavior here is pretty expected in the comma expression. >> LayoutTests/inspector/debugger/stepping/stepping-loops-expected.txt:167 >> + -> 27 for (|let i = 0; i < 2; ++i) > > Hmmm, don't we evaluate i < 2 before evaluating loop body? This is something I cobbled together. It can certainly be tweaked. For now I figured most users would want a single step-over to enter the loop. So I made that behavior work with some special treatment. step-in will step through both. I think this will most obviously get cleaned up if we move step-over to really "step over this line". >> LayoutTests/inspector/debugger/stepping/stepping-loops-expected.txt:187 >> + -> 27 for (let i = 0; i < 2; ++i|) > > Hmm, is the column correct here? I would expect stop before ++i Correct, this is a bug for a follow-up. >> LayoutTests/inspector/debugger/stepping/stepping-template-string-expected.txt:70 >> + -> 7 |return 1; > > Errr, what is this stepping into? What's the evaluation order for template strings anyway? Template strings evaluate like function calls. Something like: let str = `literal ${1} assignment ${x=1} call ${a()} call2 ${|a()}`; let str = tag`literal ${1} assignment ${x=1} call ${a()} call2 ${|a()}`; let str = tag(["", "literal ", " assignment ", " call ", " call 2", ""], [1, x=1, a(), a()]) So it would be stepping through this: magic(1, x=1, a(), a()) >> LayoutTests/inspector/debugger/stepping/stepping-try-catch-finally-expected.txt:174 >> + -> 41 throw new Error|; > > Hmm, interesting that this is its own step. I would have expected to go straight to the catch. This is the "PauseOnException" test. The "NoPauseOnException" version above does go straight to the catch. I think i you have PauseOnException enable you will want to pause when the exception happens, hopefully with more info pointing to exactly where the error happened. We could use this as an opportunity to show an inline error message in the debugger at the pause line.
Joseph Pecoraro
Comment 26 2016-09-15 20:03:53 PDT
Retitling the bug since the fix is more general.
Joseph Pecoraro
Comment 27 2016-09-26 20:12:11 PDT
Created attachment 289905 [details] [PATCH] Proposed Fix Updated patch fixed an issue in tests and fixed output location for for..in and for..of loops.
Build Bot
Comment 28 2016-09-26 20:56:48 PDT
Comment on attachment 289905 [details] [PATCH] Proposed Fix Attachment 289905 [details] did not pass mac-ews (mac): Output: http://webkit-queues.webkit.org/results/2152030 New failing tests: inspector/debugger/stepping/stepping-loops.html
Build Bot
Comment 29 2016-09-26 20:56:52 PDT
Created attachment 289907 [details] Archive of layout-test-results from ews102 for mac-yosemite The attached test failures were seen while running run-webkit-tests on the mac-ews. Bot: ews102 Port: mac-yosemite Platform: Mac OS X 10.10.5
Build Bot
Comment 30 2016-09-26 20:57:10 PDT
Comment on attachment 289905 [details] [PATCH] Proposed Fix Attachment 289905 [details] did not pass mac-wk2-ews (mac-wk2): Output: http://webkit-queues.webkit.org/results/2152027 New failing tests: inspector/debugger/stepping/stepping-loops.html
Build Bot
Comment 31 2016-09-26 20:57:15 PDT
Created attachment 289908 [details] Archive of layout-test-results from ews106 for mac-yosemite-wk2 The attached test failures were seen while running run-webkit-tests on the mac-wk2-ews. Bot: ews106 Port: mac-yosemite-wk2 Platform: Mac OS X 10.10.5
Joseph Pecoraro
Comment 32 2016-09-26 21:44:01 PDT
Apparently I didn't update the results for one of the tests...
Joseph Pecoraro
Comment 33 2016-09-26 21:48:39 PDT
Created attachment 289913 [details] [PATCH] Proposed Fix
Mark Lam
Comment 34 2016-09-27 10:23:52 PDT
Comment on attachment 289913 [details] [PATCH] Proposed Fix View in context: https://bugs.webkit.org/attachment.cgi?id=289913&action=review > Source/JavaScriptCore/ChangeLog:28 > + These pauses are not needed because if there is a statement we Missing comma between "statement" and "we". > Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:2754 > generator.emitInc(i.get()); > + generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset()); Don't we always break before an action? Hence, should this debug hook be before the inc? > Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:2794 > generator.emitInc(enumeratorIndex.get()); > generator.emitEnumeratorStructurePropertyName(propertyName.get(), enumerator.get(), enumeratorIndex.get()); > + generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset()); Ditto. > Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:2831 > generator.emitInc(enumeratorIndex.get()); > generator.emitEnumeratorGenericPropertyName(propertyName.get(), enumerator.get(), enumeratorIndex.get()); > + generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset()); Ditto. > Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:2987 > - generator.emitDebugHook(WillLeaveCallFrame, lastLine(), startOffset(), lineStartOffset()); > + generator.emitWillLeaveCallFrameDebugHook(); > generator.emitReturn(returnRegister.get()); Since emitWillLeaveCallFrameDebugHook() uses the last } of the current scope, will this work in the following scenario: function foo() { { let x = 0; return x; } // <===== this is the } of the current scope. } // <===== we would expect this to be associate with the return. Or am I mistaken?
Mark Lam
Comment 35 2016-09-27 10:44:47 PDT
Comment on attachment 289913 [details] [PATCH] Proposed Fix View in context: https://bugs.webkit.org/attachment.cgi?id=289913&action=review >> Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:2987 >> generator.emitReturn(returnRegister.get()); > > Since emitWillLeaveCallFrameDebugHook() uses the last } of the current scope, will this work in the following scenario: > > function foo() { > { > let x = 0; > return x; > } // <===== this is the } of the current scope. > } // <===== we would expect this to be associate with the return. > > Or am I mistaken? My mistake! emitWillLeaveCallFrameDebugHook() uses m_scopeNode which is the scope node for the outer most scope. Hence, my concern is invalid.
Mark Lam
Comment 36 2016-09-27 11:35:15 PDT
Comment on attachment 289913 [details] [PATCH] Proposed Fix View in context: https://bugs.webkit.org/attachment.cgi?id=289913&action=review r=me >> Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:2754 >> + generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset()); > > Don't we always break before an action? Hence, should this debug hook be before the inc? Joe explained offline that this is in a for-in loop. The iterator adjustment here is not visible to the user. Hence, there is no issue here. >> Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:2794 >> + generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset()); > > Ditto. No issue here either. >> Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:2831 >> + generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset()); > > Ditto. Same. No issue here.
Joseph Pecoraro
Comment 37 2016-09-28 13:43:24 PDT
*** Bug 25734 has been marked as a duplicate of this bug. ***
Joseph Pecoraro
Comment 38 2016-09-30 12:35:39 PDT
Joseph Pecoraro
Comment 39 2016-09-30 12:36:40 PDT
*** Bug 161721 has been marked as a duplicate of this bug. ***
Joseph Pecoraro
Comment 40 2016-09-30 12:36:57 PDT
*** Bug 161716 has been marked as a duplicate of this bug. ***
Joseph Pecoraro
Comment 41 2016-09-30 16:42:30 PDT
*** Bug 139523 has been marked as a duplicate of this bug. ***
Note You need to log in before you can comment on or make changes to this bug.