void XDebugHookHandler::onFileLoad(Unit* unit) { // Translate the unit filename to match xdebug's internal format String unit_path(const_cast<StringData*>(unit->filepath())); String filename = File::TranslatePath(unit_path); // Loop over all unmatched breakpoints for (auto iter = UNMATCHED.begin(); iter != UNMATCHED.end();) { XDebugBreakpoint& bp = BREAKPOINT_MAP.at(*iter); if (bp.type != BreakType::LINE || bp.fileName != filename) { ++iter; continue; } // If the line is invalid there's not much we can do to inform the client // in the dbgp protocol at this point. php5 xdebug doesn't do anything, // so we just cleanup if (phpAddBreakPointLine(unit, bp.line)) { add_line_breakpoint(*iter, bp, unit); iter = UNMATCHED.erase(iter); } else { BREAKPOINT_MAP.erase(*iter); iter = UNMATCHED.erase(iter); } } }
bool XDebugThreadBreakpoints::updateBreakpointLine(int id, int newLine) { auto iter = BREAKPOINT_MAP.find(id); if (iter == BREAKPOINT_MAP.end()) { return false; } XDebugBreakpoint& bp = iter->second; // Determine if we need to unregister the line auto filepath = bp.unit->filepath()->toCppString(); if (LINE_MAP[filepath].count(bp.line) == 1) { phpRemoveBreakPointLine(bp.unit, bp.line); } // Register the new line bp.line = newLine; LINE_MAP[filepath].insert(std::make_pair(bp.line, id)); phpAddBreakPointLine(bp.unit, bp.line); return true; }
bool XDebugThreadBreakpoints::updateBreakpointLine(int id, int newLine) { auto iter = BREAKPOINT_MAP.find(id); if (iter == BREAKPOINT_MAP.end()) { return false; } auto& bp = iter->second; // Determine if we need to unregister the line. auto filepath = bp.unit->filepath()->toCppString(); if (LINE_MAP[filepath].count(bp.line) == 1) { phpRemoveBreakPointLine(bp.unit, bp.line); } // Register the new line. bp.line = tightestLoc(bp.unit, newLine).line1; assertx(bp.line != -1); LINE_MAP[filepath].emplace(bp.line, id); phpAddBreakPointLine(bp.unit, bp.line); return true; }
// Adding a breakpoint. Returns a unique id for the breakpoint. int XDebugThreadBreakpoints::addBreakpoint(XDebugBreakpoint& bp) { auto const id = s_xdebug_breakpoints->m_nextBreakpointId; // php5 xdebug only accepts multiple breakpoints of the same type for // line breakpoints. A useful addition might be to allow multiple of all // types, but for now, multiple function or exception breakpoints results // in the most recent silently being used (matching php5 xdebug). switch (bp.type) { case BreakType::EXCEPTION: { // Remove duplicates then insert the name auto exceptionName = bp.exceptionName.toCppString(); auto iter = EXCEPTION_MAP.find(exceptionName); if (iter != EXCEPTION_MAP.end()) { XDEBUG_REMOVE_BREAKPOINT(iter->second); } EXCEPTION_MAP[exceptionName] = id; break; } // Attempt to find the unit/line combo case BreakType::LINE: { const Unit* unit = find_unit(bp.fileName); if (unit == nullptr) { UNMATCHED.insert(id); break; } // If the file/line combo is invalid, throw an error if (!phpAddBreakPointLine(unit, bp.line)) { throw XDebugServer::ERROR_BREAKPOINT_INVALID; } add_line_breakpoint(id, bp, unit); break; } // Try to find the breakpoint's function case BreakType::CALL: case BreakType::RETURN: { const Class* cls = nullptr; const Func* func = nullptr; if (bp.className.isNull()) { func = Unit::lookupFunc(bp.funcName.get()); } else { cls = Unit::lookupClass(bp.className.toString().get()); if (cls != nullptr) { func = cls->lookupMethod(bp.funcName.get()); } } // Either add the breakpoint or store it as unmatched. If the class // exists, we can verify that the method is valid. if (func != nullptr) { add_func_breakpoint(id, bp, func); } else if (!bp.className.isNull() && cls != nullptr) { throw XDebugServer::ERROR_BREAKPOINT_INVALID; } else { UNMATCHED.insert(id); } break; } } // Success, store the breakpoint and increment the id. BREAKPOINT_MAP[id] = bp; s_xdebug_breakpoints->m_nextBreakpointId++; return id; }
bool RunToLocationCommand::executeImpl(DebuggerSession* session, folly::dynamic* /*responseMsg*/ ) { folly::dynamic& message = getMessage(); const folly::dynamic& args = tryGetObject(message, "arguments", s_emptyArgs); const folly::dynamic& source = tryGetObject(args, "source", s_emptyArgs); const std::string& filePath = tryGetString(source, "path", ""); if (filePath.empty()) { throw DebuggerCommandException( "Run to location source path not specified." ); } const ClientPreferences& prefs = m_debugger->getClientPreferences(); const std::string& path = File::TranslatePath(String(filePath)).toCppString(); BreakpointManager* bpMgr = session->getBreakpointManager(); int line = tryGetInt(args, "line", -1); if (!prefs.linesStartAt1) { // If client lines start at 0, make them 1 based. line++; } if (line <= 0) { throw DebuggerCommandException( "Invalid continue to line specified." ); } // See if there's already a breakpoint at this file + line. const auto bpIds = bpMgr->getBreakpointIdsByFile(path); for (auto it = bpIds.begin(); it != bpIds.end(); it++) { Breakpoint* bp = bpMgr->getBreakpointById(*it); if (bp->m_line == line) { // There's already a breakpoint installed at the run to location. // Just resume the request thread and let it hit. return true; } } // Find a compilation unit to place a temp bp in. HPHP::String unitPath(path.c_str()); const auto compilationUnit = lookupUnit(unitPath.get(), "", nullptr); if (compilationUnit == nullptr) { throw DebuggerCommandException( "Could not find a loaded compilation unit to run to location in!" ); } std::pair<int, int> runLocation = m_debugger->calibrateBreakpointLineInUnit(compilationUnit, line); if (runLocation.first < 0) { throw DebuggerCommandException( "Could not find a suitable location in the compilation unit to run to!" ); } if (!phpAddBreakPointLine(compilationUnit, runLocation.first)) { throw DebuggerCommandException( "Failed to install temporary breakpoint at location." ); } RequestInfo* ri = m_debugger->getRequestInfo(); ri->m_runToLocationInfo.path = path; ri->m_runToLocationInfo.line = line; // Tell the user where we're running to. Resolving the file path and // calibrating the source line could have modified things a bit. std::string userMsg = "Resuming request "; userMsg += std::to_string(targetThreadId(session)); userMsg += " and running to resolved location "; userMsg += path; userMsg += ":"; userMsg += std::to_string(runLocation.second); m_debugger->sendUserMessage(userMsg.c_str(), DebugTransport::OutputLevelInfo); // Resume only this request thread. return true; }