Warning, file /kdevelop/kdev-python/debugger/debugsession.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2012 Sven Brauch <svenbrauch@googlemail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #ifndef PDBDEBUGSESSION_H
0008 #define PDBDEBUGSESSION_H
0009 
0010 #include <KProcess>
0011 #include <QMutexLocker>
0012 #include <QPointer>
0013 
0014 #include <QDebug>
0015 #include "debuggerdebug.h"
0016 
0017 #include <debugger/interfaces/idebugsession.h>
0018 #include <debugger/interfaces/ivariablecontroller.h>
0019 #include <debugger/interfaces/ibreakpointcontroller.h>
0020 #include "variable.h"
0021 
0022 using namespace KDevelop;
0023 
0024 namespace Python {
0025 
0026 struct PdbCommand;
0027     
0028 class DebugSession : public KDevelop::IDebugSession
0029 {
0030     Q_OBJECT
0031 public:
0032     DebugSession(QStringList program, const QUrl& workingDirectory,
0033                  const QString& envProfileName);
0034     ~DebugSession() override;
0035 
0036     IBreakpointController* breakpointController() const override;
0037     IFrameStackModel* frameStackModel() const override;
0038 
0039     /**
0040      * @brief Start the debugger.
0041      **/
0042     void start();
0043     
0044     /**
0045      * @brief Adds a command to the queue.
0046      * Commands are processed in the same order they're added.
0047      * If the type of the command added is UserType, automatic updates
0048      * (of local variables, location, ...) will be triggered.
0049      *
0050      * @param cmd The command to enqeue.
0051      **/
0052     void addCommand(PdbCommand* cmd);
0053     
0054     /**
0055      * @brief Convenience function, constructs a new UserPdbCommand and enqueues it.
0056      * Use this to enqueue simple commands invoked by user clicks ("next" etc.)
0057      *
0058      * @param cmd What you would type at the debugger command line.
0059      **/
0060     void addSimpleUserCommand(const QString& cmd);
0061     
0062     /**
0063      * @brief Convencience function, constructs a new InternalPdbCommand and enqueues it.
0064      * Use this to enqueue simple commands which are needed internally ("where", ...)
0065      *
0066      * @param cmd What you would type at the debugger command line.
0067      **/
0068     void addSimpleInternalCommand(const QString& cmd);
0069     
0070     /**
0071      * @brief Interrupt the running program with SIGINT and immediately run the specified command.
0072      * This will also trigger a location update. Program execution will continue immediately after
0073      * the given command has been run!
0074      *
0075      * @param cmd What you would type at the debugger command line, terminated by \n.
0076      **/
0077     void runImmediately(const QString& cmd);
0078     
0079     /**
0080      * @brief Constructs commands to add the given breakpoint to the debugger.
0081      *
0082      * @param bp The breakpoint to add
0083      **/
0084     void addBreakpoint(Breakpoint* bp);
0085     
0086     /**
0087      * @brief Constructs commands to remove the given breakpoint from the debugger.
0088      *
0089      * @param bp The breakpoint to remove
0090      **/
0091     void removeBreakpoint(Breakpoint* bp);
0092     
0093     /**
0094      * @brief Access this session's variable controller
0095      **/
0096     IVariableController* variableController() const override;
0097     
0098     /// Those functions just execute the basic debugger commands. They're used when the user
0099     /// clicks the appropriate button.
0100     void stepOut() override;
0101     void stepOverInstruction() override;
0102     void stepInto() override;
0103     void stepIntoInstruction() override;
0104     void stepOver() override;
0105     void jumpToCursor() override;
0106     void runToCursor() override;
0107     void run() override;
0108     void restartDebugger() override;
0109     
0110     bool restartAvaliable() const override;
0111     
0112     /**
0113      * @brief Interrupt the running program with SIGINT and set the state to PausedState
0114      **/
0115     void interruptDebugger() override;
0116     
0117     /**
0118      * @brief Kill the debugger and program being debugged.
0119      * This tries to send a "quit" command, and if the debugger doesn't react to that quickly,
0120      * it'll just kill it.
0121      **/
0122     void stopDebugger() override;
0123 
0124     /**
0125      * @brief Kill the debugger process synchronously
0126      **/
0127     void killDebuggerNow() override;
0128     
0129     /**
0130      * @brief Gives the debugger state.
0131      * The two main states are "ActiveState" and "PausedState"; the former is given
0132      * if the *user program* is being run by the debugger.
0133      * 
0134      * @return :IDebugSession::DebuggerState the current state the debugger is in
0135      **/
0136     IDebugSession::DebuggerState state() const override;
0137     
0138     /**
0139      * @brief Change the debugger state, and trigger various events depending on the previous and new state.
0140      * WARNING: Do *not* switch to ActiveState for running internal commands: If
0141      * the location is being updated by "where", no state switching should occur.
0142      * Otherwise, various endless loops might occur because kdevplatform tries auto-
0143      * update various things (like the location, ...)
0144      * State changes should only occur when starting up, shutting down, or on explicit user interaction.
0145      * 
0146      * @param state The state to change to.
0147      **/
0148     void setState(IDebugSession::DebuggerState state);
0149     
0150     /**
0151      * @brief Enqueue a command which updates the location.
0152      * Run this whenever you enqueue a command which might change the location in the source code
0153      * (like "next" or similar). This is queued, so you can do addSimpleUserCommand("next"); updateLocation();
0154      * without problems.
0155      **/
0156     void updateLocation();
0157     
0158     /**
0159      * @brief Clears the table of object IDs stored in the debugger script
0160      **/
0161     void clearObjectTable();
0162     
0163     /**
0164      * @brief Write raw data to the debugger process' stdin.
0165      * Remember that you have to terminate your input by "\n" for the debugger to process it.
0166      * 
0167      * @param cmd data to write to stdin
0168      **/
0169     void write(const QByteArray& cmd);
0170 
0171 public slots:
0172     /**
0173      * @brief Emitted when new data has been received from the debugger process (via stdout)
0174      **/
0175     void dataAvailable();
0176     /**
0177      * @brief Fetch the given variable's value and assign it, and when done call the given callback method.
0178      *
0179      * @param variable Variable object to fetch data for
0180      * @param callback object to call callbackMethod on
0181      * @param callbackMethod method to call when done
0182      **/
0183     void createVariable(Python::Variable* variable, QObject* callback, const char* callbackMethod);
0184     
0185     /**
0186      * @brief Check the command queue, and run the next command if it's not empty.
0187      **/
0188     void checkCommandQueue();
0189     
0190     /**
0191      * @brief Performs a location update.
0192      * This is used by updateLocation().
0193      **/
0194     void locationUpdateReady(QByteArray data);
0195     void debuggerQuit(int);
0196 
0197 signals:
0198     /// Emitted when the debugger becomes ready to process a new command, i.e. shows its prompt
0199     void debuggerReady();
0200     /// Emitted when a new command is added to the queue
0201     void commandAdded();
0202     /// Emitted when real data from the program is received (needs improvement)
0203     void realDataReceived(QStringList);
0204     void stderrReceived(QStringList);
0205 
0206 private:
0207     IBreakpointController* m_breakpointController;
0208     IVariableController* m_variableController;
0209     IFrameStackModel* m_frameStackModel;
0210     KProcess* m_debuggerProcess;
0211     IDebugSession::DebuggerState m_state;
0212     QByteArray m_buffer;
0213     QStringList m_program;
0214     QList<PdbCommand*> m_commandQueue;
0215     const QUrl& m_workingDirectory;
0216     const QString m_envProfileName;
0217 private:
0218     /// objects to notify next
0219     QPointer<QObject> m_nextNotifyObject;
0220     const char* m_nextNotifyMethod;
0221     /// whether the process is busy processing an internal command
0222     bool m_processBusy;
0223     
0224     /**
0225      * @brief Set the object to notify when the next command is done processing
0226      **/
0227     void setNotifyNext(QPointer<QObject> object, const char* method);
0228     
0229     /**
0230      * @brief Invoke the method given by setNotifyNext, and clear it
0231      **/
0232     void notifyNext();
0233     
0234     /**
0235      * @brief Process the next command in the queue.
0236      * WARNING: The queue must be non-empty when this is called.
0237      * If the process is busy doing something else, returns and does nothing.
0238      **/
0239     void processNextCommand();
0240     
0241     /**
0242      * @brief Clear the data accumulated in m_buffer.
0243      **/
0244     void clearOutputBuffer();
0245 
0246     /**
0247      * @brief Clean up and switch to EndedState after stopping/killing the debugger
0248      **/
0249     void finalizeState();
0250     
0251     /// stores whether the data currently received comes from the debugger
0252     /// or the debuggee.
0253     int m_inDebuggerData;
0254 };
0255 
0256 /**
0257  * @brief Base class for all Pdb command objects. Those are enqueued in the debug session.
0258  **/
0259 struct PdbCommand {
0260 public:
0261     /// notifyMethod must have a QByteArray argument, which is the 
0262     /// output produced by the command.
0263     PdbCommand(QObject* notifyObject, const char* notifyMethod) :
0264       m_notifyObject(notifyObject)
0265     , m_notifyMethod(notifyMethod)
0266     , m_output(QByteArray()) {};
0267     
0268     /**
0269      * @brief Implement this method in your sub-class to execute the command in the given session.
0270      * WARNING: The process is already locked and ready when this is called.
0271      * Don't acquire or release any locks or do fancy checking here, just do your business (write data
0272      * to the process, ...). Everything else is handled from outside.
0273      * @param session the debug session to run the command in
0274      **/
0275     virtual void run(DebugSession* session) = 0;
0276     virtual ~PdbCommand() {};
0277     void setOutput(QByteArray output) {
0278         m_output = output;
0279     };
0280     QPointer<QObject> notifyObject() {
0281         return m_notifyObject;
0282     };
0283     const char* notifyMethod() {
0284         return m_notifyMethod;
0285     };
0286     
0287     enum Type {
0288         InvalidType,
0289         InternalType,
0290         UserType
0291     };
0292     
0293     inline Type type() const {
0294         return m_type;
0295     };
0296 
0297 protected:
0298     Type m_type;
0299     QPointer<QObject> m_notifyObject;
0300     const char* m_notifyMethod;
0301     QByteArray m_output;
0302 };
0303 
0304 /**
0305  * @brief Base-class for commands which just write a simple piece of text to the debugger command line and read its output.
0306  **/
0307 struct SimplePdbCommand : public PdbCommand {
0308 public:
0309     SimplePdbCommand(QObject* notifyObject, const char* notifyMethod, const QString& command) :
0310       PdbCommand(notifyObject, notifyMethod)
0311     , m_command(command) {
0312         m_type = InvalidType;
0313     };
0314     void run(DebugSession* session) override {
0315         Q_ASSERT(m_command.endsWith('\n') && "command must end with a newline");
0316         qCDebug(KDEV_PYTHON_DEBUGGER) << "running command:" << m_command<< m_notifyMethod;
0317         session->write(m_command.toUtf8());
0318     }
0319 private:
0320     QString m_command;
0321 };
0322 
0323 /**
0324  * @brief Represents a command which is invoked by kdevelop to obtain information to display in the UI.
0325  **/
0326 struct InternalPdbCommand : public SimplePdbCommand {
0327 public:
0328     InternalPdbCommand(QObject* notifyObject, const char* notifyMethod, const QString& command) :
0329       SimplePdbCommand(notifyObject, notifyMethod, command) {
0330         m_type = InternalType;
0331     } ;
0332 };
0333 
0334 /**
0335  * @brief Represents a command which is invoked by the user by clicking a button.
0336  **/
0337 struct UserPdbCommand : public SimplePdbCommand {
0338 public:
0339     UserPdbCommand(QObject* notifyObject, const char* notifyMethod, const QString& command) :
0340       SimplePdbCommand(notifyObject, notifyMethod, command) {
0341           m_type = UserType;
0342     } ;
0343 };
0344 
0345 }
0346 
0347 #endif // DEBUGSESSION_H