Changeset 249566 in webkit


Ignore:
Timestamp:
Sep 6, 2019 1:14:08 AM (5 years ago)
Author:
Joseph Pecoraro
Message:

Tail Deleted Frames shown in Web Inspector are sometimes incorrect (Shadow Chicken)
https://bugs.webkit.org/show_bug.cgi?id=201366

Reviewed by Saam Barati.

Source/JavaScriptCore:

It is possible for the log buffer to be full right as someone is trying to
log a function prologue. In such a case the machine stack has already been
updated to include the new JavaScript call frame, but the prologue packet
cannot be included in the update because the log is full. This would mean
that the update fails to rationalize the machine stack with the shadow
log / stack. Namely, the current JavaScript call frame is unable to
find a matching prologue (the one we are holding to include after the update)
and inserts a questionable value into the stack; and in the process
missing and removing real potential tail calls.

For example:

"use strict";
function third() { return 1; }
function second() { return third(); }
function first() { return second(); }
function start() { return first(); }

If the the log fills up just as we are entering b then we may have a list
full log of packets looking like:

Shadow Log:

...
{ prologue-packet: entering start ... }
{ prologue-packet: entering first ... }
{ tail-packet: leaving first with a tail call }

Incoming Packet:

{ prologue-packet: entering second ... }

Current JS Stack:

second
start

Since the Current JavaScript stack already has second, if we process the
log without the prologue for second then we push a confused entry on the
shadow stack and clear the log such that we eventually lose the tail-call
information for first to second.

This patch solves this issue by providing enough extra space in the log
to always process the incoming packet when that forces an update. This way
clients can continue to behave exactly as they are.

--

We also document a corner case in some circumstances where the shadow
log may currently be insufficient to know how to reconcile:

For example:

"use strict";
function third() { return 1; }
function second() { return third(); }
function first() { return second(); }
function doNothingTail() { return Math.random() }
function start() {

for (i=0;i<1000;++i) doNothingTail();
return first();

}

In this case the ShadowChicken log may be processed multiple times due
to the many calls to doNothingTail / Math.random(). When calling the
Native function no prologue packet is emitted, so it is unclear that we
temporarly go deeper and come back out on the stack, so the log appears
to have lots of doNothingTail calls reusing the same frame:

Shadow Log:

...
, [123] {callee = 0x72a21aee0, frame = 0x7ffeef897270, callerFrame = 0x7ffeef8972e0, name = start}
, [124] {callee = 0x72a21af10, frame = 0x7ffeef8971f0, callerFrame = 0x7ffeef897270, name = doNothingTail}
, [125] tail-packet:{frame = 0x7ffeef8971f0}
, [126] {callee = 0x72a21af10, frame = 0x7ffeef8971f0, callerFrame = 0x7ffeef897270, name = doNothingTail}
, [127] tail-packet:{frame = 0x7ffeef8971f0}
...
, [140] {callee = 0x72a21af10, frame = 0x7ffeef8971f0, callerFrame = 0x7ffeef897270, name = doNothingTail}
, [141] tail-packet:{frame = 0x7ffeef8971f0}
, [142] {callee = 0x72a21af10, frame = 0x7ffeef8971f0, callerFrame = 0x7ffeef897270, name = doNothingTail}
, [143] tail-packet:{frame = 0x7ffeef8971f0}
, [144] {callee = 0x72a21aeb0, frame = 0x7ffeef8971f0, callerFrame = 0x7ffeef897270, name = first}
, [145] tail-packet:{frame = 0x7ffeef8971f0}
, [146] {callee = 0x72a21ae80, frame = 0x7ffeef8971f0, callerFrame = 0x7ffeef897270, name = second}
...

This log would seem to be indistinguishable from real tail recursion, such as:

"use strict";
function third() { return 1; }
function second() { return third(); }
function first() { return second(); }
function doNothingTail(n) {

return n ? doNothingTail(n-1) : first();

}
function start() {

return doNothingTail(1000);

}

Likewise there are more cases where the shadow log appears to be ambiguous with determining
the appropriate parent call frame with intermediate function calls. In practice this may
not be too problematic, as this is a best effort reconstruction of tail deleted frames.
It seems likely we would only show additional frames that did in fact happen serially
between JavaScript call frames, but may not actually be the proper parent frames
heirachy in the stack.

  • interpreter/ShadowChicken.cpp:

(JSC::ShadowChicken::Packet::dump const):
(JSC::ShadowChicken::Frame::dump const):
(JSC::ShadowChicken::dump const):
Improved debugging output. Especially for functions.

(JSC::ShadowChicken::ShadowChicken):
Make space in the log for 1 additional packet to process when we slow log.

(JSC::ShadowChicken::log):
Include this packet in our update.

(JSC::ShadowChicken::update):
Address an edge case where we can eliminate tail-deleted frames that don't make sense.

LayoutTests:

  • inspector/debugger/tail-deleted-frames-expected.txt: Removed.
  • inspector/debugger/tail-deleted-frames-from-vm-entry-expected.txt: Removed.
  • inspector/debugger/tail-deleted-frames-from-vm-entry.html: Removed.
  • inspector/debugger/tail-deleted-frames-this-value-expected.txt: Removed.
  • inspector/debugger/tail-deleted-frames-this-value.html: Removed.
  • inspector/debugger/tail-deleted-frames.html: Removed.

Remove legacy tests that are difficult to read.

  • inspector/debugger/tail-deleted-frames/resources/stack-trace-utilities.js: Added.

(TestPage.registerInitializer.window.getAsyncStackTrace):
(TestPage.registerInitializer.async.logThisObject):
(TestPage.registerInitializer.async.logScope):
(TestPage.registerInitializer.async.logCallFrame):
(TestPage.registerInitializer):

  • inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-intermediate-frames.js: Added.
  • inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-intermediate-native-tail-deleted-calls.js: Added.
  • inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-intermediate-tail-deleted-frames.js: Added.
  • inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-scopes.js: Added.
  • inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-this-value.js: Added.
  • inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-vm-entry.js: Added.
  • inspector/debugger/tail-deleted-frames/tail-deleted-frames-intermediate-frames-expected.txt: Added.
  • inspector/debugger/tail-deleted-frames/tail-deleted-frames-intermediate-frames.html: Added.
  • inspector/debugger/tail-deleted-frames/tail-deleted-frames-intermediate-tail-deleted-frames-expected.txt: Added.
  • inspector/debugger/tail-deleted-frames/tail-deleted-frames-intermediate-tail-deleted-frames.html: Added.
  • inspector/debugger/tail-deleted-frames/tail-deleted-frames-scopes-expected.txt: Added.
  • inspector/debugger/tail-deleted-frames/tail-deleted-frames-scopes.html: Added.
  • inspector/debugger/tail-deleted-frames/tail-deleted-frames-this-value-expected.txt: Added.
  • inspector/debugger/tail-deleted-frames/tail-deleted-frames-this-value.html: Added.
  • inspector/debugger/tail-deleted-frames/tail-deleted-frames-vm-entry-expected.txt: Added.
  • inspector/debugger/tail-deleted-frames/tail-deleted-frames-vm-entry.html: Added.

Include modern tests that are easier to read.

  • inspector/debugger/tail-deleted-frames/tail-deleted-frames-intermediate-native-tail-deleted-calls-expected.txt: Added.
  • inspector/debugger/tail-deleted-frames/tail-deleted-frames-intermediate-native-tail-deleted-calls.html: Added.

Include a test that is known to produce bad output, since we have reproductive steps.

  • platform/mac/TestExpectations:

Updated pathes.

Location:
trunk
Files:
17 added
4 deleted
5 edited
2 copied
2 moved

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r249565 r249566  
     12019-09-05  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Tail Deleted Frames shown in Web Inspector are sometimes incorrect (Shadow Chicken)
     4        https://bugs.webkit.org/show_bug.cgi?id=201366
     5
     6        Reviewed by Saam Barati.
     7
     8        * inspector/debugger/tail-deleted-frames-expected.txt: Removed.
     9        * inspector/debugger/tail-deleted-frames-from-vm-entry-expected.txt: Removed.
     10        * inspector/debugger/tail-deleted-frames-from-vm-entry.html: Removed.
     11        * inspector/debugger/tail-deleted-frames-this-value-expected.txt: Removed.
     12        * inspector/debugger/tail-deleted-frames-this-value.html: Removed.
     13        * inspector/debugger/tail-deleted-frames.html: Removed.
     14        Remove legacy tests that are difficult to read.
     15
     16        * inspector/debugger/tail-deleted-frames/resources/stack-trace-utilities.js: Added.
     17        (TestPage.registerInitializer.window.getAsyncStackTrace):
     18        (TestPage.registerInitializer.async.logThisObject):
     19        (TestPage.registerInitializer.async.logScope):
     20        (TestPage.registerInitializer.async.logCallFrame):
     21        (TestPage.registerInitializer):
     22        * inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-intermediate-frames.js: Added.
     23        * inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-intermediate-native-tail-deleted-calls.js: Added.
     24        * inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-intermediate-tail-deleted-frames.js: Added.
     25        * inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-scopes.js: Added.
     26        * inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-this-value.js: Added.
     27        * inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-vm-entry.js: Added.
     28        * inspector/debugger/tail-deleted-frames/tail-deleted-frames-intermediate-frames-expected.txt: Added.
     29        * inspector/debugger/tail-deleted-frames/tail-deleted-frames-intermediate-frames.html: Added.
     30        * inspector/debugger/tail-deleted-frames/tail-deleted-frames-intermediate-tail-deleted-frames-expected.txt: Added.
     31        * inspector/debugger/tail-deleted-frames/tail-deleted-frames-intermediate-tail-deleted-frames.html: Added.
     32        * inspector/debugger/tail-deleted-frames/tail-deleted-frames-scopes-expected.txt: Added.
     33        * inspector/debugger/tail-deleted-frames/tail-deleted-frames-scopes.html: Added.
     34        * inspector/debugger/tail-deleted-frames/tail-deleted-frames-this-value-expected.txt: Added.
     35        * inspector/debugger/tail-deleted-frames/tail-deleted-frames-this-value.html: Added.
     36        * inspector/debugger/tail-deleted-frames/tail-deleted-frames-vm-entry-expected.txt: Added.
     37        * inspector/debugger/tail-deleted-frames/tail-deleted-frames-vm-entry.html: Added.
     38        Include modern tests that are easier to read.
     39
     40        * inspector/debugger/tail-deleted-frames/tail-deleted-frames-intermediate-native-tail-deleted-calls-expected.txt: Added.
     41        * inspector/debugger/tail-deleted-frames/tail-deleted-frames-intermediate-native-tail-deleted-calls.html: Added.
     42        Include a test that is known to produce bad output, since we have reproductive steps.
     43
     44        * platform/mac/TestExpectations:
     45        Updated pathes.
     46
    1472019-09-06  Andres Gonzalez  <andresg_22@apple.com>
    248
  • trunk/LayoutTests/inspector/debugger/evaluateOnCallFrame-exception.html

    r239753 r249566  
    1414    const returnByValue = true;
    1515
    16     InspectorTest.debug();
    1716    let suite = InspectorTest.createAsyncSuite("Debugger.evaluateOnCallFrame.Exception");
    1817
  • trunk/LayoutTests/inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-intermediate-native-tail-deleted-calls.js

    r249565 r249566  
    22function a() {
    33    let x = 20;
    4     x;
     4    debugger;
    55    return x;
    66}
     
    1111function c() {
    1212    let z = 60;
    13     return b(); 
     13    return b();
    1414}
    1515function startABC() {
     16    for (let i = 0; i < 5; ++i)
     17        THIS_DOES_NOT_CALL_c();
    1618    c();
    1719}
     20function THIS_DOES_NOT_CALL_c() {
     21    return Math.random();
     22}
  • trunk/LayoutTests/inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-scopes.js

    r249565 r249566  
    22function a() {
    33    let x = 20;
    4     x;
     4    debugger;
    55    return x;
    66}
     
    1111function c() {
    1212    let z = 60;
    13     return b(); 
     13    return b();
    1414}
    1515function startABC() {
  • trunk/LayoutTests/inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-this-value.js

    r249565 r249566  
    22function a() {
    33    let x = 20;
    4     x;
     4    debugger;
    55    return x;
    66}
    77function b() {
    88    let y = 40;
    9     return a();
     9    return a.call({aThis: 2});
    1010}
    1111function c() {
    1212    let z = 60;
    13     return b();
     13    return b.call({bThis: 1});
    1414}
    1515function startABC() {
    16     c();
     16    c.call({cThis: 0});
    1717}
  • trunk/LayoutTests/inspector/debugger/tail-deleted-frames/resources/tail-deleted-frames-vm-entry.js

    r249565 r249566  
    66    if (i > 0)
    77        return bar(i - 1);
    8     return 25;
     8    debugger;
     9    return 99;
    910}
  • trunk/LayoutTests/platform/mac/TestExpectations

    r249561 r249566  
    10601060webkit.org/b/167711 [ Debug ] inspector/debugger/probe-manager-add-remove-actions.html [ Slow ]
    10611061webkit.org/b/168399 [ Debug ] inspector/debugger/search-scripts.html [ Pass Timeout ]
    1062 webkit.org/b/181952 [ Debug ] inspector/debugger/tail-deleted-frames-from-vm-entry.html [ Slow ]
    1063 webkit.org/b/169119 [ Debug ] inspector/debugger/tail-deleted-frames-this-value.html [ Pass Timeout ]
     1062webkit.org/b/181952 [ Debug ] inspector/debugger/tail-deleted-frames/tail-deleted-frames-vm-entry.html [ Slow ]
    10641063webkit.org/b/168387 [ Debug ] inspector/debugger/tail-recursion.html [ Pass Timeout ]
    10651064webkit.org/b/170127 inspector/dom-debugger/dom-breakpoints.html [ Pass Timeout ]
  • trunk/Source/JavaScriptCore/ChangeLog

    r249556 r249566  
     12019-09-05  Joseph Pecoraro  <pecoraro@apple.com>
     2
     3        Tail Deleted Frames shown in Web Inspector are sometimes incorrect (Shadow Chicken)
     4        https://bugs.webkit.org/show_bug.cgi?id=201366
     5
     6        Reviewed by Saam Barati.
     7
     8        It is possible for the log buffer to be full right as someone is trying to
     9        log a function prologue. In such a case the machine stack has already been
     10        updated to include the new JavaScript call frame, but the prologue packet
     11        cannot be included in the update because the log is full. This would mean
     12        that the update fails to rationalize the machine stack with the shadow
     13        log / stack. Namely, the current JavaScript call frame is unable to
     14        find a matching prologue (the one we are holding to include after the update)
     15        and inserts a questionable value into the stack; and in the process
     16        missing and removing real potential tail calls.
     17
     18        For example:
     19       
     20            "use strict";
     21            function third() { return 1; }
     22            function second() { return third(); }
     23            function first() { return second(); }
     24            function start() { return first(); }
     25
     26        If the the log fills up just as we are entering `b` then we may have a list
     27        full log of packets looking like:
     28
     29          Shadow Log:
     30            ...
     31            { prologue-packet: entering `start` ... }
     32            { prologue-packet: entering `first` ... }
     33            { tail-packet: leaving `first` with a tail call }
     34
     35          Incoming Packet:
     36            { prologue-packet: entering `second` ... }
     37
     38          Current JS Stack:
     39            second
     40            start
     41
     42        Since the Current JavaScript stack already has `second`, if we process the
     43        log without the prologue for `second` then we push a confused entry on the
     44        shadow stack and clear the log such that we eventually lose the tail-call
     45        information for `first` to `second`.
     46
     47        This patch solves this issue by providing enough extra space in the log
     48        to always process the incoming packet when that forces an update. This way
     49        clients can continue to behave exactly as they are.
     50
     51        --
     52
     53        We also document a corner case in some circumstances where the shadow
     54        log may currently be insufficient to know how to reconcile:
     55       
     56        For example:
     57
     58            "use strict";
     59            function third() { return 1; }
     60            function second() { return third(); }
     61            function first() { return second(); }
     62            function doNothingTail() { return Math.random() }
     63            function start() {
     64                for (i=0;i<1000;++i) doNothingTail();
     65                return first();
     66            }
     67
     68        In this case the ShadowChicken log may be processed multiple times due
     69        to the many calls to `doNothingTail` / `Math.random()`. When calling the
     70        Native function no prologue packet is emitted, so it is unclear that we
     71        temporarly go deeper and come back out on the stack, so the log appears
     72        to have lots of doNothingTail calls reusing the same frame:
     73
     74          Shadow Log:
     75            ...
     76            , [123] {callee = 0x72a21aee0, frame = 0x7ffeef897270, callerFrame = 0x7ffeef8972e0, name = start}
     77            , [124] {callee = 0x72a21af10, frame = 0x7ffeef8971f0, callerFrame = 0x7ffeef897270, name = doNothingTail}
     78            , [125] tail-packet:{frame = 0x7ffeef8971f0}
     79            , [126] {callee = 0x72a21af10, frame = 0x7ffeef8971f0, callerFrame = 0x7ffeef897270, name = doNothingTail}
     80            , [127] tail-packet:{frame = 0x7ffeef8971f0}
     81            ...
     82            , [140] {callee = 0x72a21af10, frame = 0x7ffeef8971f0, callerFrame = 0x7ffeef897270, name = doNothingTail}
     83            , [141] tail-packet:{frame = 0x7ffeef8971f0}
     84            , [142] {callee = 0x72a21af10, frame = 0x7ffeef8971f0, callerFrame = 0x7ffeef897270, name = doNothingTail}
     85            , [143] tail-packet:{frame = 0x7ffeef8971f0}
     86            , [144] {callee = 0x72a21aeb0, frame = 0x7ffeef8971f0, callerFrame = 0x7ffeef897270, name = first}
     87            , [145] tail-packet:{frame = 0x7ffeef8971f0}
     88            , [146] {callee = 0x72a21ae80, frame = 0x7ffeef8971f0, callerFrame = 0x7ffeef897270, name = second}
     89            ...
     90
     91        This log would seem to be indistinguishable from real tail recursion, such as:
     92
     93            "use strict";
     94            function third() { return 1; }
     95            function second() { return third(); }
     96            function first() { return second(); }
     97            function doNothingTail(n) {
     98                return n ? doNothingTail(n-1) : first();
     99            }
     100            function start() {
     101                return doNothingTail(1000);
     102            }
     103
     104        Likewise there are more cases where the shadow log appears to be ambiguous with determining
     105        the appropriate parent call frame with intermediate function calls. In practice this may
     106        not be too problematic, as this is a best effort reconstruction of tail deleted frames.
     107        It seems likely we would only show additional frames that did in fact happen serially
     108        between JavaScript call frames, but may not actually be the proper parent frames
     109        heirachy in the stack.
     110
     111        * interpreter/ShadowChicken.cpp:
     112        (JSC::ShadowChicken::Packet::dump const):
     113        (JSC::ShadowChicken::Frame::dump const):
     114        (JSC::ShadowChicken::dump const):
     115        Improved debugging output. Especially for functions.
     116
     117        (JSC::ShadowChicken::ShadowChicken):
     118        Make space in the log for 1 additional packet to process when we slow log.
     119
     120        (JSC::ShadowChicken::log):
     121        Include this packet in our update.
     122
     123        (JSC::ShadowChicken::update):
     124        Address an edge case where we can eliminate tail-deleted frames that don't make sense.
     125
    11262019-09-05  Mark Lam  <mark.lam@apple.com>
    2127
  • trunk/Source/JavaScriptCore/interpreter/ShadowChicken.cpp

    r232983 r249566  
    4646   
    4747    if (isPrologue()) {
     48        String name = "?"_s;
     49        if (auto* function = jsDynamicCast<JSFunction*>(callee->vm(), callee)) {
     50            name = function->name(callee->vm());
     51            if (name.isEmpty())
     52                name = "?"_s;
     53        }
     54
    4855        out.print(
    4956            "{callee = ", RawPointer(callee), ", frame = ", RawPointer(frame), ", callerFrame = ",
    50             RawPointer(callerFrame), "}");
     57            RawPointer(callerFrame), ", name = ", name, "}");
    5158        return;
    5259    }
     
    6370void ShadowChicken::Frame::dump(PrintStream& out) const
    6471{
     72    String name = "?"_s;
     73    if (auto* function = jsDynamicCast<JSFunction*>(callee->vm(), callee)) {
     74        name = function->name(callee->vm());
     75        if (name.isEmpty())
     76            name = "?"_s;
     77    }
     78
    6579    out.print(
    66         "{callee = ", RawPointer(callee), ", frame = ", RawPointer(frame), ", isTailDeleted = ",
    67         isTailDeleted, "}");
     80        "{callee = ", *callee, ", frame = ", RawPointer(frame), ", isTailDeleted = ",
     81        isTailDeleted, ", name = ", name, "}");
    6882}
    6983
     
    7185    : m_logSize(Options::shadowChickenLogSize())
    7286{
    73     m_log = static_cast<Packet*>(fastZeroedMalloc(sizeof(Packet) * m_logSize));
     87    // Allow one additional packet beyond m_logEnd. This is useful for the moment we
     88    // log a packet when the log is full and force an update. At that moment the packet
     89    // that is being logged should be included in the update because it may be
     90    // a critical prologue needed to rationalize the current machine stack with the
     91    // shadow stack.
     92    m_log = static_cast<Packet*>(fastZeroedMalloc(sizeof(Packet) * m_logSize + 1));
    7493    m_logCursor = m_log;
    7594    m_logEnd = m_log + m_logSize;
     
    83102void ShadowChicken::log(VM& vm, ExecState* exec, const Packet& packet)
    84103{
     104    // This write is allowed because we construct the log with space for 1 additional packet.
     105    *m_logCursor++ = packet;
    85106    update(vm, exec);
    86     *m_logCursor++ = packet;
    87107}
    88108
     
    143163    }
    144164
    145    
    146165    if (ShadowChickenInternal::verbose)
    147166        dataLog("    Revised stack: ", listDump(m_stack), "\n");
     
    289308
    290309            CallFrame* callFrame = visitor->callFrame();
    291             if (ShadowChickenInternal::verbose)
    292                 dataLog("    Examining ", RawPointer(callFrame), "\n");
     310            if (ShadowChickenInternal::verbose) {
     311                dataLog("    Examining callFrame:", RawPointer(callFrame), ", callee:", RawPointer(callFrame->jsCallee()), ", callerFrame:", RawPointer(callFrame->callerFrame()), "\n");
     312                JSObject* callee = callFrame->jsCallee();
     313                if (auto* function = jsDynamicCast<JSFunction*>(callee->vm(), callee))
     314                    dataLog("      Function = ", function->name(callee->vm()), "\n");
     315            }
     316
    293317            if (callFrame == highestPointSinceLastTime) {
    294318                if (ShadowChickenInternal::verbose)
    295                     dataLog("    Bailing at ", RawPointer(callFrame), " because it's the highest point since last time.\n");
     319                    dataLog("    Bailing at ", RawPointer(callFrame), " because it's the highest point since last time\n");
     320
     321                // FIXME: At this point the shadow stack may still have tail deleted frames
     322                // that do not run into the current call frame but are left in the shadow stack.
     323                // Those tail deleted frames should be validated somehow.
     324
    296325                return StackVisitor::Done;
    297326            }
     
    319348                && m_log[indexInLog].frame == toPush.last().frame) {
    320349                if (ShadowChickenInternal::verbose)
    321                     dataLog("    Going to loop through to find tail deleted frames with indexInLog = ", indexInLog, " and push-stack top = ", toPush.last(), "\n");
     350                    dataLog("    Going to loop through to find tail deleted frames using ", RawPointer(callFrame), " with indexInLog = ", indexInLog, " and push-stack top = ", toPush.last(), "\n");
    322351                for (;;) {
    323352                    ASSERT(m_log[indexInLog].frame == toPush.last().frame);
     
    341370                    }
    342371                    indexInLog--; // Skip over the tail packet.
     372
     373                    // FIXME: After a few iterations the tail packet referenced frame may not be the
     374                    // same as the original callFrame for the real stack frame we started with.
     375                    // It is unclear when we should break.
    343376                   
    344377                    if (!advanceIndexInLogTo(tailPacket.frame, nullptr, nullptr)) {
     
    380413
    381414    if (ShadowChickenInternal::verbose)
    382         dataLog("    After pushing: ", *this, "\n");
     415        dataLog("    After pushing: ", listDump(m_stack), "\n");
    383416
    384417    // Remove tail frames until the number of tail deleted frames is small enough.
     
    448481    out.print("\n");
    449482    for (unsigned i = 0; i < limit; ++i)
    450         out.print("\t", comma, m_log[i], "\n");
     483        out.print("\t", comma, "[", i, "] ", m_log[i], "\n");
    451484    out.print("]}");
    452485}
Note: See TracChangeset for help on using the changeset viewer.