void DebugManager::UpdateConsoleScope(DynamicObject* copyFromScope, ScriptContext* scriptContext) { Assert(copyFromScope != nullptr); DynamicObject* consoleScope = this->GetConsoleScope(scriptContext); Js::RecyclableObject* recyclableObject = Js::RecyclableObject::FromVar(copyFromScope); ulong newPropCount = recyclableObject->GetPropertyCount(); for (ulong i = 0; i < newPropCount; i++) { Js::PropertyId propertyId = recyclableObject->GetPropertyId((Js::PropertyIndex)i); // For deleted properties we won't have a property id if (propertyId != Js::Constants::NoProperty) { Js::PropertyValueInfo propertyValueInfo; Var propertyValue; BOOL gotPropertyValue = recyclableObject->GetProperty(recyclableObject, propertyId, &propertyValue, &propertyValueInfo, scriptContext); AssertMsg(gotPropertyValue, "DebugManager::UpdateConsoleScope Should have got valid value?"); OUTPUT_TRACE(Js::ConsoleScopePhase, _u("Adding property '%s'\n"), scriptContext->GetPropertyName(propertyId)->GetBuffer()); BOOL updateSuccess = consoleScope->SetPropertyWithAttributes(propertyId, propertyValue, propertyValueInfo.GetAttributes(), &propertyValueInfo); AssertMsg(updateSuccess, "DebugManager::UpdateConsoleScope Unable to update property value. Am I missing a scenario?"); } } OUTPUT_TRACE(Js::ConsoleScopePhase, _u("Number of properties on console scope object after update are %d\n"), consoleScope->GetPropertyCount()); }
/** * \brief This function is a callback for the start of an * element during APIVSXML parsing. * * \param[in] pData - Pointer to data structure to fill in * \param[in] pElement - Pointer to element tag * \param[in] pAttr - Pointer to attribute list * \return STATUS_FAIL - Parsing of APIVSXML input file failed * STATUS_PASS - Parsing of APIVSXML input file was successful */ static void inputXmlStartElement(void *pData, const char *pElement, const char **pAttr) { // See if we have just started if (0 == s_depth) { // We are at the top, so the only valid line // is <APIVSXML>. Ensure this is what we got if (STATUS_PASS == pCode_start(pData, pElement, pAttr)) { s_depth++; OUTPUT_TRACE("inputXmlStartElement(): PASS"); } else { OUTPUT_TRACE("inputXmlStartElement(): *** FAIL ***"); } } else { // Parse the opening element if (STATUS_PASS == pCode_parseOpen(pData, pElement, pAttr)) { s_depth++; OUTPUT_TRACE("inputXmlStartElement(): PASS"); } else { OUTPUT_TRACE("inputXmlStartElement(): *** FAIL ***"); } } }
void ProbeContainer::DispatchMutationBreakpoint(InterpreterHaltState* pHaltState) { Assert(pHaltState->stopType == STOP_MUTATIONBREAKPOINT); OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchMutationBreakpoint: start: this=%p, pHaltState=%p\n"), this, pHaltState); if (!CanDispatchHalt(pHaltState)) { return; } // will store Current offset of the bytecode block. int currentOffset = -1; __try { InitializeLocation(pHaltState); OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchMutationBreakpoint: initialized location: pHaltState=%p, pHaltState->IsValid()=%d\n"), pHaltState, pHaltState->IsValid()); if (pHaltState->IsValid()) { // 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 mutation 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); } debugManager->stepController.Deactivate(pHaltState); debugManager->asyncBreakController.Deactivate(); pHaltState->GetFunction()->CheckAndRegisterFuncToDiag(pScriptContext); Assert(pHaltState->GetFunction()->GetScriptContext() == pScriptContext); haltCallbackProbe->DispatchHalt(pHaltState); } } __finally { // Restore the current offset; if (currentOffset != -1 && pHaltState->topFrame->IsInterpreterFrame()) { pHaltState->SetCurrentOffset(currentOffset); } DestroyLocation(); } }
DiagNativeStackFrame::DiagNativeStackFrame( ScriptFunction* function, int byteCodeOffset, void* stackAddr, void *codeAddr, int frameIndex) : DiagStackFrame(frameIndex), m_function(function), m_byteCodeOffset(byteCodeOffset), m_stackAddr(stackAddr), m_localVarSlotsOffset(InvalidOffset), m_localVarChangedOffset(InvalidOffset) { Assert(m_stackAddr != NULL); AssertMsg(m_function && m_function->GetScriptContext() && m_function->GetScriptContext()->IsInDebugMode(), "This only supports functions in debug mode."); FunctionEntryPointInfo * entryPointInfo = GetFunction()->GetEntryPointFromNativeAddress((DWORD_PTR)codeAddr); if (entryPointInfo) { m_localVarSlotsOffset = entryPointInfo->localVarSlotsOffset; m_localVarChangedOffset = entryPointInfo->localVarChangedOffset; } else { AssertMsg(FALSE, "Failed to get entry point for native address. Most likely the frame is old/gone."); } OUTPUT_TRACE(Js::DebuggerPhase, L"DiagNativeStackFrame::DiagNativeStackFrame: e.p(addr %p)=%p varOff=%d changedOff=%d\n", codeAddr, entryPointInfo, m_localVarSlotsOffset, m_localVarChangedOffset); }
void ProbeContainer::DestroyLocation() { OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DestroyLocation (start): this=%p, IsNextStatementChanged=%d, haltCallbackProbe=%p\n"), this, this->IsNextStatementChanged, haltCallbackProbe); if (IsNextStatementChanged) { Assert(bytecodeOffset != debugManager->stepController.byteOffset); // Note: when we dispatching an exception bytecodeOffset would be same as pProbeManager->pCurrentInterpreterLocation->GetCurrentOffset(). debugManager->pCurrentInterpreterLocation->SetCurrentOffset(bytecodeOffset); IsNextStatementChanged = false; } framePointers = nullptr; // Reset the exception object. jsExceptionObject = nullptr; Assert(debugManager); debugManager->UnsetCurrentInterpreterLocation(); pinnedPropertyRecords->Reset(); // Guarding if the probe engine goes away when we are sitting at breakpoint. if (haltCallbackProbe) { // The clean up is called here to scriptengine's object to remove all DebugStackFrames haltCallbackProbe->CleanupHalt(); } }
void ProbeContainer::DispatchStepHandler(InterpreterHaltState* pHaltState, OpCode* pOriginalOpcode) { OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchStepHandler: start: this=%p, pHaltState=%p, pOriginalOpcode=0x%x\n"), this, pHaltState, pOriginalOpcode); if (!CanDispatchHalt(pHaltState)) { return; } __try { InitializeLocation(pHaltState); OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchStepHandler: initialized location: pHaltState=%p, pHaltState->IsValid()=%d\n"), pHaltState, pHaltState->IsValid()); if (pHaltState->IsValid()) // Only proceed if we find a valid top frame and that is the executing function { if (debugManager->stepController.IsStepComplete(pHaltState, haltCallbackProbe, *pOriginalOpcode)) { OpCode oldOpcode = *pOriginalOpcode; pHaltState->GetFunction()->ProbeAtOffset(pHaltState->GetCurrentOffset(), pOriginalOpcode); pHaltState->GetFunction()->CheckAndRegisterFuncToDiag(pScriptContext); debugManager->stepController.Deactivate(pHaltState); haltCallbackProbe->DispatchHalt(pHaltState); if (oldOpcode == OpCode::Break && debugManager->stepController.stepType == STEP_DOCUMENT) { // That means we have delivered the stepping to the debugger, where we had the breakpoint // already, however it is possible that debugger can initiate the step_document. In that // case debugger did not break due to break. So we have break as a breakpoint reason. *pOriginalOpcode = OpCode::Break; } else if (OpCode::Break == *pOriginalOpcode) { debugManager->stepController.stepCompleteOnInlineBreakpoint = true; } } } } __finally { DestroyLocation(); } OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchStepHandler: end: pHaltState=%p\n"), pHaltState); }
void ProbeContainer::AsyncActivate(HaltCallback* haltCallback) { OUTPUT_TRACE(Js::DebuggerPhase, _u("Async break activated\n")); InterlockedExchangePointer((PVOID*)&this->pAsyncHaltCallback, haltCallback); Assert(debugManager); debugManager->asyncBreakController.Activate(haltCallback); }
void ProbeContainer::DispatchInlineBreakpoint(InterpreterHaltState* pHaltState) { OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchInlineBreakpoint: start: this=%p, pHaltState=%p\n"), this, pHaltState); if (!CanDispatchHalt(pHaltState)) { return; } Assert(pHaltState->stopType == STOP_INLINEBREAKPOINT); __try { InitializeLocation(pHaltState); OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchInlineBreakpoint: initialized location: pHaltState=%p, pHaltState->IsValid()=%d\n"), pHaltState, pHaltState->IsValid()); Assert(pHaltState->IsValid()); // The ByteCodeReader should be available at this point, but because of possibility of garbled frame, we shouldn't hit AV if (pHaltState->IsValid()) { #if DBG pHaltState->GetFunction()->MustBeInDebugMode(); #endif // an inline breakpoint is being dispatched deactivate other stopping controllers debugManager->stepController.Deactivate(pHaltState); debugManager->asyncBreakController.Deactivate(); pHaltState->GetFunction()->CheckAndRegisterFuncToDiag(pScriptContext); haltCallbackProbe->DispatchHalt(pHaltState); } } __finally { DestroyLocation(); } OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchInlineBreakpoint: end: pHaltState=%p\n"), pHaltState); }
/** * \brief This function is a callback for the end of an element during APIVSXML * parsing. * * \param[in] pData - Pointer to data structure to fill in * \param[in] pElement - Pointer to element tag * \return STATUS_FAIL - Parsing of APIVSXML input file failed * STATUS_PASS - Parsing of APIVSXML input file was successful */ static void inputXmlEndElement(void *pData, const char *pElement) { // Parse the closing of the element if (STATUS_PASS == pCode_parseClose(pData, pElement)) { s_depth--; OUTPUT_TRACE("inputXmlEndElement(): PASS"); } else { OUTPUT_TRACE("inputXmlEndElement(): *** FAIL ***"); } // See if we are processing the root if (0 == s_depth) { // We are processing what should be the colsing tag for </APIVSXML> pCode_end(pData, pElement); } }
Var CrossSite::ProfileThunk(RecyclableObject* callable, CallInfo callInfo, ...) { JavascriptFunction* function = JavascriptFunction::FromVar(callable); Assert(function->GetTypeId() == TypeIds_Function); Assert(function->GetEntryPoint() == CrossSite::ProfileThunk); RUNTIME_ARGUMENTS(args, callInfo); ScriptContext * scriptContext = function->GetScriptContext(); // It is not safe to access the function body if the script context is not alive. scriptContext->VerifyAliveWithHostContext(!function->IsExternal(), scriptContext->GetThreadContext()->GetPreviousHostScriptContext()); JavascriptMethod entryPoint; FunctionInfo *funcInfo = function->GetFunctionInfo(); TTD_XSITE_LOG(callable->GetScriptContext(), "DefaultOrProfileThunk", callable); #ifdef ENABLE_WASM if (WasmScriptFunction::Is(function)) { AsmJsFunctionInfo* asmInfo = funcInfo->GetFunctionBody()->GetAsmJsFunctionInfo(); Assert(asmInfo); if (asmInfo->IsWasmDeferredParse()) { entryPoint = WasmLibrary::WasmDeferredParseExternalThunk; } else { entryPoint = Js::AsmJsExternalEntryPoint; } } else #endif if (funcInfo->HasBody()) { #if ENABLE_DEBUG_CONFIG_OPTIONS char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; #endif entryPoint = ScriptFunction::FromVar(function)->GetEntryPointInfo()->jsMethod; if (funcInfo->IsDeferred() && scriptContext->IsProfiling()) { // if the current entrypoint is deferred parse we need to update it appropriately for the profiler mode. entryPoint = Js::ScriptContext::GetProfileModeThunk(entryPoint); } OUTPUT_TRACE(Js::ScriptProfilerPhase, _u("CrossSite::ProfileThunk FunctionNumber : %s, Entrypoint : 0x%08X\n"), funcInfo->GetFunctionProxy()->GetDebugNumberSet(debugStringBuffer), entryPoint); } else { entryPoint = ProfileEntryThunk; } return CommonThunk(function, entryPoint, args); }
void ProbeContainer::DispatchAsyncBreak(InterpreterHaltState* pHaltState) { OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchAsyncBreak: start: this=%p, pHaltState=%p\n"), this, pHaltState); if (!this->pAsyncHaltCallback || !CanDispatchHalt(pHaltState)) { return; } __try { InitializeLocation(pHaltState, /* We don't need to match script context, stop at any available script function */ false); OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchAsyncBreak: initialized location: pHaltState=%p, pHaltState->IsValid()=%d\n"), pHaltState, pHaltState->IsValid()); if (pHaltState->IsValid()) { // Activate the current haltCallback with asyncStepController. debugManager->asyncBreakController.Activate(this->pAsyncHaltCallback); if (debugManager->asyncBreakController.IsAtStoppingLocation(pHaltState)) { OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchAsyncBreak: IsAtStoppingLocation: pHaltState=%p\n"), pHaltState); pHaltState->GetFunction()->CheckAndRegisterFuncToDiag(pScriptContext); debugManager->stepController.Deactivate(pHaltState); debugManager->asyncBreakController.DispatchAndReset(pHaltState); } } } __finally { DestroyLocation(); } OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchAsyncBreak: end: pHaltState=%p\n"), pHaltState); }
// // Saves the profile to the WININET cache and returns the bytes written // uint SourceDynamicProfileManager::SaveToProfileCacheAndRelease(SourceContextInfo* info) { uint bytesWritten = 0; #ifdef ENABLE_WININET_PROFILE_DATA_CACHE if(profileDataCache) { if(ShouldSaveToProfileCache(info)) { OUTPUT_TRACE(Js::DynamicProfilePhase, L"Saving profile. Number of functions: %d Url: %s...\n", startupFunctions->Length(), info->url); bytesWritten = SaveToProfileCache(); if(bytesWritten == 0) { OUTPUT_TRACE(Js::DynamicProfilePhase, L"Profile saving FAILED\n"); } } profileDataCache->Release(); profileDataCache = nullptr; } #endif return bytesWritten; }
bool ProbeContainer::InitializeLocation(InterpreterHaltState* pHaltState, bool fMatchWithCurrentScriptContext) { Assert(debugManager); debugManager->SetCurrentInterpreterLocation(pHaltState); ArenaAllocator* pDiagArena = debugManager->GetDiagnosticArena()->Arena(); UpdateFramePointers(fMatchWithCurrentScriptContext); pHaltState->framePointers = framePointers; pHaltState->stringBuilder = Anew(pDiagArena, StringBuilder<ArenaAllocator>, pDiagArena); if (pHaltState->framePointers->Count() > 0) { pHaltState->topFrame = pHaltState->framePointers->Peek(0); } OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::InitializeLocation (end): this=%p, pHaltState=%p, fMatch=%d, topFrame=%p\n"), this, pHaltState, fMatchWithCurrentScriptContext, pHaltState->topFrame); return true; }
void WritePerfHint(PerfHints hint, Js::FunctionBody * functionBody, uint byteCodeOffset /*= Js::Constants::NoByteCodeOffset*/) { Assert(functionBody); Assert(((uint)hint) < _countof(s_perfHintContainer)); PerfHintItem item = s_perfHintContainer[(uint)hint]; int level = CONFIG_FLAG(PerfHintLevel); Assert(level <= (int)PerfHintLevels::VERBOSE); if ((int)item.level <= level) { ULONG lineNumber = functionBody->GetLineNumber(); LONG columnNumber = functionBody->GetColumnNumber(); if (byteCodeOffset != Js::Constants::NoByteCodeOffset) { functionBody->GetLineCharOffset(byteCodeOffset, &lineNumber, &columnNumber, false /*canAllocateLineCache*/); // returned values are 0-based. Adjusting. lineNumber++; columnNumber++; } // We will print the short name. TCHAR shortName[255]; Js::FunctionBody::GetShortNameFromUrl(functionBody->GetSourceName(), shortName, 255); OUTPUT_TRACE(Js::PerfHintPhase, _u("%s : %s {\n Function : %s [%s @ %u, %u]\n Consequences : %s\n Suggestion : %s\n}\n"), item.isNotOptimized ? _u("Not optimized") : _u("Optimized"), item.description, functionBody->GetExternalDisplayName(), shortName, lineNumber, columnNumber, item.consequences, item.suggestion); Output::Flush(); } }
bool XDataAllocator::Alloc(ULONG_PTR functionStart, DWORD functionSize, ushort pdataCount, ushort xdataSize, SecondaryAllocation* allocation) { XDataAllocation* xdata = static_cast<XDataAllocation*>(allocation); Assert(start != nullptr); Assert(current != nullptr); Assert(current >= start); Assert(xdataSize <= XDATA_SIZE); Assert(pdataCount == 1); // Allocate a new xdata entry if((End() - current) >= XDATA_SIZE) { xdata->address = current; current += XDATA_SIZE; } // try allocating from the free list else if(freeList) { auto entry = freeList; xdata->address = entry->address; this->freeList = entry->next; HeapDelete(entry); } else { xdata->address = nullptr; OUTPUT_TRACE(Js::XDataAllocatorPhase, _u("No space for XDATA.\n")); } #ifndef _WIN32 if (xdata->address) { ClearHead(xdata->address); // mark empty .eh_frame } #endif return xdata->address != nullptr; }
// // Log loop body load event to VTune // void VTuneChakraProfile::LogLoopBodyLoadEvent(Js::FunctionBody* body, Js::LoopHeader* loopHeader, Js::LoopEntryPointInfo* entryPoint, uint16 loopNumber) { #if ENABLE_NATIVE_CODEGEN if (isJitProfilingActive) { iJIT_Method_Load methodInfo; memset(&methodInfo, 0, sizeof(iJIT_Method_Load)); const char16* methodName = body->GetExternalDisplayName(); size_t methodLength = wcslen(methodName); methodLength = min(methodLength, (size_t)UINT_MAX); // Just truncate if it is too big size_t length = methodLength * 3 + /* spaces */ 2 + _countof(LoopStr) + /*size of loop number*/ 10 + /*NULL*/ 1; utf8char_t* utf8MethodName = HeapNewNoThrowArray(utf8char_t, length); if(utf8MethodName) { methodInfo.method_id = iJIT_GetNewMethodID(); size_t len = utf8::EncodeInto(utf8MethodName, methodName, (charcount_t)methodLength); sprintf_s((char*)(utf8MethodName + len), length - len," %s %d", LoopStr, loopNumber + 1); methodInfo.method_name = (char*)utf8MethodName; methodInfo.method_load_address = (void*)entryPoint->GetNativeAddress(); methodInfo.method_size = (uint)entryPoint->GetCodeSize(); // Size in memory - Must be exact size_t urlLength = 0; utf8char_t* utf8Url = GetUrl(body, &urlLength); methodInfo.source_file_name = (char*)utf8Url; iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, &methodInfo); OUTPUT_TRACE(Js::ProfilerPhase, _u("Loop body load event: %s Loop %d\n"), methodName, loopNumber + 1); if(urlLength > 0) { HeapDeleteArray(urlLength, utf8Url); } HeapDeleteArray(length, utf8MethodName); } } #endif }
void ProbeContainer::UpdateFramePointers(bool fMatchWithCurrentScriptContext) { ArenaAllocator* pDiagArena = debugManager->GetDiagnosticArena()->Arena(); framePointers = Anew(pDiagArena, DiagStack, pDiagArena); JavascriptStackWalker walker(pScriptContext, !fMatchWithCurrentScriptContext, nullptr/*returnAddress*/, true/*forceFullWalk*/); DiagStack* tempFramePointers = Anew(pDiagArena, DiagStack, pDiagArena); const bool isLibraryFrameEnabledDebugger = IsLibraryStackFrameSupportEnabled(); walker.WalkUntil([&](JavascriptFunction* func, ushort frameIndex) -> bool { if (isLibraryFrameEnabledDebugger || !func->IsLibraryCode()) { DiagStackFrame* frm = nullptr; InterpreterStackFrame *interpreterFrame = walker.GetCurrentInterpreterFrame(); ScriptContext* frameScriptContext = walker.GetCurrentScriptContext(); Assert(frameScriptContext); if (!fMatchWithCurrentScriptContext && !frameScriptContext->IsScriptContextInDebugMode() && tempFramePointers->Count() == 0) { // this means the top frame is not in the debug mode. We shouldn't be stopping for this break. // This could happen if the exception happens on the diagnosticsScriptEngine. return true; } // Ignore frames which are not in debug mode, which can happen when diag engine calls into user engine under debugger // -- topmost frame is under debugger but some frames could be in non-debug mode as they are from diag engine. if (frameScriptContext->IsScriptContextInDebugMode() && (!fMatchWithCurrentScriptContext || frameScriptContext == pScriptContext)) { if (interpreterFrame) { frm = Anew(pDiagArena, DiagInterpreterStackFrame, interpreterFrame, frameIndex); } else { #if ENABLE_NATIVE_CODEGEN if (func->IsScriptFunction()) { frm = Anew(pDiagArena, DiagNativeStackFrame, ScriptFunction::FromVar(walker.GetCurrentFunction()), walker.GetByteCodeOffset(), walker.GetCurrentArgv(), walker.GetCurrentCodeAddr(), frameIndex); } else #else Assert(!func->IsScriptFunction()); #endif { frm = Anew(pDiagArena, DiagRuntimeStackFrame, func, walker.GetCurrentNativeLibraryEntryName(), walker.GetCurrentArgv(), frameIndex); } } } if (frm) { tempFramePointers->Push(frm); } } return false; }); OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::UpdateFramePointers: detected %d frames (this=%p, fMatchWithCurrentScriptContext=%d)\n"), tempFramePointers->Count(), this, fMatchWithCurrentScriptContext); while (tempFramePointers->Count()) { framePointers->Push(tempFramePointers->Pop()); } }
// // Log JIT method native load event to VTune // void VTuneChakraProfile::LogMethodNativeLoadEvent(Js::FunctionBody* body, Js::FunctionEntryPointInfo* entryPoint) { #if ENABLE_NATIVE_CODEGEN if (isJitProfilingActive) { iJIT_Method_Load methodInfo; memset(&methodInfo, 0, sizeof(iJIT_Method_Load)); const char16* methodName = body->GetExternalDisplayName(); // Append function line number info to method name so that VTune can distinguish between polymorphic methods char16 methodNameBuffer[_MAX_PATH]; ULONG lineNumber = body->GetLineNumber(); char16 numberBuffer[20]; _ltow_s(lineNumber, numberBuffer, 10); wcscpy_s(methodNameBuffer, methodName); if (entryPoint->GetJitMode() == ExecutionMode::SimpleJit) { wcscat_s(methodNameBuffer, _u(" Simple")); } wcscat_s(methodNameBuffer, _u(" {line:")); wcscat_s(methodNameBuffer, numberBuffer); wcscat_s(methodNameBuffer, _u("}")); size_t methodLength = wcslen(methodNameBuffer); Assert(methodLength < _MAX_PATH); size_t length = methodLength * 3 + 1; utf8char_t* utf8MethodName = HeapNewNoThrowArray(utf8char_t, length); if (utf8MethodName) { methodInfo.method_id = iJIT_GetNewMethodID(); utf8::EncodeIntoAndNullTerminate(utf8MethodName, methodNameBuffer, (charcount_t)methodLength); methodInfo.method_name = (char*)utf8MethodName; methodInfo.method_load_address = (void*)entryPoint->GetNativeAddress(); methodInfo.method_size = (uint)entryPoint->GetCodeSize(); // Size in memory - Must be exact LineNumberInfo numberInfo[1]; uint lineCount = (entryPoint->GetNativeOffsetMapCount()) * 2 + 1; // may need to record both .begin and .end for all elements LineNumberInfo* pLineInfo = HeapNewNoThrowArray(LineNumberInfo, lineCount); if (pLineInfo == NULL || Js::Configuration::Global.flags.DisableVTuneSourceLineInfo) { // resort to original implementation, attribute all samples to first line numberInfo[0].LineNumber = lineNumber; numberInfo[0].Offset = 0; methodInfo.line_number_size = 1; methodInfo.line_number_table = numberInfo; } else { int size = entryPoint->PopulateLineInfo(pLineInfo, body); methodInfo.line_number_size = size; methodInfo.line_number_table = pLineInfo; } size_t urlLength = 0; utf8char_t* utf8Url = GetUrl(body, &urlLength); methodInfo.source_file_name = (char*)utf8Url; OUTPUT_TRACE(Js::ProfilerPhase, _u("Method load event: %s\n"), methodNameBuffer); iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, &methodInfo); HeapDeleteArray(lineCount, pLineInfo); if (urlLength > 0) { HeapDeleteArray(urlLength, utf8Url); } HeapDeleteArray(length, utf8MethodName); } } #endif }
bool StepController::IsStepComplete(InterpreterHaltState* haltState, HaltCallback * haltCallback, OpCode originalOpcode) { int currentFrameCount = haltState->framePointers->Count(); AssertMsg(currentFrameCount > 0, "In IsStepComplete we must have at least one frame."); FunctionBody* body = haltState->framePointers->Peek()->GetJavascriptFunction()->GetFunctionBody(); bool canPossiblyHalt = haltCallback->CanHalt(haltState); OUTPUT_TRACE(Js::DebuggerPhase, _u("StepController::IsStepComplete(): stepType = %d "), stepType); uint scriptId = GetScriptId(body); AssertMsg(scriptId != InvalidScriptId, "scriptId cannot be 'invalid-reserved'"); int byteOffset = haltState->GetCurrentOffset(); bool fCanHalt = false; if (this->frameCountWhenSet > currentFrameCount && STEP_DOCUMENT != stepType) { // all steps match once the frame they started on has popped. fCanHalt = canPossiblyHalt; } else if (STEP_DOCUMENT == stepType) { OUTPUT_TRACE(Js::DebuggerPhase, _u("StepController::IsStepComplete(): docId when set=%d, currentDocId = %d, can Halt = %d, will halt = %d "), this->scriptIdWhenSet, scriptId, canPossiblyHalt, fCanHalt); fCanHalt = (scriptId != this->scriptIdWhenSet) && canPossiblyHalt; } else if (STEP_IN != stepType && this->frameCountWhenSet < currentFrameCount) { // Only step into allows the stack to be deeper OUTPUT_TRACE(Js::DebuggerPhase, _u("StepController::IsStepComplete(stepType = %d) returning false "), stepType); return false; } else if (STEP_OUT == stepType) { fCanHalt = this->frameCountWhenSet > currentFrameCount && canPossiblyHalt; } else if (nullptr != this->statementMap && this->statementMap->isSubexpression && STEP_IN != stepType) { // Only step into started from subexpression is allowed to stop on another subexpression Js::FunctionBody* pCurrentFuncBody = haltState->GetFunction(); Js::FunctionBody::StatementMap* map = pCurrentFuncBody->GetMatchingStatementMapFromByteCode(byteOffset, false); if (nullptr != map && map->isSubexpression) // Execute remaining Subexpressions { fCanHalt = false; } else { Js::FunctionBody::StatementMap* outerMap = pCurrentFuncBody->GetMatchingStatementMapFromByteCode(this->statementMap->byteCodeSpan.begin, true); if (nullptr != outerMap && map == outerMap) // Execute the rest of current regular statement { fCanHalt = false; } else { fCanHalt = canPossiblyHalt; } } } else { // Match if we are no longer on the original statement. Stepping means move off current statement. if (body != this->body || NULL == this->statementMap || !this->statementMap->byteCodeSpan.Includes(byteOffset)) { fCanHalt = canPossiblyHalt; } } // At this point we are verifying of global return opcode. // The global returns are alway added as a zero range begin with zero. if (fCanHalt && originalOpcode == OpCode::Ret) { Js::FunctionBody* pCurrentFuncBody = haltState->GetFunction(); Js::FunctionBody::StatementMap* map = pCurrentFuncBody->GetMatchingStatementMapFromByteCode(byteOffset, true); fCanHalt = !FunctionBody::IsDummyGlobalRetStatement(&map->sourceSpan); if (fCanHalt) { // We are breaking at last line of function, imagine '}' AddReturnToReturnedValueContainer(); } } OUTPUT_TRACE(Js::DebuggerPhase, _u("StepController::IsStepComplete(stepType = %d) returning %d "), stepType, fCanHalt); return fCanHalt; }
// // Loads the profile from the WININET cache // bool SourceDynamicProfileManager::LoadFromProfileCache(IActiveScriptDataCache* profileDataCache, LPCWSTR url) { #ifdef ENABLE_WININET_PROFILE_DATA_CACHE AssertMsg(CONFIG_FLAG(WininetProfileCache), "Profile caching should be enabled for us to get here"); Assert(profileDataCache); AssertMsg(!IsProfileLoadedFromWinInet(), "Duplicate profile cache loading?"); // Keep a copy of this and addref it profileDataCache->AddRef(); this->profileDataCache = profileDataCache; IStream* readStream; HRESULT hr = profileDataCache->GetReadDataStream(&readStream); if(SUCCEEDED(hr)) { Assert(readStream != nullptr); // stream reader owns the stream and will close it on destruction SimpleStreamReader streamReader(readStream); DWORD jscriptMajorVersion; DWORD jscriptMinorVersion; if(FAILED(AutoSystemInfo::GetJscriptFileVersion(&jscriptMajorVersion, &jscriptMinorVersion))) { return false; } DWORD majorVersion; if(!streamReader.Read(&majorVersion) || majorVersion != jscriptMajorVersion) { return false; } DWORD minorVersion; if(!streamReader.Read(&minorVersion) || minorVersion != jscriptMinorVersion) { return false; } uint numberOfFunctions; if(!streamReader.Read(&numberOfFunctions) || numberOfFunctions > MAX_FUNCTION_COUNT) { return false; } BVFixed* functions = BVFixed::New(numberOfFunctions, this->recycler); if(!streamReader.ReadArray(functions->GetData(), functions->WordCount())) { return false; } this->cachedStartupFunctions = functions; OUTPUT_TRACE(Js::DynamicProfilePhase, L"Profile load succeeded. Function count: %d %s\n", numberOfFunctions, url); #if DBG_DUMP if(PHASE_TRACE1(Js::DynamicProfilePhase) && Js::Configuration::Global.flags.Verbose) { OUTPUT_VERBOSE_TRACE(Js::DynamicProfilePhase, L"Profile loaded:\n"); functions->Dump(); } #endif return true; } else if (hr == HRESULT_FROM_WIN32(ERROR_WRITE_PROTECT)) { this->isNonCachableScript = true; OUTPUT_VERBOSE_TRACE(Js::DynamicProfilePhase, L"Profile load failed. Non-cacheable resource. %s\n", url); } else { OUTPUT_TRACE(Js::DynamicProfilePhase, L"Profile load failed. No read stream. %s\n", url); } #endif return false; }
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; }