CHAKRA_API JsDiagRemoveBreakpoint( _In_ unsigned int breakpointId) { #ifndef ENABLE_SCRIPT_DEBUGGING return JsErrorCategoryUsage; #else return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode { JsrtContext *currentContext = JsrtContext::GetCurrent(); JsrtRuntime* runtime = currentContext->GetRuntime(); ThreadContextScope scope(runtime->GetThreadContext()); if (!scope.IsValid()) { return JsErrorWrongThread; } JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager(); VALIDATE_IS_DEBUGGING(jsrtDebugManager); if (!jsrtDebugManager->RemoveBreakpoint(breakpointId)) { return JsErrorInvalidArgument; } return JsNoError; }); #endif }
CHAKRA_API JsDiagGetBreakOnException( _In_ JsRuntimeHandle runtimeHandle, _Out_ JsDiagBreakOnExceptionAttributes* exceptionAttributes) { #ifndef ENABLE_SCRIPT_DEBUGGING return JsErrorCategoryUsage; #else return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode { VALIDATE_INCOMING_RUNTIME_HANDLE(runtimeHandle); PARAM_NOT_NULL(exceptionAttributes); *exceptionAttributes = JsDiagBreakOnExceptionAttributeNone; JsrtRuntime * runtime = JsrtRuntime::FromHandle(runtimeHandle); JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager(); VALIDATE_IS_DEBUGGING(jsrtDebugManager); *exceptionAttributes = jsrtDebugManager->GetBreakOnException(); return JsNoError; }); #endif }
CHAKRA_API JsDiagRequestAsyncBreak( _In_ JsRuntimeHandle runtimeHandle) { #ifndef ENABLE_SCRIPT_DEBUGGING return JsErrorCategoryUsage; #else return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode { VALIDATE_INCOMING_RUNTIME_HANDLE(runtimeHandle); JsrtRuntime * runtime = JsrtRuntime::FromHandle(runtimeHandle); JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager(); VALIDATE_IS_DEBUGGING(jsrtDebugManager); for (Js::ScriptContext *scriptContext = runtime->GetThreadContext()->GetScriptContextList(); scriptContext != nullptr && !scriptContext->IsClosed(); scriptContext = scriptContext->next) { jsrtDebugManager->EnableAsyncBreak(scriptContext); } return JsNoError; }); #endif }
CHAKRA_API JsDiagSetBreakpoint( _In_ unsigned int scriptId, _In_ unsigned int lineNumber, _In_ unsigned int columnNumber, _Out_ JsValueRef *breakpoint) { #ifndef ENABLE_SCRIPT_DEBUGGING return JsErrorCategoryUsage; #else return ContextAPIWrapper_NoRecord<false>([&](Js::ScriptContext* scriptContext) -> JsErrorCode { PARAM_NOT_NULL(breakpoint); *breakpoint = JS_INVALID_REFERENCE; JsrtContext* currentContext = JsrtContext::GetCurrent(); JsrtRuntime* runtime = currentContext->GetRuntime(); ThreadContextScope scope(runtime->GetThreadContext()); if (!scope.IsValid()) { return JsErrorWrongThread; } VALIDATE_IS_DEBUGGING(runtime->GetJsrtDebugManager()); Js::Utf8SourceInfo* utf8SourceInfo = nullptr; for (Js::ScriptContext* currentScriptContext = runtime->GetThreadContext()->GetScriptContextList(); currentScriptContext != nullptr && utf8SourceInfo == nullptr && !currentScriptContext->IsClosed(); currentScriptContext = currentScriptContext->next) { currentScriptContext->MapScript([&](Js::Utf8SourceInfo* sourceInfo) -> bool { if (sourceInfo->GetSourceInfoId() == scriptId) { utf8SourceInfo = sourceInfo; return true; } return false; }); } if (utf8SourceInfo != nullptr && utf8SourceInfo->HasDebugDocument()) { JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager(); Js::DynamicObject* bpObject = jsrtDebugManager->SetBreakPoint(currentContext->GetScriptContext(), utf8SourceInfo, lineNumber, columnNumber); if(bpObject != nullptr) { *breakpoint = bpObject; return JsNoError; } return JsErrorDiagUnableToPerformAction; } return JsErrorDiagObjectNotFound; }); #endif }
CHAKRA_API JsDiagStopDebugging( _In_ JsRuntimeHandle runtimeHandle, _Out_opt_ void** callbackState) { #ifndef ENABLE_SCRIPT_DEBUGGING return JsErrorCategoryUsage; #else return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode { VALIDATE_INCOMING_RUNTIME_HANDLE(runtimeHandle); if (callbackState != nullptr) { *callbackState = nullptr; } JsrtRuntime * runtime = JsrtRuntime::FromHandle(runtimeHandle); ThreadContext * threadContext = runtime->GetThreadContext(); VALIDATE_RUNTIME_STATE_FOR_START_STOP_DEBUGGING(threadContext); JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager(); VALIDATE_IS_DEBUGGING(jsrtDebugManager); for (Js::ScriptContext *scriptContext = runtime->GetThreadContext()->GetScriptContextList(); scriptContext != nullptr && !scriptContext->IsClosed(); scriptContext = scriptContext->next) { Assert(scriptContext->IsScriptContextInDebugMode()); HRESULT hr; if (FAILED(hr = scriptContext->OnDebuggerDetached())) { Debugger_AttachDetach_unrecoverable_error(hr); // Inconsistent state, we can't continue from here return JsErrorFatal; } Js::DebugContext* debugContext = scriptContext->GetDebugContext(); Js::ProbeContainer* probeContainer = debugContext->GetProbeContainer(); probeContainer->UninstallInlineBreakpointProbe(nullptr); probeContainer->UninstallDebuggerScriptOptionCallback(); jsrtDebugManager->ClearBreakpointDebugDocumentDictionary(); } void* cbState = jsrtDebugManager->GetAndClearCallbackState(); if (callbackState != nullptr) { *callbackState = cbState; } return JsNoError; }); #endif }
// This is called at process detach. // threadcontext created from runtime should not be destroyed in ThreadBoundThreadContext // we should clean them up at process detach only as runtime can be used in other threads // even after the current physical thread was destroyed. // This is called after ThreadBoundThreadContext are cleaned up, so the remaining items // in the globalthreadContext linklist should be for jsrt only. void JsrtRuntime::Uninitialize() { ThreadContext* currentThreadContext = ThreadContext::GetThreadContextList(); ThreadContext* tmpThreadContext; while (currentThreadContext) { Assert(!currentThreadContext->IsScriptActive()); JsrtRuntime* currentRuntime = static_cast<JsrtRuntime*>(currentThreadContext->GetJSRTRuntime()); tmpThreadContext = currentThreadContext; currentThreadContext = currentThreadContext->Next(); currentRuntime->CloseContexts(); RentalThreadContextManager::DestroyThreadContext(tmpThreadContext); HeapDelete(currentRuntime); } }
CHAKRA_API JsDiagGetBreakpoints( _Out_ JsValueRef *breakpoints) { #ifndef ENABLE_SCRIPT_DEBUGGING return JsErrorCategoryUsage; #else return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode { PARAM_NOT_NULL(breakpoints); *breakpoints = JS_INVALID_REFERENCE; JsrtContext *currentContext = JsrtContext::GetCurrent(); Js::JavascriptArray* bpsArray = currentContext->GetScriptContext()->GetLibrary()->CreateArray(); JsrtRuntime * runtime = currentContext->GetRuntime(); ThreadContextScope scope(runtime->GetThreadContext()); if (!scope.IsValid()) { return JsErrorWrongThread; } JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager(); VALIDATE_IS_DEBUGGING(jsrtDebugManager); for (Js::ScriptContext *scriptContext = runtime->GetThreadContext()->GetScriptContextList(); scriptContext != nullptr && !scriptContext->IsClosed(); scriptContext = scriptContext->next) { jsrtDebugManager->GetBreakpoints(&bpsArray, scriptContext); } *breakpoints = bpsArray; return JsNoError; }); #endif }
// This is called at process detach. // threadcontext created from runtime should not be destroyed in ThreadBoundThreadContext // we should clean them up at process detach only as runtime can be used in other threads // even after the current physical thread was destroyed. // This is called after ThreadBoundThreadContext are cleaned up, so the remaining items // in the globalthreadContext linklist should be for jsrt only. void JsrtRuntime::Uninitialize() { ThreadContext* currentThreadContext = ThreadContext::GetThreadContextList(); ThreadContext* tmpThreadContext; while (currentThreadContext) { Assert(!currentThreadContext->IsScriptActive()); JsrtRuntime* currentRuntime = static_cast<JsrtRuntime*>(currentThreadContext->GetJSRTRuntime()); tmpThreadContext = currentThreadContext; currentThreadContext = currentThreadContext->Next(); #ifdef CHAKRA_STATIC_LIBRARY // xplat-todo: Cleanup staticlib shutdown. This only shuts down threads. // Other closing contexts / finalizers having trouble with current // runtime/context. RentalThreadContextManager::DestroyThreadContext(tmpThreadContext); #else currentRuntime->CloseContexts(); RentalThreadContextManager::DestroyThreadContext(tmpThreadContext); HeapDelete(currentRuntime); #endif } }
CHAKRA_API JsDiagSetStepType( _In_ JsDiagStepType stepType) { #ifndef ENABLE_SCRIPT_DEBUGGING return JsErrorCategoryUsage; #else return ContextAPIWrapper_NoRecord<true>([&](Js::ScriptContext * scriptContext) -> JsErrorCode { JsrtContext *currentContext = JsrtContext::GetCurrent(); JsrtRuntime* runtime = currentContext->GetRuntime(); VALIDATE_RUNTIME_IS_AT_BREAK(runtime); JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager(); VALIDATE_IS_DEBUGGING(jsrtDebugManager); if (stepType == JsDiagStepTypeStepIn) { jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_STEP_INTO); } else if (stepType == JsDiagStepTypeStepOut) { jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_STEP_OUT); } else if (stepType == JsDiagStepTypeStepOver) { jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_STEP_OVER); } else if (stepType == JsDiagStepTypeStepBack) { #if ENABLE_TTD ThreadContext* threadContext = runtime->GetThreadContext(); if(!threadContext->IsRuntimeInTTDMode()) { //Don't want to fail hard when user accidentally clicks this so pring message and step forward fprintf(stderr, "Must be in replay mode to use reverse-step - launch with \"--replay-debug\" flag in Node."); jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_STEP_OVER); } else { threadContext->TTDExecutionInfo->SetPendingTTDStepBackMove(); //don't worry about BP suppression because we are just going to throw after we return jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_CONTINUE); } #else return JsErrorInvalidArgument; #endif } else if (stepType == JsDiagStepTypeReverseContinue) { #if ENABLE_TTD ThreadContext* threadContext = runtime->GetThreadContext(); if(!threadContext->IsRuntimeInTTDMode()) { //Don't want to fail hard when user accidentally clicks this so pring message and step forward fprintf(stderr, "Must be in replay mode to use reverse-continue - launch with \"--replay-debug\" flag in Node."); jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_CONTINUE); } else { threadContext->TTDExecutionInfo->SetPendingTTDReverseContinueMove(JsTTDMoveMode::JsTTDMoveScanIntervalForContinue); //don't worry about BP suppression because we are just going to throw after we return jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_CONTINUE); } #else return JsErrorInvalidArgument; #endif } else if (stepType == JsDiagStepTypeContinue) { jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_CONTINUE); } return JsNoError; }); #endif }
CHAKRA_API JsDiagStartDebugging( _In_ JsRuntimeHandle runtimeHandle, _In_ JsDiagDebugEventCallback debugEventCallback, _In_opt_ void* callbackState) { #ifndef ENABLE_SCRIPT_DEBUGGING return JsErrorCategoryUsage; #else return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode { VALIDATE_INCOMING_RUNTIME_HANDLE(runtimeHandle); PARAM_NOT_NULL(debugEventCallback); JsrtRuntime * runtime = JsrtRuntime::FromHandle(runtimeHandle); ThreadContext * threadContext = runtime->GetThreadContext(); VALIDATE_RUNTIME_STATE_FOR_START_STOP_DEBUGGING(threadContext); if (runtime->GetJsrtDebugManager() != nullptr && runtime->GetJsrtDebugManager()->IsDebugEventCallbackSet()) { return JsErrorDiagAlreadyInDebugMode; } // Create the debug object to save callback function and data runtime->EnsureJsrtDebugManager(); JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager(); jsrtDebugManager->SetDebugEventCallback(debugEventCallback, callbackState); if (threadContext->GetDebugManager() != nullptr) { threadContext->GetDebugManager()->SetLocalsDisplayFlags(Js::DebugManager::LocalsDisplayFlags::LocalsDisplayFlags_NoGroupMethods); } for (Js::ScriptContext *scriptContext = runtime->GetThreadContext()->GetScriptContextList(); scriptContext != nullptr && !scriptContext->IsClosed(); scriptContext = scriptContext->next) { Assert(!scriptContext->IsScriptContextInDebugMode()); Js::DebugContext* debugContext = scriptContext->GetDebugContext(); if (debugContext->GetHostDebugContext() == nullptr) { debugContext->SetHostDebugContext(jsrtDebugManager); } HRESULT hr; if (FAILED(hr = scriptContext->OnDebuggerAttached())) { Debugger_AttachDetach_fatal_error(hr); // Inconsistent state, we can't continue from here return JsErrorFatal; } // ScriptContext might get closed in OnDebuggerAttached if (!scriptContext->IsClosed()) { Js::ProbeContainer* probeContainer = debugContext->GetProbeContainer(); probeContainer->InitializeInlineBreakEngine(jsrtDebugManager); probeContainer->InitializeDebuggerScriptOptionCallback(jsrtDebugManager); } } return JsNoError; }); #endif }