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