void GetByIdStatus::dump(PrintStream& out) const { out.print("("); switch (m_state) { case NoInformation: out.print("NoInformation"); break; case Simple: out.print("Simple"); break; case TakesSlowPath: out.print("TakesSlowPath"); break; case MakesCalls: out.print("MakesCalls"); break; } out.print(", ", listDump(m_variants), ", seenInJIT = ", m_wasSeenInJIT, ")"); }
void CallLinkStatus::dump(PrintStream& out) const { if (!isSet()) { out.print("Not Set"); return; } CommaPrinter comma; if (m_isProved) out.print(comma, "Statically Proved"); if (m_couldTakeSlowPath) out.print(comma, "Could Take Slow Path"); if (!m_variants.isEmpty()) out.print(comma, listDump(m_variants)); if (m_maxNumArguments) out.print(comma, "maxNumArguments = ", m_maxNumArguments); }
void PutByIdStatus::dump(PrintStream& out) const { switch (m_state) { case NoInformation: out.print("(NoInformation)"); return; case Simple: out.print("(", listDump(m_variants), ")"); return; case TakesSlowPath: out.print("(TakesSlowPath)"); return; case MakesCalls: out.print("(MakesCalls)"); return; } RELEASE_ASSERT_NOT_REACHED(); }
void CallLinkInfo::visitWeak(RepatchBuffer& repatchBuffer) { auto handleSpecificCallee = [&] (JSFunction* callee) { if (Heap::isMarked(callee->executable())) m_hasSeenClosure = true; else m_clearedByGC = true; }; if (isLinked()) { if (stub()) { if (!stub()->visitWeak(repatchBuffer)) { if (Options::verboseOSR()) { dataLog( "Clearing closure call from ", *repatchBuffer.codeBlock(), " to ", listDump(stub()->variants()), ", stub routine ", RawPointer(stub()), ".\n"); } unlink(repatchBuffer); m_clearedByGC = true; } } else if (!Heap::isMarked(m_callee.get())) { if (Options::verboseOSR()) { dataLog( "Clearing call from ", *repatchBuffer.codeBlock(), " to ", RawPointer(m_callee.get()), " (", m_callee.get()->executable()->hashFor(specializationKind()), ").\n"); } handleSpecificCallee(m_callee.get()); unlink(repatchBuffer); } } if (haveLastSeenCallee() && !Heap::isMarked(lastSeenCallee())) { handleSpecificCallee(lastSeenCallee()); clearLastSeenCallee(); } }
bool listGetAndRemoveNext(listHead* pList, void** ppData) { struct listNode* pNode; bool result; pthread_mutex_lock(&pList->mutex); if (pList->pFirst) { /* Empty list */ ALOGE("Failed to deallocate (list empty)"); result = false; goto clean_and_return; } /* Work on the first node */ pNode = pList->pFirst; /* Return the data */ if (ppData != NULL) { *ppData = pNode->pData; } /* Remove and deallocate the node */ pList->pFirst = pNode->pNext; TRACE("Deallocating node: %8p (%8p)", pNode, pNode->pData); free(pNode); result = true; clean_and_return: listDump(pList); pthread_mutex_unlock(&pList->mutex); return result; }
void StackMaps::dump(PrintStream& out) const { out.print("Version:", version, ", StackSizes[", listDump(stackSizes), "], Constants:[", listDump(constants), "], Records:[", listDump(records), "]"); }
void StackMaps::Record::dump(PrintStream& out) const { out.print( "(#", patchpointID, ", offset = ", instructionOffset, ", flags = ", flags, ", [", listDump(locations), "])"); }
void ShadowChicken::update(VM&, ExecState* exec) { if (verbose) { dataLog("Running update on: ", *this, "\n"); WTFReportBacktrace(); } const unsigned logCursorIndex = m_logCursor - m_log; // We need to figure out how to reconcile the current machine stack with our shadow stack. We do // that by figuring out how much of the shadow stack to pop. We apply three different rules. The // precise rule relies on the log. The log contains caller frames, which means that we know // where we bottomed out after making any call. If we bottomed out but made no calls then 'exec' // will tell us. That's why "highestPointSinceLastTime" will go no lower than exec. The third // rule, based on comparing to the current real stack, is executed in a later loop. CallFrame* highestPointSinceLastTime = exec; for (unsigned i = logCursorIndex; i--;) { Packet packet = m_log[i]; if (packet.isPrologue()) { CallFrame* watermark; if (i && m_log[i - 1].isTail()) watermark = packet.frame; else watermark = packet.callerFrame; highestPointSinceLastTime = std::max(highestPointSinceLastTime, watermark); } } if (verbose) dataLog(" Highest point since last time: ", RawPointer(highestPointSinceLastTime), "\n"); while (!m_stack.isEmpty() && m_stack.last().frame < highestPointSinceLastTime) m_stack.removeLast(); if (verbose) dataLog(" Revised stack: ", listDump(m_stack), "\n"); // It's possible that the top of stack is now tail-deleted. The stack no longer contains any // frames below the log's high watermark. That means that we just need to look for the first // occurence of a tail packet for the current stack top. if (!m_stack.isEmpty()) { ASSERT(!m_stack.last().isTailDeleted); for (unsigned i = 0; i < logCursorIndex; ++i) { Packet& packet = m_log[i]; if (packet.isTail() && packet.frame == m_stack.last().frame) { m_stack.last().isTailDeleted = true; break; } } } if (verbose) dataLog(" Revised stack: ", listDump(m_stack), "\n"); // The log-based and exec-based rules require that ShadowChicken was enabled. The point of // ShadowChicken is to give sensible-looking results even if we had not logged. This means that // we need to reconcile the shadow stack and the real stack by actually looking at the real // stack. This reconciliation allows the shadow stack to have extra tail-deleted frames, but it // forbids it from diverging from the real stack on normal frames. if (!m_stack.isEmpty()) { Vector<Frame> stackRightNow; StackVisitor::visit( exec, [&] (StackVisitor& visitor) -> StackVisitor::Status { if (visitor->isInlinedFrame()) return StackVisitor::Continue; bool isTailDeleted = false; stackRightNow.append(Frame(visitor->callee(), visitor->callFrame(), isTailDeleted)); return StackVisitor::Continue; }); stackRightNow.reverse(); if (verbose) dataLog(" Stack right now: ", listDump(stackRightNow), "\n"); unsigned shadowIndex = 0; unsigned rightNowIndex = 0; while (shadowIndex < m_stack.size() && rightNowIndex < stackRightNow.size()) { if (m_stack[shadowIndex].isTailDeleted) { shadowIndex++; continue; } if (m_stack[shadowIndex] == stackRightNow[rightNowIndex]) { shadowIndex++; rightNowIndex++; continue; } break; } m_stack.resize(shadowIndex); if (verbose) dataLog(" Revised stack: ", listDump(m_stack), "\n"); } // It's possible that the top stack frame is actually lower than highestPointSinceLastTime. // Account for that here. highestPointSinceLastTime = nullptr; for (unsigned i = m_stack.size(); i--;) { if (!m_stack[i].isTailDeleted) { highestPointSinceLastTime = m_stack[i].frame; break; } } if (verbose) dataLog(" Highest point since last time: ", RawPointer(highestPointSinceLastTime), "\n"); // Set everything up so that we know where the top frame is in the log. unsigned indexInLog = logCursorIndex; auto advanceIndexInLogTo = [&] (CallFrame* frame, JSObject* callee, CallFrame* callerFrame) -> bool { if (verbose) dataLog(" Advancing to frame = ", RawPointer(frame), " from indexInLog = ", indexInLog, "\n"); if (indexInLog > logCursorIndex) { if (verbose) dataLog(" Bailing.\n"); return false; } unsigned oldIndexInLog = indexInLog; while (indexInLog--) { Packet packet = m_log[indexInLog]; // If all callees opt into ShadowChicken, then this search will rapidly terminate when // we find our frame. But if our frame's callee didn't emit a prologue packet because it // didn't opt in, then we will keep looking backwards until we *might* find a different // frame. If we've been given the callee and callerFrame as a filter, then it's unlikely // that we will hit the wrong frame. But we don't always have that information. // // This means it's worth adding other filters. For example, we could track changes in // stack size. Once we've seen a frame at some height, we're no longer interested in // frames below that height. Also, we can break as soon as we see a frame higher than // the one we're looking for. // FIXME: Add more filters. // https://bugs.webkit.org/show_bug.cgi?id=155685 if (packet.isPrologue() && packet.frame == frame && (!callee || packet.callee == callee) && (!callerFrame || packet.callerFrame == callerFrame)) { if (verbose) dataLog(" Found at indexInLog = ", indexInLog, "\n"); return true; } } // This is an interesting eventuality. We will see this if ShadowChicken was not // consistently enabled. We have a choice between: // // - Leaving the log index at -1, which will prevent the log from being considered. This is // the most conservative. It means that we will not be able to recover tail-deleted frames // from anything that sits above a frame that didn't log a prologue packet. This means // that everyone who creates prologues must log prologue packets. // // - Restoring the log index to what it was before. This prevents us from considering // whether this frame has tail-deleted frames behind it, but that's about it. The problem // with this approach is that it might recover tail-deleted frames that aren't relevant. // I haven't thought about this too deeply, though. // // It seems like the latter option is less harmful, so that's what we do. indexInLog = oldIndexInLog; if (verbose) dataLog(" Didn't find it.\n"); return false; }; Vector<Frame> toPush; StackVisitor::visit( exec, [&] (StackVisitor& visitor) -> StackVisitor::Status { if (visitor->isInlinedFrame()) { // FIXME: Handle inlining. // https://bugs.webkit.org/show_bug.cgi?id=155686 return StackVisitor::Continue; } CallFrame* callFrame = visitor->callFrame(); if (verbose) dataLog(" Examining ", RawPointer(callFrame), "\n"); if (!toPush.isEmpty() && indexInLog < logCursorIndex // This condition protects us from the case where advanceIndexInLogTo didn't find // anything. && m_log[indexInLog].frame == toPush.last().frame) { if (verbose) dataLog(" Going to loop through things with indexInLog = ", indexInLog, " and push-stack top = ", toPush.last(), "\n"); for (;;) { ASSERT(m_log[indexInLog].frame == toPush.last().frame); // Right now the index is pointing at a prologue packet of the last frame that // we pushed. Peek behind that packet to see if there is a tail packet. If there // is one then we know that there is a corresponding prologue packet that will // tell us about a tail-deleted frame. if (!indexInLog) break; Packet lastPacket = m_log[indexInLog - 1]; if (!lastPacket.isTail()) { // Last frame that we recorded was not the outcome of a tail call. So, there // will not be any more deleted frames. // FIXME: We might want to have a filter here. Consider that this was a tail // marker for a tail call to something that didn't log anything. It should // be sufficient to give the tail marker a copy of the caller frame. // https://bugs.webkit.org/show_bug.cgi?id=155687 break; } indexInLog--; // Skip over the tail packet. if (!advanceIndexInLogTo(lastPacket.frame, nullptr, nullptr)) { // We were unable to locate the prologue packet for this tail packet. That's // quite suspect, so give up. break; } Packet packet = m_log[indexInLog]; bool isTailDeleted = true; toPush.append(Frame(packet.callee, packet.frame, isTailDeleted)); } } if (callFrame == highestPointSinceLastTime) { if (verbose) dataLog(" Bailing at ", RawPointer(callFrame), " because it's the highest point since last time.\n"); return StackVisitor::Done; } advanceIndexInLogTo(callFrame, callFrame->callee(), callFrame->callerFrame()); bool isTailDeleted = false; toPush.append(Frame(visitor->callee(), callFrame, isTailDeleted)); return StackVisitor::Continue; }); if (verbose) dataLog(" Pushing: ", listDump(toPush), "\n"); for (unsigned i = toPush.size(); i--;) m_stack.append(toPush[i]); // We want to reset the log. There is a fun corner-case: there could be a tail marker at the end // of this log. We could make that work by setting isTailDeleted on the top of stack, but that // would require more corner cases in the complicated reconciliation code above. That code // already knows how to handle a tail packet at the beginning, so we just leverage that here. if (logCursorIndex && m_log[logCursorIndex - 1].isTail()) { m_log[0] = m_log[logCursorIndex - 1]; m_logCursor = m_log + 1; } else m_logCursor = m_log; if (verbose) dataLog(" After pushing: ", *this, "\n"); // Remove tail frames until the stack is small enough again. const unsigned stackSizeLimit = Options::shadowChickenStackSizeLimit(); if (m_stack.size() > stackSizeLimit) { unsigned dstIndex = 0; unsigned srcIndex = 0; unsigned size = m_stack.size(); while (srcIndex < m_stack.size()) { Frame frame = m_stack[srcIndex++]; if (size > stackSizeLimit && frame.isTailDeleted) { size--; continue; } m_stack[dstIndex++] = frame; } RELEASE_ASSERT(dstIndex == size); m_stack.resize(size); } if (verbose) dataLog(" After clean-up: ", *this, "\n"); }
void ObjectMaterializationData::dump(PrintStream& out) const { out.print("[", listDump(m_properties), "]"); }
void BasicBlock::dumpFooter(PrintStream& out) const { if (successors().size()) out.print(" Successors: ", listDump(successors()), "\n"); }
void SwitchValue::dumpMeta(CommaPrinter& comma, PrintStream& out) const { out.print(comma, "cases = [", listDump(m_values), "]"); }
void StackMaps::dump(PrintStream& out) const { out.print("Constants:[", listDump(constants), "], Records:[", listDump(records), "]"); }
void Stackmap::dump(PrintStream& out) const { out.print( "{reps = ", listDump(m_reps), ", generator = ", RawPointer(m_generator.get()), ", clobbered = ", m_clobbered, ", usedRegisters = ", m_usedRegisters, "}"); }
void ControlValue::dumpMeta(PrintStream& out) const { out.print(listDump(m_successors)); }