File indexing completed on 2024-05-12 04:39:43

0001 /*
0002     SPDX-FileCopyrightText: 1999 John Birch <jbb@kdevelop.org>
0003     SPDX-FileCopyrightText: 2016 Aetf <aetf@unlimitedcodeworks.xyz>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #ifndef _MICOMMAND_H_
0009 #define _MICOMMAND_H_
0010 
0011 #include "mi/mi.h"
0012 
0013 #include <QObject>
0014 #include <QString>
0015 #include <QStringList>
0016 #include <QPointer>
0017 
0018 #include <functional>
0019 
0020 namespace KDevMI {
0021 
0022 class MIDebugSession;
0023 
0024 namespace MI {
0025 
0026 class VarItem;
0027 class ValueCallback;
0028 
0029 enum CommandFlag {
0030     /// The command handler also wishes to receive an error responses, overriding the default error handler
0031     CmdHandlesError = 1 << 0,
0032 
0033     /// The command is expected to cause the inferior to run. Controllers that display the
0034     /// program's state should refrain from sending commands while a command with this flag
0035     /// is currently pending; however, note that a command with this flag needn't be guaranteed
0036     /// to lead to a running state.
0037     CmdMaybeStartsRunning = 1 << 1,
0038 
0039     /// The command is a temporary-run type command, meaning that it typically causes the program
0040     /// to run, but only for a short time before it stops again (e.g. Step and StepInto-type
0041     /// commands). When the program is running due to this type of command, a CmdImmediately
0042     /// command will wait before forcing an interrupt of the debugger, and the program is _not_
0043     /// automatically restarted if an interrupt was forced.
0044     ///
0045     /// TODO: this special handling has not actually been implemented yet
0046     CmdTemporaryRun = 1 << 2,
0047 
0048     /// This command should be executed immediately, even if the program is currently running
0049     /// (e.g. breakpoint setting and modification); however, if the program is interrupted,
0050     /// it should be resumed after this command has run.
0051     CmdImmediately = 1 << 3,
0052 
0053     /// This is a command that should interrupt a running program, without resuming.
0054     CmdInterrupt = 1 << 4,
0055 };
0056 Q_DECLARE_FLAGS(CommandFlags, CommandFlag)
0057 Q_DECLARE_OPERATORS_FOR_FLAGS(CommandFlags)
0058 
0059 //base class for handlers
0060 class MICommandHandler
0061 {
0062 public:
0063     virtual ~MICommandHandler() {}
0064     virtual void handle(const ResultRecord&) = 0;
0065     virtual bool handlesError() { return false; }
0066 
0067     /**
0068      * If the handler object should be deleted after the handle() call.
0069      */
0070     virtual bool autoDelete() { return true; }
0071 };
0072 
0073 class FunctionCommandHandler : public MICommandHandler {
0074 public:
0075     using Function = std::function<void (const ResultRecord&)>;
0076 
0077     explicit FunctionCommandHandler(const Function& callback, CommandFlags flags = {});
0078 
0079     void handle(const ResultRecord&) override;
0080     bool handlesError() override;
0081 
0082 private:
0083     CommandFlags _flags;
0084     Function _callback;
0085 };
0086 
0087 /**
0088  * @author John Birch
0089  */
0090 
0091 class MICommand
0092 {
0093 protected:
0094     explicit MICommand(CommandType type, const QString& arguments = QString(), CommandFlags flags = {});
0095     friend class KDevMI::MIDebugSession;
0096 
0097 public:
0098 
0099     virtual ~MICommand();
0100 
0101     CommandType type() const;
0102     virtual QString miCommand() const;
0103 
0104     CommandFlags flags() const {return flags_;}
0105 
0106     /**
0107      * Returns the MI token with which the command is sent, allowing the parser to match up
0108      * the result message with the command.
0109      */
0110     uint32_t token() const {return token_;}
0111 
0112     /**
0113      * Set the MI token. This is done by \ref MICommandQueue.
0114      */
0115     void setToken(uint32_t token) {token_ = token;}
0116 
0117     /**
0118      * Returns the thread that needs to be currently selected when this command is executed,
0119      * or -1 if there is no requirement.
0120      */
0121     int thread() const;
0122 
0123     /**
0124      * Set the thread required to be currently selected when the command is executed.
0125      */
0126     void setThread(int thread);
0127 
0128     /**
0129      * Returns the frame that needs to be currently selected when this command is executed,
0130      * or -1 if there is no requirement.
0131      */
0132     int frame() const;
0133 
0134     /**
0135      * Set the frame required to be currently selected when the command is executed.
0136      */
0137     void setFrame(int frame);
0138 
0139     /**
0140      * Sets the handler for results.
0141      *
0142      * The command object assumes ownership of @p handler.
0143      */
0144     void setHandler(MICommandHandler* handler);
0145     void setHandler(const FunctionCommandHandler::Function &callback);
0146 
0147     template<class Handler>
0148     void setHandler(Handler* handler_this, void (Handler::* handler_method)(const ResultRecord&));
0149 
0150     /* The command that should be sent to debugger.
0151        This method is virtual so the command can compute this
0152        dynamically, possibly using results of the previous
0153        commands.
0154        If the empty string is returned, nothing is sent. */
0155     virtual QString cmdToSend();
0156 
0157     /* Returns the initial string that was specified in
0158        ctor invocation. The actual command will be
0159        determined by cmdToSend above and the return
0160        value of this method is only used in various
0161        diagnostic messages emitted before actually
0162        sending the command. */
0163     QString initialString() const;
0164 
0165     /* Returns true if this is command entered by the user
0166        and so should be always shown in the gdb output window. */
0167     virtual bool isUserCommand() const;
0168 
0169     // If there's a handler for this command, invokes it and returns true.
0170     // Otherwise, returns false.
0171     bool invokeHandler(const ResultRecord& r);
0172 
0173     // Returns 'true' if 'invokeHandler' should be invoked even
0174     // on MI errors.
0175     bool handlesError() const;
0176 
0177     // Called by debuggercontroller for each new output string
0178     // debugger emits for this command. In MI mode, this includes
0179     // all "stream" messages, but does not include MI responses.
0180     void newOutput(const QString&);
0181 
0182     const QStringList& allStreamOutput() const;
0183 
0184     QString command() const;
0185 
0186     void setStateReloading(bool f);
0187 
0188     bool stateReloading() const;
0189 
0190     /// Called when the command has been enqueued in the debug session
0191     /// and the command is wait for being submitted to GDB.
0192     void markAsEnqueued();
0193 
0194     /// Called when the command has been submitted to GDB and the command
0195     /// waits for completion by GDB.
0196     void markAsSubmitted();
0197 
0198     /// Called when the command has been completed and the response has arrived.
0199     void markAsCompleted();
0200 
0201     /// returns the amount of time (in ms) passed between submission and completion.
0202     qint64 gdbProcessingTime() const;
0203 
0204     /// returns the amount of time (in ms) passed between enqueuing and submission.
0205     qint64 queueTime() const;
0206 
0207     /// returns the amount of time (in ms) passed between enqueuing and completion.
0208     qint64 totalProcessingTime() const;
0209 
0210 protected:
0211     CommandType type_;
0212     CommandFlags flags_;
0213     uint32_t token_ = 0;
0214     QString command_;
0215     MICommandHandler *commandHandler_;
0216     QStringList lines;
0217     bool stateReloading_;
0218 
0219     int m_thread;
0220     int m_frame;
0221     // remember the timestamps (in ms since start of the epoch) when this command
0222     // - was added to the command queue (enqueued)
0223     // - was submitted to GDB
0224     // - was completed; response from GDB arrived
0225     qint64 m_enqueueTimestamp;
0226     qint64 m_submitTimestamp;
0227     qint64 m_completeTimestamp;
0228 };
0229 
0230 class UserCommand : public MICommand
0231 {
0232 public:
0233     UserCommand(CommandType type, const QString& s);
0234 
0235     bool isUserCommand() const override;
0236 };
0237 
0238 /** This is a class for raw CLI commands. Instead of invoking
0239     user provided hook with MI response, it invokes the a hook
0240     with lists of strings.
0241 */
0242 class CliCommand : public MICommand
0243 {
0244 public:
0245     template<class Handler>
0246     CliCommand(CommandType type, const QString& command,
0247                Handler* handler_this,
0248                void (Handler::* handler_method)(const QStringList&),
0249                CommandFlags flags = {});
0250 };
0251 
0252 /** Command that does nothing and can be just used to invoke
0253     a user provided handler when all preceding commands are
0254     executed.
0255 */
0256 class SentinelCommand : public MICommand
0257 {
0258 public:
0259     using Function = std::function<void ()>;
0260 
0261     template<class Handler>
0262     SentinelCommand(Handler* handler_this,
0263                     void (Handler::* handler_method)(),
0264                     CommandFlags flags = {})
0265         : MICommand(NonMI, QString(), flags)
0266     {
0267         QPointer<Handler> guarded_this(handler_this);
0268         handler = [guarded_this, handler_method]() {
0269             if (guarded_this) {
0270                 (guarded_this.data()->*handler_method)();
0271             }
0272         };
0273     }
0274 
0275     explicit SentinelCommand(const Function& handler, CommandFlags flags = {})
0276         : MICommand(NonMI, QString(), flags)
0277         , handler(handler)
0278     {
0279     }
0280 
0281     using MICommand::invokeHandler;
0282     void invokeHandler()
0283     {
0284         handler();
0285     }
0286 
0287     QString cmdToSend() override
0288     {
0289         return QString();
0290     }
0291 
0292 private:
0293     Function handler;
0294 };
0295 
0296 class ExpressionValueCommand : public QObject, public MICommand
0297 {
0298     Q_OBJECT
0299 
0300 public:
0301     using handler_method_t = void (QObject::*)(const QString&);
0302 
0303     template<class Handler>
0304     ExpressionValueCommand(
0305         const QString& expression,
0306         Handler* handler_this,
0307         void (Handler::* handler_method)(const QString&))
0308     : MICommand(DataEvaluateExpression, expression),
0309       handler_this(handler_this),
0310       handler_method(static_cast<handler_method_t>(handler_method))
0311     {
0312         setHandler(this, &ExpressionValueCommand::handleResponse);
0313     }
0314 
0315     void handleResponse(const ResultRecord& r)
0316     {
0317         (handler_this.data()->*handler_method)(r[QStringLiteral("value")].literal());
0318     }
0319 
0320 private:
0321     QPointer<QObject> handler_this;
0322     handler_method_t handler_method;
0323 };
0324 
0325 template<class Handler>
0326 FunctionCommandHandler::Function guarded_callback(Handler *handler_this,
0327                                                  void (Handler::* handler_method)(const ResultRecord&))
0328 {
0329     QPointer<Handler> guarded_this(handler_this);
0330     return [guarded_this, handler_method](const ResultRecord& r) {
0331         if (guarded_this) {
0332             (guarded_this.data()->*handler_method)(r);
0333         }
0334     };
0335 }
0336 
0337 template<class Handler>
0338 void MICommand::setHandler(Handler* handler_this,
0339                            void (Handler::* handler_method)(const ResultRecord&))
0340 {
0341     QPointer<Handler> guarded_this(handler_this);
0342     setHandler(new FunctionCommandHandler([guarded_this, handler_method](const ResultRecord& r) {
0343         if (guarded_this) {
0344             (guarded_this.data()->*handler_method)(r);
0345         }
0346     }, flags()));
0347 }
0348 
0349 template<class Handler>
0350 CliCommand::CliCommand(
0351     CommandType type,
0352     const QString& command,
0353     Handler* handler_this,
0354     void (Handler::* handler_method)(const QStringList&),
0355     CommandFlags flags)
0356 : MICommand(type, command)
0357 {
0358     QPointer<Handler> guarded_this(handler_this);
0359     setHandler(new FunctionCommandHandler([this, guarded_this, handler_method](const ResultRecord&) {
0360         if (guarded_this) {
0361             (guarded_this.data()->*handler_method)(this->allStreamOutput());
0362         }
0363     }, flags));
0364 }
0365 
0366 } // end of namespace MI
0367 } // end of namespace KDevMI
0368 
0369 #endif