1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
|
/*
SPDX-FileCopyrightText: 2012 Sven Brauch <svenbrauch@googlemail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef PDBDEBUGSESSION_H
#define PDBDEBUGSESSION_H
#include <KProcess>
#include <QMutexLocker>
#include <QPointer>
#include <QDebug>
#include "debuggerdebug.h"
#include <debugger/interfaces/idebugsession.h>
#include <debugger/interfaces/ivariablecontroller.h>
#include <debugger/interfaces/ibreakpointcontroller.h>
#include "variable.h"
using namespace KDevelop;
namespace Python {
struct PdbCommand;
class DebugSession : public KDevelop::IDebugSession
{
Q_OBJECT
public:
DebugSession(QStringList program, const QUrl& workingDirectory,
const QString& envProfileName);
~DebugSession() override;
IBreakpointController* breakpointController() const override;
IFrameStackModel* frameStackModel() const override;
/**
* @brief Start the debugger.
**/
void start();
/**
* @brief Adds a command to the queue.
* Commands are processed in the same order they're added.
* If the type of the command added is UserType, automatic updates
* (of local variables, location, ...) will be triggered.
*
* @param cmd The command to enqeue.
**/
void addCommand(PdbCommand* cmd);
/**
* @brief Convenience function, constructs a new UserPdbCommand and enqueues it.
* Use this to enqueue simple commands invoked by user clicks ("next" etc.)
*
* @param cmd What you would type at the debugger command line.
**/
void addSimpleUserCommand(const QString& cmd);
/**
* @brief Convencience function, constructs a new InternalPdbCommand and enqueues it.
* Use this to enqueue simple commands which are needed internally ("where", ...)
*
* @param cmd What you would type at the debugger command line.
**/
void addSimpleInternalCommand(const QString& cmd);
/**
* @brief Interrupt the running program with SIGINT and immediately run the specified command.
* This will also trigger a location update. Program execution will continue immediately after
* the given command has been run!
*
* @param cmd What you would type at the debugger command line, terminated by \n.
**/
void runImmediately(const QString& cmd);
/**
* @brief Constructs commands to add the given breakpoint to the debugger.
*
* @param bp The breakpoint to add
**/
void addBreakpoint(Breakpoint* bp);
/**
* @brief Constructs commands to remove the given breakpoint from the debugger.
*
* @param bp The breakpoint to remove
**/
void removeBreakpoint(Breakpoint* bp);
/**
* @brief Access this session's variable controller
**/
IVariableController* variableController() const override;
/// Those functions just execute the basic debugger commands. They're used when the user
/// clicks the appropriate button.
void stepOut() override;
void stepOverInstruction() override;
void stepInto() override;
void stepIntoInstruction() override;
void stepOver() override;
void jumpToCursor() override;
void runToCursor() override;
void run() override;
void restartDebugger() override;
bool restartAvaliable() const override;
/**
* @brief Interrupt the running program with SIGINT and set the state to PausedState
**/
void interruptDebugger() override;
/**
* @brief Kill the debugger and program being debugged.
* This tries to send a "quit" command, and if the debugger doesn't react to that quickly,
* it'll just kill it.
**/
void stopDebugger() override;
/**
* @brief Kill the debugger process synchronously
**/
void killDebuggerNow() override;
/**
* @brief Gives the debugger state.
* The two main states are "ActiveState" and "PausedState"; the former is given
* if the *user program* is being run by the debugger.
*
* @return :IDebugSession::DebuggerState the current state the debugger is in
**/
IDebugSession::DebuggerState state() const override;
/**
* @brief Change the debugger state, and trigger various events depending on the previous and new state.
* WARNING: Do *not* switch to ActiveState for running internal commands: If
* the location is being updated by "where", no state switching should occur.
* Otherwise, various endless loops might occur because kdevplatform tries auto-
* update various things (like the location, ...)
* State changes should only occur when starting up, shutting down, or on explicit user interaction.
*
* @param state The state to change to.
**/
void setState(IDebugSession::DebuggerState state);
/**
* @brief Enqueue a command which updates the location.
* Run this whenever you enqueue a command which might change the location in the source code
* (like "next" or similar). This is queued, so you can do addSimpleUserCommand("next"); updateLocation();
* without problems.
**/
void updateLocation();
/**
* @brief Clears the table of object IDs stored in the debugger script
**/
void clearObjectTable();
/**
* @brief Write raw data to the debugger process' stdin.
* Remember that you have to terminate your input by "\n" for the debugger to process it.
*
* @param cmd data to write to stdin
**/
void write(const QByteArray& cmd);
public Q_SLOTS:
/**
* @brief Emitted when new data has been received from the debugger process (via stdout)
**/
void dataAvailable();
/**
* @brief Fetch the given variable's value and assign it, and when done call the given callback method.
*
* @param variable Variable object to fetch data for
* @param callback object to call callbackMethod on
* @param callbackMethod method to call when done
**/
void createVariable(Python::Variable* variable, QObject* callback, const char* callbackMethod);
/**
* @brief Check the command queue, and run the next command if it's not empty.
**/
void checkCommandQueue();
/**
* @brief Performs a location update.
* This is used by updateLocation().
**/
void locationUpdateReady(QByteArray data);
void debuggerQuit(int);
Q_SIGNALS:
/// Emitted when the debugger becomes ready to process a new command, i.e. shows its prompt
void debuggerReady();
/// Emitted when a new command is added to the queue
void commandAdded();
/// Emitted when real data from the program is received (needs improvement)
void realDataReceived(QStringList);
void stderrReceived(QStringList);
private:
IBreakpointController* m_breakpointController;
IVariableController* m_variableController;
IFrameStackModel* m_frameStackModel;
KProcess* m_debuggerProcess;
IDebugSession::DebuggerState m_state;
QByteArray m_buffer;
QStringList m_program;
QList<PdbCommand*> m_commandQueue;
const QUrl& m_workingDirectory;
const QString m_envProfileName;
private:
/// objects to notify next
QPointer<QObject> m_nextNotifyObject;
const char* m_nextNotifyMethod;
/// whether the process is busy processing an internal command
bool m_processBusy;
/**
* @brief Set the object to notify when the next command is done processing
**/
void setNotifyNext(QPointer<QObject> object, const char* method);
/**
* @brief Invoke the method given by setNotifyNext, and clear it
**/
void notifyNext();
/**
* @brief Process the next command in the queue.
* WARNING: The queue must be non-empty when this is called.
* If the process is busy doing something else, returns and does nothing.
**/
void processNextCommand();
/**
* @brief Clear the data accumulated in m_buffer.
**/
void clearOutputBuffer();
/**
* @brief Clean up and switch to EndedState after stopping/killing the debugger
**/
void finalizeState();
/// stores whether the data currently received comes from the debugger
/// or the debuggee.
int m_inDebuggerData;
};
/**
* @brief Base class for all Pdb command objects. Those are enqueued in the debug session.
**/
struct PdbCommand {
public:
/// notifyMethod must have a QByteArray argument, which is the
/// output produced by the command.
PdbCommand(QObject* notifyObject, const char* notifyMethod) :
m_notifyObject(notifyObject)
, m_notifyMethod(notifyMethod)
, m_output(QByteArray()) {};
/**
* @brief Implement this method in your sub-class to execute the command in the given session.
* WARNING: The process is already locked and ready when this is called.
* Don't acquire or release any locks or do fancy checking here, just do your business (write data
* to the process, ...). Everything else is handled from outside.
* @param session the debug session to run the command in
**/
virtual void run(DebugSession* session) = 0;
virtual ~PdbCommand() {};
void setOutput(QByteArray output) {
m_output = output;
};
QPointer<QObject> notifyObject() {
return m_notifyObject;
};
const char* notifyMethod() {
return m_notifyMethod;
};
enum Type {
InvalidType,
InternalType,
UserType
};
inline Type type() const {
return m_type;
};
protected:
Type m_type;
QPointer<QObject> m_notifyObject;
const char* m_notifyMethod;
QByteArray m_output;
};
/**
* @brief Base-class for commands which just write a simple piece of text to the debugger command line and read its output.
**/
struct SimplePdbCommand : public PdbCommand {
public:
SimplePdbCommand(QObject* notifyObject, const char* notifyMethod, const QString& command) :
PdbCommand(notifyObject, notifyMethod)
, m_command(command) {
m_type = InvalidType;
};
void run(DebugSession* session) override {
Q_ASSERT(m_command.endsWith(QLatin1Char('\n')) && "command must end with a newline");
qCDebug(KDEV_PYTHON_DEBUGGER) << "running command:" << m_command<< m_notifyMethod;
session->write(m_command.toUtf8());
}
private:
QString m_command;
};
/**
* @brief Represents a command which is invoked by kdevelop to obtain information to display in the UI.
**/
struct InternalPdbCommand : public SimplePdbCommand {
public:
InternalPdbCommand(QObject* notifyObject, const char* notifyMethod, const QString& command) :
SimplePdbCommand(notifyObject, notifyMethod, command) {
m_type = InternalType;
} ;
};
/**
* @brief Represents a command which is invoked by the user by clicking a button.
**/
struct UserPdbCommand : public SimplePdbCommand {
public:
UserPdbCommand(QObject* notifyObject, const char* notifyMethod, const QString& command) :
SimplePdbCommand(notifyObject, notifyMethod, command) {
m_type = UserType;
} ;
};
}
#endif // DEBUGSESSION_H
|