File indexing completed on 2024-04-21 04:36:03

0001 /*
0002    Copyright 2009 Niko Sams <niko.sams@gmail.com>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License version 2 as published by the Free Software Foundation.
0007 
0008    This library is distributed in the hope that it will be useful,
0009    but WITHOUT ANY WARRANTY; without even the implied warranty of
0010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0011    Library General Public License for more details.
0012 
0013    You should have received a copy of the GNU Library General Public License
0014    along with this library; see the file COPYING.LIB.  If not, write to
0015    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0016    Boston, MA 02110-1301, USA.
0017  */
0018 
0019 #include "breakpointcontroller.h"
0020 
0021 #include <QDomElement>
0022 #include <QDebug>
0023 #include <KLocalizedString>
0024 
0025 #include <debugger/breakpoint/breakpoint.h>
0026 
0027 #include "debugsession.h"
0028 #include "connection.h"
0029 #include "debuggerdebug.h"
0030 
0031 namespace XDebug {
0032 BreakpointController::BreakpointController(DebugSession* parent)
0033     : IBreakpointController(parent)
0034 {
0035     connect(debugSession(), &DebugSession::stateChanged, this,
0036             &BreakpointController::stateChanged);
0037 }
0038 
0039 void BreakpointController::sendMaybe(KDevelop::Breakpoint* breakpoint)
0040 {
0041     if (!debugSession()->connection()) {
0042         return;
0043     }
0044 
0045     if (breakpoint->deleted()) {
0046         auto it = m_ids.find(breakpoint);
0047         if (it != m_ids.end()) {
0048             QString cmd("breakpoint_remove");
0049             QStringList args;
0050             args << "-d " + m_ids[breakpoint];
0051             debugSession()->connection()->sendCommand(cmd, args);
0052             m_ids.erase(it);
0053         }
0054     } else if (m_dirty[breakpoint].contains(KDevelop::Breakpoint::LocationColumn)) {
0055         if (breakpoint->enabled()) {
0056             QString cmd = m_ids.contains(breakpoint) ? "breakpoint_update" : "breakpoint_set";
0057             QStringList args;
0058             qCDebug(KDEV_PHP_DEBUGGER) << "breakpoint kind" << breakpoint->kind();
0059             if (breakpoint->kind() == KDevelop::Breakpoint::CodeBreakpoint) {
0060                 if (m_ids.contains(breakpoint)) {
0061                     args << "-d " + m_ids[breakpoint];
0062                 } else if (breakpoint->line() != -1) {
0063                     args << "-t line";
0064                 } else {
0065                     args << "-t call";
0066                 }
0067                 if (breakpoint->line() != -1) {
0068                     QPair<QUrl, int> u = qMakePair(breakpoint->url(), breakpoint->line());
0069                     u = debugSession()->convertToRemoteUrl(u);
0070                     args << "-f " + u.first.url();
0071                     args << "-n " + QString::number(u.second + 1);
0072                 } else {
0073                     args << "-m " + breakpoint->expression();
0074                 }
0075             } else if (breakpoint->kind() == KDevelop::Breakpoint::WriteBreakpoint) {
0076                 args << "-t watch";
0077                 args << "-m " + breakpoint->expression();
0078             } else {
0079                 error(breakpoint, i18n("breakpoint type is not supported"), KDevelop::Breakpoint::LocationColumn);
0080                 return;
0081             }
0082             if (breakpoint->ignoreHits()) {
0083                 args << "-h " + QString::number(breakpoint->ignoreHits() + 1);
0084                 args << "-o >=";
0085             }
0086             CallbackWithCookie<BreakpointController, KDevelop::Breakpoint>* cb =
0087                 new CallbackWithCookie<BreakpointController, KDevelop::Breakpoint>
0088                     (this, &BreakpointController::handleSetBreakpoint, breakpoint, true);
0089             debugSession()->connection()->sendCommand(cmd, args, breakpoint->condition().toUtf8(), cb);
0090         }
0091     } else if (m_dirty[breakpoint].contains(KDevelop::Breakpoint::EnableColumn)) {
0092         Q_ASSERT(m_ids.contains(breakpoint));
0093         QString cmd = "breakpoint_update";
0094         QStringList args;
0095         args << "-d " + m_ids[breakpoint];
0096         args << QString("-s %0").arg(breakpoint->enabled() ? "enabled" : "disabled");
0097         debugSession()->connection()->sendCommand(cmd, args);
0098     }
0099     m_dirty[breakpoint].clear();
0100 }
0101 
0102 void BreakpointController::handleSetBreakpoint(KDevelop::Breakpoint* breakpoint, const QDomDocument& xml)
0103 {
0104     Q_ASSERT(xml.documentElement().attribute("command") == "breakpoint_set"
0105              || xml.documentElement().attribute("command") == "breakpoint_update");
0106     Q_ASSERT(breakpoint);
0107     if (xml.documentElement().attribute("command") == "breakpoint_set") {
0108         m_ids[breakpoint] = xml.documentElement().attribute("id");
0109     }
0110     if (!xml.documentElement().firstChildElement("error").isNull()) {
0111         qCWarning(KDEV_PHP_DEBUGGER) << "breakpoint error" << xml.documentElement().firstChildElement("error").text();
0112         error(breakpoint, xml.documentElement().firstChildElement("error").text(), KDevelop::Breakpoint::LocationColumn);
0113     }
0114 }
0115 
0116 DebugSession* BreakpointController::debugSession()
0117 {
0118     return static_cast<DebugSession*>(KDevelop::IBreakpointController::debugSession());
0119 }
0120 
0121 void BreakpointController::stateChanged(KDevelop::IDebugSession::DebuggerState state)
0122 {
0123     qCDebug(KDEV_PHP_DEBUGGER) << state;
0124     if (state == KDevelop::IDebugSession::StartingState) {
0125         m_ids.clear();
0126         sendMaybeAll();
0127     } else if (state == KDevelop::IDebugSession::PausedState) {
0128         Callback<BreakpointController>* cb =
0129             new Callback<BreakpointController>(this, &BreakpointController::handleBreakpointList);
0130         debugSession()->connection()->sendCommand("breakpoint_list", QStringList(), QByteArray(), cb);
0131     }
0132 }
0133 
0134 void BreakpointController::handleBreakpointList(const QDomDocument& xml)
0135 {
0136     Q_ASSERT(xml.firstChildElement().attribute("command") == "breakpoint_list");
0137 
0138     QDomElement el = xml.documentElement().firstChildElement("breakpoint");
0139     while (!el.isNull()) {
0140         // the breakpoint may have been removed in the meantime, if so, ignore it
0141         if (auto* b = m_ids.key(el.attribute("id"))) {
0142             setHitCount(b, el.attribute("hit_count").toInt());
0143         }
0144         el = el.nextSiblingElement("breakpoint");
0145     }
0146 }
0147 }