void XDebugServer::addCmdAndTrans(xdebug_xml_node& node) { // lastcmd and lasttransid are not always set (for example when the // connection is severed before the first command is sent) if (m_lastCommand == Command::NONE || m_lastTransactionId == nullptr) { return; } // TODO(#4489053) Change this when xml api is changed char* command = const_cast<char*>(getCommandStr(m_lastCommand)); xdebug_xml_add_attribute_ex(&node, "command", command, 0, 0); xdebug_xml_add_attribute_ex(&node, "transaction_id", m_lastTransactionId, 0, 0); }
void XDebugHook::onOpcode(PC pc) { auto server = XDEBUG_GLOBAL(Server); if (server == nullptr) { return; } // Likely case is that there was no break command. auto brk = server->getAndClearBreak(); if (LIKELY(brk == nullptr)) { return; } server->log("Request thread received break command"); VMRegAnchor anchor; auto const unit = vmfp()->func()->unit(); auto const line = unit->getLineNumber(unit->offsetOf(pc)); auto const filepath = const_cast<StringData*>(unit->filepath()); auto const transpath = File::TranslatePath(String(filepath)); // XDebugServer::breakpoint will send the response for the command before the // break command, but we first need to send a response for the break command. auto response = xdebug_xml_node_init("response"); server->addXmlns(*response); auto const& cmd_str = brk->getCommandStr(); auto const& trans_id = brk->getTransactionId(); // Manually add status and reason. XDebugServer still thinks we're running // because we haven't run XDebugServer::breakpoint yet. xdebug_xml_add_attribute(response, "status", "break"); xdebug_xml_add_attribute(response, "reason", "ok"); // Ditto with command, XDebugServer is tracking the command before the break. xdebug_xml_add_attribute_dup(response, "command", cmd_str.data()); xdebug_xml_add_attribute_dup(response, "transaction_id", trans_id.data()); delete brk; server->sendMessage(*response); xdebug_xml_node_dtor(response); // Now we can go into a command loop. server->breakpoint(transpath, init_null(), init_null(), line); }
void XDebugServer::pollSocketLoop() { while (true) { // Take some load off the CPU when polling thread is paused. std::this_thread::sleep_for(std::chrono::seconds(1)); // XXX: This can take the lock even when we want to pause the thread. std::lock_guard<std::mutex> lock(m_pollingMtx); // We're paused if the request thread is handling commands right now. if (m_pausePollingThread.load()) { continue; } // We've been told to exit. if (m_stopPollingThread.load()) { log("Polling thread stopping\n"); break; } // At this point we know that the request thread is busy running, so we let // it enter the jit. m_requestThread->m_reqInjectionData.setDebuggerIntr(false); if (m_bufferAvail == 0) { // Check if there is any input waiting on us. if (!poll_socket(m_socket)) { continue; } // Actually get the input from the socket. if (!readInput()) { log("Polling thread had input but failed to read it\n"); continue; } } try { log("Polling thread received: %s\n", m_bufferCur); auto cmd = parseCommand(); if (cmd->getCommandStr() != "break") { delete cmd; continue; } log("Received break command\n"); // We're going to set the 'break' flag on our XDebugServer, and pause // ourselves. The request thread will unpause us when it exits its // command loop. m_pausePollingThread.store(true); auto const old = m_break.exchange(cmd); always_assert(old == nullptr); // Force request thread to run in the interpreter, and hit // XDebugHookHandler::onOpcode(). m_requestThread->m_reqInjectionData.setDebuggerIntr(true); m_requestThread->m_reqInjectionData.setFlag(DebuggerSignalFlag); } catch (const XDebugExn&) { log("Polling thread got invalid command: %s\n", m_bufferCur); // Can drop the error. } } }