BreakpointProbe* DebugDocument::SetBreakPoint(StatementLocation statement, BREAKPOINT_STATE bps) { ScriptContext* scriptContext = this->utf8SourceInfo->GetScriptContext(); if (scriptContext == nullptr || scriptContext->IsClosed()) { return nullptr; } switch (bps) { default: AssertMsg(FALSE, "Bad breakpoint state"); // Fall thru case BREAKPOINT_DISABLED: case BREAKPOINT_DELETED: { BreakpointProbeList* pBreakpointList = this->GetBreakpointList(); if (pBreakpointList) { ArenaAllocator arena(_u("TemporaryBreakpointList"), scriptContext->GetThreadContext()->GetDebugManager()->GetDiagnosticPageAllocator(), Throw::OutOfMemory); BreakpointProbeList* pDeleteList = this->NewBreakpointList(&arena); pBreakpointList->Map([&statement, scriptContext, pDeleteList](int index, BreakpointProbe * breakpointProbe) { if (breakpointProbe->Matches(statement.function, statement.statement.begin)) { scriptContext->GetDebugContext()->GetProbeContainer()->RemoveProbe(breakpointProbe); pDeleteList->Add(breakpointProbe); } }); pDeleteList->Map([pBreakpointList](int index, BreakpointProbe * breakpointProbe) { pBreakpointList->Remove(breakpointProbe); }); pDeleteList->Clear(); } break; } case BREAKPOINT_ENABLED: { BreakpointProbe* pProbe = Anew(scriptContext->AllocatorForDiagnostics(), BreakpointProbe, this, statement, scriptContext->GetThreadContext()->GetDebugManager()->GetNextBreakpointId()); scriptContext->GetDebugContext()->GetProbeContainer()->AddProbe(pProbe); BreakpointProbeList* pBreakpointList = this->GetBreakpointList(); pBreakpointList->Add(pProbe); return pProbe; break; } } return nullptr; }
BreakpointProbe* DebugDocument::SetBreakPoint_TTDWbpId(int64 bpId, StatementLocation statement) { ScriptContext* scriptContext = this->utf8SourceInfo->GetScriptContext(); BreakpointProbe* pProbe = Anew(scriptContext->AllocatorForDiagnostics(), BreakpointProbe, this, statement, (uint32)bpId); scriptContext->GetDebugContext()->GetProbeContainer()->AddProbe(pProbe); BreakpointProbeList* pBreakpointList = this->GetBreakpointList(); pBreakpointList->Add(pProbe); return pProbe; }
bool ProbeContainer::DispatchExceptionBreakpoint(InterpreterHaltState* pHaltState) { OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchExceptionBreakpoint: start: this=%p, pHaltState=%p\n"), this, pHaltState); bool fSuccess = false; if (!haltCallbackProbe || haltCallbackProbe->IsInClosedState() || debugManager->IsAtDispatchHalt()) { OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchExceptionBreakpoint: not in break mode: pHaltState=%p\n"), pHaltState); // Will not be able to handle multiple break-hits. return fSuccess; } Assert(pHaltState->stopType == STOP_EXCEPTIONTHROW); jsExceptionObject = pHaltState->exceptionObject->GetThrownObject(nullptr); // Will store current offset of the bytecode block. int currentOffset = -1; __try { InitializeLocation(pHaltState, false); OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchExceptionBreakpoint: initialized location: pHaltState=%p, IsInterpreterFrame=%d\n"), pHaltState, pHaltState->IsValid(), pHaltState->topFrame && pHaltState->topFrame->IsInterpreterFrame()); // The ByteCodeReader should be available at this point, but because of possibility of garbled frame, we shouldn't hit AV if (pHaltState->IsValid() && pHaltState->GetFunction()->GetScriptContext()->IsScriptContextInDebugMode()) { #if DBG pHaltState->GetFunction()->MustBeInDebugMode(); #endif // For interpreter frames, change the current location pointer of bytecode block, as it might be pointing to the next statement on the body. // In order to generated proper binding of break on exception to the statement, the bytecode offset needed to be on the same span // of the statement. // For native frames the offset is always current. // Move back a single byte to ensure that it falls under on the same statement. if (pHaltState->topFrame->IsInterpreterFrame()) { currentOffset = pHaltState->GetCurrentOffset(); Assert(currentOffset > 0); pHaltState->SetCurrentOffset(currentOffset - 1); } // an inline breakpoint is being dispatched deactivate other stopping controllers debugManager->stepController.Deactivate(pHaltState); debugManager->asyncBreakController.Deactivate(); pHaltState->GetFunction()->CheckAndRegisterFuncToDiag(pScriptContext); ScriptContext *pTopFuncContext = pHaltState->GetFunction()->GetScriptContext(); // If the top function's context is different from the current context, that means current frame is not alive anymore and breaking here cannot not happen. // So in that case we will consider the top function's context and break on that context. if (pTopFuncContext != pScriptContext) { OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchExceptionBreakpoint: top function's context is different from the current context: pHaltState=%p, haltCallbackProbe=%p\n"), pHaltState, pTopFuncContext->GetDebugContext()->GetProbeContainer()->haltCallbackProbe); if (pTopFuncContext->GetDebugContext()->GetProbeContainer()->haltCallbackProbe) { pTopFuncContext->GetDebugContext()->GetProbeContainer()->haltCallbackProbe->DispatchHalt(pHaltState); fSuccess = true; } } else { haltCallbackProbe->DispatchHalt(pHaltState); fSuccess = true; } } } __finally { // If the next statement has changed, we need to log that to exception object so that it will not try to advance to next statement again. pHaltState->exceptionObject->SetIgnoreAdvanceToNextStatement(IsNextStatementChanged); // Restore the current offset; if (currentOffset != -1 && pHaltState->topFrame->IsInterpreterFrame()) { pHaltState->SetCurrentOffset(currentOffset); } DestroyLocation(); } OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchExceptionBreakpoint: end: pHaltState=%p, fSuccess=%d\n"), pHaltState, fSuccess); return fSuccess; }