JSTrapStatus CThreadDebugger::BreakHandler(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* UNUSED(rval), jsval UNUSED(closure), BREAK_SRC breakSrc) { uint line = JS_PCToLineNumber(cx, script, pc); std::string filename(JS_GetScriptFilename(cx, script)); SetIsInBreak(true); SaveCallstack(); SetLastBreakLine(line); SetBreakFileName(filename); *m->m_pLastBreakFrame = NULL; if (breakSrc == BREAK_SRC_INTERRUP) { JS_ClearInterrupt(m->m_pScriptInterface->GetRuntime(), NULL, NULL); JS_SetSingleStepMode(cx, script, false); } if (m->m_pDebuggingServer->GetSettingSimultaneousThreadBreak()) { m->m_pDebuggingServer->SetBreakRequestedByThread(true); } // Wait until the user continues the execution while (1) { DBGCMD nextDbgCmd = GetNextDbgCmd(); while (!m->m_StackInfoRequests.empty()) { StackInfoRequest request = m->m_StackInfoRequests.front(); SaveStackFrameData(request.requestType, request.nestingLevel); SDL_SemPost(request.semaphore); m->m_StackInfoRequests.pop(); } if (nextDbgCmd == DBG_CMD_NONE) { // Wait a while before checking for new m_NextDbgCmd again. // We don't want this loop to take 100% of a CPU core for each thread that is in break mode. // On the other hande we don't want the debugger to become unresponsive. SDL_Delay(100); } else if (nextDbgCmd == DBG_CMD_SINGLESTEP || nextDbgCmd == DBG_CMD_STEPINTO || nextDbgCmd == DBG_CMD_STEPOUT) { JSStackFrame* iter = NULL; *m->m_pLastBreakFrame = JS_FrameIterator(m->m_pScriptInterface->GetContext(), &iter); if (!JS_SetSingleStepMode(cx, script, true)) LOGERROR(L"JS_SetSingleStepMode returned false!"); // TODO: When can this happen? else { if (nextDbgCmd == DBG_CMD_SINGLESTEP) { JS_SetInterrupt(m->m_pScriptInterface->GetRuntime(), StepHandler_, this); break; } else if (nextDbgCmd == DBG_CMD_STEPINTO) { JS_SetInterrupt(m->m_pScriptInterface->GetRuntime(), StepIntoHandler_, this); break; } else if (nextDbgCmd == DBG_CMD_STEPOUT) { JS_SetInterrupt(m->m_pScriptInterface->GetRuntime(), StepOutHandler_, this); break; } } } else if (nextDbgCmd == DBG_CMD_CONTINUE) { if (!JS_SetSingleStepMode(cx, script, true)) LOGERROR(L"JS_SetSingleStepMode returned false!"); // TODO: When can this happen? else { // Setup a handler to check for break-requests from the DebuggingServer regularly JS_SetInterrupt(m->m_pScriptInterface->GetRuntime(), CheckForBreakRequestHandler_, this); } break; } else debug_warn("Invalid DBGCMD found in CThreadDebugger::BreakHandler!"); } ClearTrapsToRemove(); SetAllNewTraps(); SetNextDbgCmd(DBG_CMD_NONE); SetIsInBreak(false); SetBreakFileName(""); // All saved stack data becomes invalid { CScopeLock lock(m->m_Mutex); m->m_StackFrameData.clear(); } return JSTRAP_CONTINUE; }
void* CDebuggingServer::MgDebuggingServerCallback(mg_event event, struct mg_connection *conn, const struct mg_request_info *request_info) { void* handled = (void*)""; // arbitrary non-NULL pointer to indicate successful handling const char* header200 = "HTTP/1.1 200 OK\r\n" "Access-Control-Allow-Origin: *\r\n" // TODO: not great for security "Content-Type: text/plain; charset=utf-8\r\n\r\n"; const char* header404 = "HTTP/1.1 404 Not Found\r\n" "Content-Type: text/plain; charset=utf-8\r\n\r\n" "Unrecognised URI"; switch (event) { case MG_NEW_REQUEST: { std::stringstream stream; std::string uri = request_info->uri; if (uri == "/GetThreadDebuggerStatus") { GetThreadDebuggerStatus(stream); } else if (uri == "/EnumVfsJSFiles") { EnumVfsJSFiles(stream); } else if (uri == "/GetAllCallstacks") { GetAllCallstacks(stream); } else if (uri == "/Continue") { uint threadDebuggerID; if (!GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID)) return handled; // TODO: handle the return value SetNextDbgCmd(threadDebuggerID, DBG_CMD_CONTINUE); } else if (uri == "/Break") { SetBreakRequestedByUser(true); } else if (uri == "/SetSettingSimultaneousThreadBreak") { std::string strEnabled; bool bEnabled = false; if (!GetWebArgs(conn, request_info, "enabled", strEnabled)) return handled; // TODO: handle the return value if (strEnabled == "true") bEnabled = true; else if (strEnabled == "false") bEnabled = false; else return handled; // TODO: return an error state SetSettingSimultaneousThreadBreak(bEnabled); } else if (uri == "/GetSettingSimultaneousThreadBreak") { stream << "{ \"Enabled\" : " << (GetSettingSimultaneousThreadBreak() ? "true" : "false") << " } "; } else if (uri == "/SetSettingBreakOnException") { std::string strEnabled; bool bEnabled = false; if (!GetWebArgs(conn, request_info, "enabled", strEnabled)) return handled; // TODO: handle the return value if (strEnabled == "true") bEnabled = true; else if (strEnabled == "false") bEnabled = false; else return handled; // TODO: return an error state SetSettingBreakOnException(bEnabled); } else if (uri == "/GetSettingBreakOnException") { stream << "{ \"Enabled\" : " << (GetSettingBreakOnException() ? "true" : "false") << " } "; } else if (uri == "/Step") { uint threadDebuggerID; if (!GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID)) return handled; // TODO: handle the return value SetNextDbgCmd(threadDebuggerID, DBG_CMD_SINGLESTEP); } else if (uri == "/StepInto") { uint threadDebuggerID; if (!GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID)) return handled; // TODO: handle the return value SetNextDbgCmd(threadDebuggerID, DBG_CMD_STEPINTO); } else if (uri == "/StepOut") { uint threadDebuggerID; if (!GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID)) return handled; // TODO: handle the return value SetNextDbgCmd(threadDebuggerID, DBG_CMD_STEPOUT); } else if (uri == "/GetStackFrame") { uint nestingLevel; uint threadDebuggerID; if (!GetWebArgs(conn, request_info, "nestingLevel", nestingLevel) || !GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID)) { return handled; } GetStackFrameData(stream, nestingLevel, threadDebuggerID, STACK_INFO_LOCALS); } else if (uri == "/GetStackFrameThis") { uint nestingLevel; uint threadDebuggerID; if (!GetWebArgs(conn, request_info, "nestingLevel", nestingLevel) || !GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID)) { return handled; } GetStackFrameData(stream, nestingLevel, threadDebuggerID, STACK_INFO_THIS); } else if (uri == "/GetCurrentGlobalObject") { uint threadDebuggerID; if (!GetWebArgs(conn, request_info, "threadDebuggerID", threadDebuggerID)) { return handled; } GetStackFrameData(stream, 0, threadDebuggerID, STACK_INFO_GLOBALOBJECT); } else if (uri == "/ToggleBreakpoint") { std::string filename; uint line; if (!GetWebArgs(conn, request_info, "filename", filename) || !GetWebArgs(conn, request_info, "line", line)) { return handled; } ToggleBreakPoint(filename, line); } else if (uri == "/GetFile") { std::string filename; if (!GetWebArgs(conn, request_info, "filename", filename)) return handled; GetFile(filename, stream); } else { mg_printf(conn, "%s", header404); return handled; } mg_printf(conn, "%s", header200); std::string str = stream.str(); mg_write(conn, str.c_str(), str.length()); return handled; } case MG_HTTP_ERROR: return NULL; case MG_EVENT_LOG: // Called by Mongoose's cry() LOGERROR(L"Mongoose error: %hs", request_info->log_message); return NULL; case MG_INIT_SSL: return NULL; default: debug_warn(L"Invalid Mongoose event type"); return NULL; } };