]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/lldb/tools/lldb-mi/MICmdInvoker.cpp
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / lldb / tools / lldb-mi / MICmdInvoker.cpp
1 //===-- MICmdInvoker.cpp ----------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 // In-house headers:
10 #include "MICmdInvoker.h"
11 #include "MICmdBase.h"
12 #include "MICmdMgr.h"
13 #include "MICmnLog.h"
14 #include "MICmnStreamStdout.h"
15 #include "MIDriver.h"
16
17 //++
18 // Details: CMICmdInvoker constructor.
19 // Type:    Method.
20 // Args:    None.
21 // Return:  None.
22 // Throws:  None.
23 //--
24 CMICmdInvoker::CMICmdInvoker() : m_rStreamOut(CMICmnStreamStdout::Instance()) {}
25
26 //++
27 // Details: CMICmdInvoker destructor.
28 // Type:    Overridable.
29 // Args:    None.
30 // Return:  None.
31 // Throws:  None.
32 //--
33 CMICmdInvoker::~CMICmdInvoker() { Shutdown(); }
34
35 //++
36 // Details: Initialize resources for *this Command Invoker.
37 // Type:    Method.
38 // Args:    None.
39 // Return:  MIstatus::success - Functional succeeded.
40 //          MIstatus::failure - Functional failed.
41 // Throws:  None.
42 //--
43 bool CMICmdInvoker::Initialize() {
44   m_clientUsageRefCnt++;
45
46   if (m_bInitialized)
47     return MIstatus::success;
48
49   m_bInitialized = true;
50
51   return MIstatus::success;
52 }
53
54 //++
55 // Details: Release resources for *this Stdin stream.
56 // Type:    Method.
57 // Args:    None.
58 // Return:  MIstatus::success - Functional succeeded.
59 //          MIstatus::failure - Functional failed.
60 // Throws:  None.
61 //--
62 bool CMICmdInvoker::Shutdown() {
63   if (--m_clientUsageRefCnt > 0)
64     return MIstatus::success;
65
66   if (!m_bInitialized)
67     return MIstatus::success;
68
69   CmdDeleteAll();
70
71   m_bInitialized = false;
72
73   return MIstatus::success;
74 }
75
76 //++
77 // Details: Empty the map of invoked commands doing work. Command objects are
78 // deleted too.
79 // Type:    Method.
80 // Args:    None.
81 // Return:  None.
82 // Throws:  None.
83 //--
84 void CMICmdInvoker::CmdDeleteAll() {
85   CMICmdMgr &rMgr = CMICmdMgr::Instance();
86   MapCmdIdToCmd_t::const_iterator it = m_mapCmdIdToCmd.begin();
87   while (it != m_mapCmdIdToCmd.end()) {
88     const MIuint cmdId((*it).first);
89     MIunused(cmdId);
90     CMICmdBase *pCmd = (*it).second;
91     const CMIUtilString &rCmdName(pCmd->GetCmdData().strMiCmd);
92     MIunused(rCmdName);
93     rMgr.CmdDelete(pCmd->GetCmdData());
94
95     // Next
96     ++it;
97   }
98   m_mapCmdIdToCmd.clear();
99 }
100
101 //++
102 // Details: Remove from the map of invoked commands doing work a command that
103 // has finished
104 //          its work. The command object is deleted too.
105 // Type:    Method.
106 // Args:    vId             - (R) Command object's unique ID.
107 //          vbYesDeleteCmd  - (R) True = Delete command object, false = delete
108 //          via the Command Manager.
109 // Return:  None.
110 // Throws:  None.
111 //--
112 bool CMICmdInvoker::CmdDelete(const MIuint vId,
113                               const bool vbYesDeleteCmd /*= false*/) {
114   CMICmdMgr &rMgr = CMICmdMgr::Instance();
115   MapCmdIdToCmd_t::const_iterator it = m_mapCmdIdToCmd.find(vId);
116   if (it != m_mapCmdIdToCmd.end()) {
117     CMICmdBase *pCmd = (*it).second;
118     if (vbYesDeleteCmd) {
119       // Via registered interest command manager callback *this object to delete
120       // the command
121       m_mapCmdIdToCmd.erase(it);
122       delete pCmd;
123     } else
124       // Notify other interested object of this command's pending deletion
125       rMgr.CmdDelete(pCmd->GetCmdData());
126   }
127
128   if (m_mapCmdIdToCmd.empty())
129     rMgr.CmdUnregisterForDeleteNotification(*this);
130
131   return MIstatus::success;
132 }
133
134 //++
135 // Details: Add to the map of invoked commands doing work a command that is
136 // about to
137 //          start to do work.
138 // Type:    Method.
139 // Args:    vCmd    - (R) Command object.
140 // Return:  None.
141 // Throws:  None.
142 //--
143 bool CMICmdInvoker::CmdAdd(const CMICmdBase &vCmd) {
144   if (m_mapCmdIdToCmd.empty()) {
145     CMICmdMgr &rMgr = CMICmdMgr::Instance();
146     rMgr.CmdRegisterForDeleteNotification(*this);
147   }
148
149   const MIuint &cmdId(vCmd.GetCmdData().id);
150   MapCmdIdToCmd_t::const_iterator it = m_mapCmdIdToCmd.find(cmdId);
151   if (it != m_mapCmdIdToCmd.end())
152     return MIstatus::success;
153
154   MapPairCmdIdToCmd_t pr(cmdId, const_cast<CMICmdBase *>(&vCmd));
155   m_mapCmdIdToCmd.insert(pr);
156
157   return MIstatus::success;
158 }
159
160 //++
161 // Details: Having previously had the potential command validated and found
162 // valid now
163 //          get the command executed.
164 //          If the Functionality returns MIstatus::failure call
165 //          GetErrorDescription().
166 //          This function is used by the application's main thread.
167 // Type:    Method.
168 // Args:    vCmd    - (RW) Command object.
169 // Return:  MIstatus::success - Functionality succeeded.
170 //          MIstatus::failure - Functionality failed.
171 // Throws:  None.
172 //--
173 bool CMICmdInvoker::CmdExecute(CMICmdBase &vCmd) {
174   bool bOk = CmdAdd(vCmd);
175
176   if (bOk) {
177     vCmd.AddCommonArgs();
178     if (!vCmd.ParseArgs()) {
179       // Report command execution failed
180       const SMICmdData cmdData(vCmd.GetCmdData());
181       CmdStdout(cmdData);
182       CmdCauseAppExit(vCmd);
183       CmdDelete(cmdData.id);
184
185       // Proceed to wait or execute next command
186       return MIstatus::success;
187     }
188   }
189
190   if (bOk && !vCmd.Execute()) {
191     // Report command execution failed
192     const SMICmdData cmdData(vCmd.GetCmdData());
193     CmdStdout(cmdData);
194     CmdCauseAppExit(vCmd);
195     CmdDelete(cmdData.id);
196
197     // Proceed to wait or execute next command
198     return MIstatus::success;
199   }
200
201   bOk = CmdExecuteFinished(vCmd);
202
203   return bOk;
204 }
205
206 //++
207 // Details: Called when a command has finished its Execution() work either
208 // synchronously
209 //          because the command executed was the type a non event type or
210 //          asynchronously
211 //          via the command's callback (because of an SB Listener event). Needs
212 //          to be called
213 //          so that *this invoker call do some house keeping and then proceed to
214 //          call
215 //          the command's Acknowledge() function.
216 // Type:    Method.
217 // Args:    vCmd    - (R) Command object.
218 // Return:  MIstatus::success - Functionality succeeded.
219 //          MIstatus::failure - Functionality failed.
220 // Throws:  None.
221 //--
222 bool CMICmdInvoker::CmdExecuteFinished(CMICmdBase &vCmd) {
223   // Command finished now get the command to gather it's information and form
224   // the MI
225   // Result record
226   if (!vCmd.Acknowledge()) {
227     // Report command acknowledge functionality failed
228     const SMICmdData cmdData(vCmd.GetCmdData());
229     CmdStdout(cmdData);
230     CmdCauseAppExit(vCmd);
231     CmdDelete(cmdData.id);
232
233     // Proceed to wait or execute next command
234     return MIstatus::success;
235   }
236
237   // Retrieve the command's latest data/information. Needed for commands of the
238   // event type so have
239   // a record of commands pending finishing execution.
240   const CMIUtilString &rMIResultRecord(vCmd.GetMIResultRecord());
241   SMICmdData cmdData(
242       vCmd.GetCmdData()); // Make a copy as the command will be deleted soon
243   cmdData.strMiCmdResultRecord = rMIResultRecord; // Precautionary copy as the
244                                                   // command might forget to do
245                                                   // this
246   if (vCmd.HasMIResultRecordExtra()) {
247     cmdData.bHasResultRecordExtra = true;
248     const CMIUtilString &rMIExtra(vCmd.GetMIResultRecordExtra());
249     cmdData.strMiCmdResultRecordExtra =
250         rMIExtra; // Precautionary copy as the command might forget to do this
251   }
252
253   // Send command's MI response to the client
254   bool bOk = CmdStdout(cmdData);
255
256   // Delete the command object as do not require anymore
257   bOk = bOk && CmdDelete(vCmd.GetCmdData().id);
258
259   return bOk;
260 }
261
262 //++
263 // Details: If the MI Driver is not operating via a client i.e. Eclipse check
264 // the command
265 //          on failure suggests the application exits. A command can be such
266 //          that a
267 //          failure cannot the allow the application to continue operating.
268 // Args:    vCmd    - (R) Command object.
269 // Return:  None.
270 // Return:  None.
271 // Throws:  None.
272 //--
273 void CMICmdInvoker::CmdCauseAppExit(const CMICmdBase &vCmd) const {
274   if (vCmd.GetExitAppOnCommandFailure()) {
275     CMIDriver &rDriver(CMIDriver::Instance());
276     if (rDriver.IsDriverDebuggingArgExecutable()) {
277       rDriver.SetExitApplicationFlag(true);
278     }
279   }
280 }
281
282 //++
283 // Details: Write to stdout and the Log file the command's MI formatted result.
284 // Type:    vCmdData    - (R) A command's information.
285 // Return:  MIstatus::success - Functionality succeeded.
286 //          MIstatus::failure - Functionality failed.
287 // Return:  None.
288 // Throws:  None.
289 //--
290 bool CMICmdInvoker::CmdStdout(const SMICmdData &vCmdData) const {
291   bool bOk = m_pLog->WriteLog(vCmdData.strMiCmdAll);
292   const bool bLock = bOk && m_rStreamOut.Lock();
293   bOk = bOk && bLock &&
294         m_rStreamOut.WriteMIResponse(vCmdData.strMiCmdResultRecord);
295   if (bOk && vCmdData.bHasResultRecordExtra) {
296     bOk = m_rStreamOut.WriteMIResponse(vCmdData.strMiCmdResultRecordExtra);
297   }
298   bOk = bLock && m_rStreamOut.Unlock();
299
300   return bOk;
301 }
302
303 //++
304 // Details: Required by the CMICmdMgr::ICmdDeleteCallback. *this object is
305 // registered
306 //          with the Command Manager to receive callbacks when a command is
307 //          being deleted.
308 //          An object, *this invoker, does not delete a command object itself
309 //          but calls
310 //          the Command Manager to delete a command object. This function is the
311 //          Invoker's
312 //          called.
313 //          The Invoker owns the command objects and so can delete them but must
314 //          do it
315 //          via the manager so other objects can be notified of the deletion.
316 // Type:    Method.
317 // Args:    vCmd    - (RW) Command.
318 // Return:  None.
319 // Throws:  None.
320 //--
321 void CMICmdInvoker::Delete(SMICmdData &vCmd) { CmdDelete(vCmd.id, true); }