File indexing completed on 2024-03-24 04:38:35
0001 /* 0002 * XDebug Debugger Support 0003 * 0004 * Copyright 2009 Niko Sams <niko.sams@gmail.com> 0005 * 0006 * This program is free software; you can redistribute it and/or modify 0007 * it under the terms of the GNU General Public License as 0008 * published by the Free Software Foundation; either version 2 of the 0009 * License, or (at your option) any later version. 0010 * 0011 * This program is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 * GNU General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU General Public 0017 * License along with this program; if not, write to the 0018 * Free Software Foundation, Inc., 0019 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0020 */ 0021 0022 #include "debugsession.h" 0023 0024 #include <QTime> 0025 #include <QTcpSocket> 0026 #include <QTcpServer> 0027 #include <QFile> 0028 0029 #include <QDebug> 0030 #include <KLocalizedString> 0031 0032 #include <interfaces/ilaunchconfiguration.h> 0033 #include <debugger/util/pathmappings.h> 0034 0035 #include "connection.h" 0036 #include "breakpointcontroller.h" 0037 #include "framestackmodel.h" 0038 #include "variablecontroller.h" 0039 #include "launchconfig.h" 0040 #include "debuggerdebug.h" 0041 0042 namespace XDebug { 0043 DebugSession::DebugSession() 0044 : KDevelop::IDebugSession() 0045 , m_breakpointController(new BreakpointController(this)) 0046 , m_variableController(new VariableController(this)) 0047 , m_frameStackModel(new FrameStackModel(this)) 0048 , m_server(nullptr) 0049 , m_connection(nullptr) 0050 , m_launchConfiguration(nullptr) 0051 , m_acceptMultipleConnections(false) 0052 { 0053 } 0054 0055 DebugSession::~DebugSession() 0056 { 0057 emit finished(); 0058 } 0059 0060 void DebugSession::setLaunchConfiguration(KDevelop::ILaunchConfiguration* cfg) 0061 { 0062 m_launchConfiguration = cfg; 0063 } 0064 0065 void DebugSession::setAcceptMultipleConnections(bool v) 0066 { 0067 m_acceptMultipleConnections = v; 0068 } 0069 0070 bool DebugSession::listenForConnection(QString& error) 0071 { 0072 Q_ASSERT(!m_server); 0073 m_server = new QTcpServer(this); 0074 qCDebug(KDEV_PHP_DEBUGGER); 0075 int remotePortSetting = m_launchConfiguration->config().readEntry("RemotePort", 9000); 0076 if (m_server->listen(QHostAddress::Any, remotePortSetting)) { 0077 connect(m_server, &QTcpServer::newConnection, this, &DebugSession::incomingConnection); 0078 // avoid 'debug launch' button 0079 stateChanged(ActiveState); 0080 } else { 0081 error = i18n("Opening port %1 failed: %2.", remotePortSetting, m_server->errorString()); 0082 qCWarning(KDEV_PHP_DEBUGGER) << "Error" << m_server->errorString(); 0083 delete m_server; 0084 m_server = nullptr; 0085 return false; 0086 } 0087 return m_server->isListening(); 0088 } 0089 0090 void DebugSession::closeServer() 0091 { 0092 if (m_server) { 0093 m_server->close(); 0094 m_server->deleteLater(); 0095 m_server = nullptr; 0096 } 0097 } 0098 0099 void DebugSession::incomingConnection() 0100 { 0101 qCDebug(KDEV_PHP_DEBUGGER); 0102 QTcpSocket* client = m_server->nextPendingConnection(); 0103 0104 if (m_connection) { 0105 m_connection->disconnect(); 0106 m_connection->deleteLater(); 0107 m_connection = nullptr; 0108 } 0109 0110 m_connection = new Connection(client, this); 0111 connect(m_connection, &Connection::output, this, &DebugSession::output); 0112 connect(m_connection, &Connection::outputLine, this, &DebugSession::outputLine); 0113 connect(m_connection, &Connection::stateChanged, this, &DebugSession::stateChanged); 0114 connect(m_connection, &Connection::stateChanged, this, &DebugSession::_stateChanged); 0115 connect(m_connection, &Connection::currentPositionChanged,this, &DebugSession::currentPositionChanged); 0116 connect(m_connection, &Connection::closed, this, &DebugSession::connectionClosed); 0117 0118 if (!m_acceptMultipleConnections) { 0119 closeServer(); 0120 } 0121 } 0122 0123 void DebugSession::connectionClosed() 0124 { 0125 Q_ASSERT(sender() == m_connection); 0126 0127 if (m_acceptMultipleConnections && m_server && m_server->isListening() 0128 ) { 0129 // clear variable widget 0130 emit stateChanged(NotStartedState); 0131 // avoid 'debug launch' button 0132 emit stateChanged(ActiveState); 0133 } else { 0134 m_connection->setState(DebugSession::EndedState); 0135 } 0136 m_connection->deleteLater(); 0137 m_connection = nullptr; 0138 } 0139 0140 void DebugSession::_stateChanged(KDevelop::IDebugSession::DebuggerState state) 0141 { 0142 qCDebug(KDEV_PHP_DEBUGGER) << state; 0143 if (state == StartingState) { 0144 run(); 0145 } else if (state == PausedState) { 0146 raiseEvent(program_state_changed); 0147 } else if (state == StoppingState) { 0148 m_connection->sendCommand("stop"); 0149 } else if (state == EndedState) { 0150 raiseEvent(debugger_exited); 0151 emit finished(); 0152 } 0153 } 0154 0155 DebugSession::DebuggerState DebugSession::state() const 0156 { 0157 if (!m_connection) { 0158 return NotStartedState; 0159 } 0160 return m_connection->currentState(); 0161 } 0162 0163 void DebugSession::run() 0164 { 0165 Q_ASSERT(m_connection); 0166 m_connection->sendCommand("run"); 0167 m_connection->setState(ActiveState); 0168 } 0169 0170 void DebugSession::stepOut() 0171 { 0172 Q_ASSERT(m_connection); 0173 m_connection->sendCommand("step_out"); 0174 m_connection->setState(ActiveState); 0175 } 0176 0177 void DebugSession::stepOverInstruction() 0178 { 0179 } 0180 0181 void DebugSession::stepInto() 0182 { 0183 Q_ASSERT(m_connection); 0184 m_connection->sendCommand("step_into"); 0185 m_connection->setState(ActiveState); 0186 } 0187 0188 void DebugSession::stepIntoInstruction() 0189 { 0190 } 0191 0192 void DebugSession::stepOver() 0193 { 0194 Q_ASSERT(m_connection); 0195 m_connection->sendCommand("step_over"); 0196 m_connection->setState(ActiveState); 0197 } 0198 0199 void DebugSession::jumpToCursor() 0200 { 0201 } 0202 0203 void DebugSession::runToCursor() 0204 { 0205 } 0206 0207 void DebugSession::interruptDebugger() 0208 { 0209 } 0210 0211 void DebugSession::stopDebugger() 0212 { 0213 closeServer(); 0214 // finish debugger when no connection active 0215 if ( state() == DebugSession::NotStartedState && !m_connection ) { 0216 stateChanged(DebugSession::EndedState); 0217 emit finished(); 0218 } else { 0219 m_connection->sendCommand("stop"); 0220 } 0221 } 0222 0223 void DebugSession::killDebuggerNow() 0224 { 0225 closeServer(); 0226 if (m_connection) { 0227 m_connection->close(); 0228 } else { 0229 emit stateChanged(EndedState); 0230 emit finished(); 0231 } 0232 } 0233 0234 void DebugSession::restartDebugger() 0235 { 0236 } 0237 void DebugSession::eval(QByteArray source) 0238 { 0239 Q_ASSERT(m_connection); 0240 m_connection->sendCommand("eval", QStringList(), source); 0241 } 0242 0243 bool DebugSession::waitForFinished(int msecs) 0244 { 0245 QTime stopWatch; 0246 stopWatch.start(); 0247 if (!waitForState(DebugSession::StoppingState, msecs)) { 0248 return false; 0249 } 0250 if (msecs != -1) { 0251 msecs = msecs - stopWatch.elapsed(); 0252 } 0253 return true; 0254 } 0255 0256 bool DebugSession::waitForState(KDevelop::IDebugSession::DebuggerState state, int msecs) 0257 { 0258 if (!m_connection) { 0259 return false; 0260 } 0261 if (m_connection->currentState() == state) { 0262 return true; 0263 } 0264 QTime stopWatch; 0265 stopWatch.start(); 0266 if (!waitForConnected(msecs)) { 0267 return false; 0268 } 0269 while (m_connection->currentState() != state) { 0270 if (!m_connection) { 0271 return false; 0272 } 0273 if (!m_connection->socket()) { 0274 return false; 0275 } 0276 if (!m_connection->socket()->isOpen()) { 0277 return false; 0278 } 0279 m_connection->socket()->waitForReadyRead(100); 0280 if (msecs != -1 && stopWatch.elapsed() > msecs) { 0281 return false; 0282 } 0283 } 0284 return true; 0285 } 0286 0287 bool DebugSession::waitForConnected(int msecs) 0288 { 0289 if (!m_connection) { 0290 Q_ASSERT(m_server); 0291 if (!m_server->waitForNewConnection(msecs)) { 0292 return false; 0293 } 0294 } 0295 Q_ASSERT(m_connection); 0296 Q_ASSERT(m_connection->socket()); 0297 return m_connection->socket()->waitForConnected(msecs); 0298 } 0299 0300 Connection* DebugSession::connection() 0301 { 0302 return m_connection; 0303 } 0304 0305 bool DebugSession::restartAvaliable() const 0306 { 0307 //not supported at all by xdebug 0308 return false; 0309 } 0310 0311 KDevelop::IBreakpointController* DebugSession::breakpointController() const 0312 { 0313 return m_breakpointController; 0314 } 0315 0316 KDevelop::IVariableController* DebugSession::variableController() const 0317 { 0318 return m_variableController; 0319 } 0320 0321 KDevelop::IFrameStackModel* DebugSession::frameStackModel() const 0322 { 0323 return m_frameStackModel; 0324 } 0325 0326 QPair<QUrl, int> DebugSession::convertToLocalUrl(const QPair<QUrl, int>& remoteUrl) const 0327 { 0328 Q_ASSERT(m_launchConfiguration); 0329 QPair<QUrl, int> ret = remoteUrl; 0330 ret.first = KDevelop::PathMappings::convertToLocalUrl(m_launchConfiguration->config(), remoteUrl.first); 0331 return ret; 0332 } 0333 0334 QPair<QUrl, int> DebugSession::convertToRemoteUrl(const QPair<QUrl, int>& localUrl) const 0335 { 0336 Q_ASSERT(m_launchConfiguration); 0337 QPair<QUrl, int> ret = localUrl; 0338 ret.first = KDevelop::PathMappings::convertToRemoteUrl(m_launchConfiguration->config(), localUrl.first); 0339 return ret; 0340 } 0341 0342 void DebugSession::currentPositionChanged(const QUrl& url, int line) 0343 { 0344 setCurrentPosition(url, line, QString()); 0345 } 0346 }