1*80814287SRaphael Isemann //===-- CommandObjectFrame.cpp --------------------------------------------===//
230fdc8d8SChris Lattner //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
630fdc8d8SChris Lattner //
730fdc8d8SChris Lattner //===----------------------------------------------------------------------===//
8c8ecc2a9SEugene Zelenko #include "CommandObjectFrame.h"
930fdc8d8SChris Lattner #include "lldb/Core/Debugger.h"
106d56d2ceSJim Ingham #include "lldb/Core/ValueObject.h"
115548cb50SEnrico Granata #include "lldb/DataFormatters/DataVisualization.h"
124d93b8cdSEnrico Granata #include "lldb/DataFormatters/ValueObjectPrinter.h"
1359998b7bSJonas Devlieghere #include "lldb/Host/Config.h"
143eb2b44dSZachary Turner #include "lldb/Host/OptionParser.h"
1541ae8e74SKuba Mracek #include "lldb/Host/StringConvert.h"
1630fdc8d8SChris Lattner #include "lldb/Interpreter/CommandInterpreter.h"
1730fdc8d8SChris Lattner #include "lldb/Interpreter/CommandReturnObject.h"
181deb7962SGreg Clayton #include "lldb/Interpreter/OptionGroupFormat.h"
192837b766SJim Ingham #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
20715c2365SGreg Clayton #include "lldb/Interpreter/OptionGroupVariable.h"
21b9c1b51eSKate Stone #include "lldb/Interpreter/Options.h"
226754e04fSEnrico Granata #include "lldb/Symbol/Function.h"
236d56d2ceSJim Ingham #include "lldb/Symbol/SymbolContext.h"
246d56d2ceSJim Ingham #include "lldb/Symbol/Variable.h"
256d56d2ceSJim Ingham #include "lldb/Symbol/VariableList.h"
26b57e4a1bSJason Molenda #include "lldb/Target/StackFrame.h"
2741ae8e74SKuba Mracek #include "lldb/Target/StackFrameRecognizer.h"
284740a734SSean Callanan #include "lldb/Target/StopInfo.h"
296d56d2ceSJim Ingham #include "lldb/Target/Target.h"
30b9c1b51eSKate Stone #include "lldb/Target/Thread.h"
31145d95c9SPavel Labath #include "lldb/Utility/Args.h"
3230fdc8d8SChris Lattner 
33796ac80bSJonas Devlieghere #include <memory>
34796ac80bSJonas Devlieghere #include <string>
35796ac80bSJonas Devlieghere 
3630fdc8d8SChris Lattner using namespace lldb;
3730fdc8d8SChris Lattner using namespace lldb_private;
3830fdc8d8SChris Lattner 
394740a734SSean Callanan #pragma mark CommandObjectFrameDiagnose
404740a734SSean Callanan 
414740a734SSean Callanan // CommandObjectFrameInfo
424740a734SSean Callanan 
434740a734SSean Callanan // CommandObjectFrameDiagnose
444740a734SSean Callanan 
45ec67e734SRaphael Isemann #define LLDB_OPTIONS_frame_diag
46ec67e734SRaphael Isemann #include "CommandOptions.inc"
471f0f5b5bSZachary Turner 
48b9c1b51eSKate Stone class CommandObjectFrameDiagnose : public CommandObjectParsed {
494740a734SSean Callanan public:
50b9c1b51eSKate Stone   class CommandOptions : public Options {
514740a734SSean Callanan   public:
52b9c1b51eSKate Stone     CommandOptions() : Options() { OptionParsingStarting(nullptr); }
534740a734SSean Callanan 
544740a734SSean Callanan     ~CommandOptions() override = default;
554740a734SSean Callanan 
5697206d57SZachary Turner     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
57b9c1b51eSKate Stone                           ExecutionContext *execution_context) override {
5897206d57SZachary Turner       Status error;
594740a734SSean Callanan       const int short_option = m_getopt_table[option_idx].val;
60b9c1b51eSKate Stone       switch (short_option) {
614740a734SSean Callanan       case 'r':
624740a734SSean Callanan         reg = ConstString(option_arg);
634740a734SSean Callanan         break;
644740a734SSean Callanan 
65b9c1b51eSKate Stone       case 'a': {
66fe11483bSZachary Turner         address.emplace();
67fe11483bSZachary Turner         if (option_arg.getAsInteger(0, *address)) {
684740a734SSean Callanan           address.reset();
69b9c1b51eSKate Stone           error.SetErrorStringWithFormat("invalid address argument '%s'",
70fe11483bSZachary Turner                                          option_arg.str().c_str());
714740a734SSean Callanan         }
72b9c1b51eSKate Stone       } break;
734740a734SSean Callanan 
74b9c1b51eSKate Stone       case 'o': {
75fe11483bSZachary Turner         offset.emplace();
76fe11483bSZachary Turner         if (option_arg.getAsInteger(0, *offset)) {
774740a734SSean Callanan           offset.reset();
78b9c1b51eSKate Stone           error.SetErrorStringWithFormat("invalid offset argument '%s'",
79fe11483bSZachary Turner                                          option_arg.str().c_str());
804740a734SSean Callanan         }
81b9c1b51eSKate Stone       } break;
824740a734SSean Callanan 
834740a734SSean Callanan       default:
8436162014SRaphael Isemann         llvm_unreachable("Unimplemented option");
854740a734SSean Callanan       }
864740a734SSean Callanan 
874740a734SSean Callanan       return error;
884740a734SSean Callanan     }
894740a734SSean Callanan 
90b9c1b51eSKate Stone     void OptionParsingStarting(ExecutionContext *execution_context) override {
914740a734SSean Callanan       address.reset();
924740a734SSean Callanan       reg.reset();
934740a734SSean Callanan       offset.reset();
944740a734SSean Callanan     }
954740a734SSean Callanan 
961f0f5b5bSZachary Turner     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
9770602439SZachary Turner       return llvm::makeArrayRef(g_frame_diag_options);
981f0f5b5bSZachary Turner     }
994740a734SSean Callanan 
1004740a734SSean Callanan     // Options.
1014740a734SSean Callanan     llvm::Optional<lldb::addr_t> address;
1024740a734SSean Callanan     llvm::Optional<ConstString> reg;
1034740a734SSean Callanan     llvm::Optional<int64_t> offset;
1044740a734SSean Callanan   };
1054740a734SSean Callanan 
1064740a734SSean Callanan   CommandObjectFrameDiagnose(CommandInterpreter &interpreter)
1074740a734SSean Callanan       : CommandObjectParsed(interpreter, "frame diagnose",
108b9c1b51eSKate Stone                             "Try to determine what path path the current stop "
109b9c1b51eSKate Stone                             "location used to get to a register or address",
110b9c1b51eSKate Stone                             nullptr,
111b9c1b51eSKate Stone                             eCommandRequiresThread | eCommandTryTargetAPILock |
112b9c1b51eSKate Stone                                 eCommandProcessMustBeLaunched |
1134740a734SSean Callanan                                 eCommandProcessMustBePaused),
114b9c1b51eSKate Stone         m_options() {
1154740a734SSean Callanan     CommandArgumentEntry arg;
1164740a734SSean Callanan     CommandArgumentData index_arg;
1174740a734SSean Callanan 
1184740a734SSean Callanan     // Define the first (and only) variant of this arg.
1194740a734SSean Callanan     index_arg.arg_type = eArgTypeFrameIndex;
1204740a734SSean Callanan     index_arg.arg_repetition = eArgRepeatOptional;
1214740a734SSean Callanan 
122b9c1b51eSKate Stone     // There is only one variant this argument could be; put it into the
123b9c1b51eSKate Stone     // argument entry.
1244740a734SSean Callanan     arg.push_back(index_arg);
1254740a734SSean Callanan 
1264740a734SSean Callanan     // Push the data for the first argument into the m_arguments vector.
1274740a734SSean Callanan     m_arguments.push_back(arg);
1284740a734SSean Callanan   }
1294740a734SSean Callanan 
1304740a734SSean Callanan   ~CommandObjectFrameDiagnose() override = default;
1314740a734SSean Callanan 
132b9c1b51eSKate Stone   Options *GetOptions() override { return &m_options; }
1334740a734SSean Callanan 
1344740a734SSean Callanan protected:
135b9c1b51eSKate Stone   bool DoExecute(Args &command, CommandReturnObject &result) override {
1364740a734SSean Callanan     Thread *thread = m_exe_ctx.GetThreadPtr();
1374740a734SSean Callanan     StackFrameSP frame_sp = thread->GetSelectedFrame();
1384740a734SSean Callanan 
1394740a734SSean Callanan     ValueObjectSP valobj_sp;
1404740a734SSean Callanan 
141b9c1b51eSKate Stone     if (m_options.address.hasValue()) {
142b9c1b51eSKate Stone       if (m_options.reg.hasValue() || m_options.offset.hasValue()) {
143b9c1b51eSKate Stone         result.AppendError(
144b9c1b51eSKate Stone             "`frame diagnose --address` is incompatible with other arguments.");
1454740a734SSean Callanan         result.SetStatus(eReturnStatusFailed);
1464740a734SSean Callanan         return false;
1474740a734SSean Callanan       }
1484740a734SSean Callanan       valobj_sp = frame_sp->GuessValueForAddress(m_options.address.getValue());
149b9c1b51eSKate Stone     } else if (m_options.reg.hasValue()) {
150b9c1b51eSKate Stone       valobj_sp = frame_sp->GuessValueForRegisterAndOffset(
151b9c1b51eSKate Stone           m_options.reg.getValue(), m_options.offset.getValueOr(0));
152b9c1b51eSKate Stone     } else {
1534740a734SSean Callanan       StopInfoSP stop_info_sp = thread->GetStopInfo();
154b9c1b51eSKate Stone       if (!stop_info_sp) {
1554740a734SSean Callanan         result.AppendError("No arguments provided, and no stop info.");
1564740a734SSean Callanan         result.SetStatus(eReturnStatusFailed);
1574740a734SSean Callanan         return false;
1584740a734SSean Callanan       }
1594740a734SSean Callanan 
1604740a734SSean Callanan       valobj_sp = StopInfo::GetCrashingDereference(stop_info_sp);
1614740a734SSean Callanan     }
1624740a734SSean Callanan 
163b9c1b51eSKate Stone     if (!valobj_sp) {
1644740a734SSean Callanan       result.AppendError("No diagnosis available.");
1654740a734SSean Callanan       result.SetStatus(eReturnStatusFailed);
1664740a734SSean Callanan       return false;
1674740a734SSean Callanan     }
1684740a734SSean Callanan 
169a925974bSAdrian Prantl     DumpValueObjectOptions::DeclPrintingHelper helper =
170a925974bSAdrian Prantl         [&valobj_sp](ConstString type, ConstString var,
171a925974bSAdrian Prantl                      const DumpValueObjectOptions &opts,
1723bc714b2SZachary Turner                      Stream &stream) -> bool {
173b9c1b51eSKate Stone       const ValueObject::GetExpressionPathFormat format = ValueObject::
174b9c1b51eSKate Stone           GetExpressionPathFormat::eGetExpressionPathFormatHonorPointers;
1755d1f711aSPavel Labath       const bool qualify_cxx_base_classes = false;
1764740a734SSean Callanan       valobj_sp->GetExpressionPath(stream, qualify_cxx_base_classes, format);
1774740a734SSean Callanan       stream.PutCString(" =");
1784740a734SSean Callanan       return true;
1794740a734SSean Callanan     };
1804740a734SSean Callanan 
1814740a734SSean Callanan     DumpValueObjectOptions options;
1824740a734SSean Callanan     options.SetDeclPrintingHelper(helper);
183b9c1b51eSKate Stone     ValueObjectPrinter printer(valobj_sp.get(), &result.GetOutputStream(),
184b9c1b51eSKate Stone                                options);
1854740a734SSean Callanan     printer.PrintValueObject();
1864740a734SSean Callanan 
1874740a734SSean Callanan     return true;
1884740a734SSean Callanan   }
1894740a734SSean Callanan 
1904740a734SSean Callanan protected:
1914740a734SSean Callanan   CommandOptions m_options;
1924740a734SSean Callanan };
1934740a734SSean Callanan 
19430fdc8d8SChris Lattner #pragma mark CommandObjectFrameInfo
19530fdc8d8SChris Lattner 
19630fdc8d8SChris Lattner // CommandObjectFrameInfo
19730fdc8d8SChris Lattner 
198b9c1b51eSKate Stone class CommandObjectFrameInfo : public CommandObjectParsed {
19930fdc8d8SChris Lattner public:
2007428a18cSKate Stone   CommandObjectFrameInfo(CommandInterpreter &interpreter)
201a925974bSAdrian Prantl       : CommandObjectParsed(interpreter, "frame info",
202a925974bSAdrian Prantl                             "List information about the current "
203b9c1b51eSKate Stone                             "stack frame in the current thread.",
204b9c1b51eSKate Stone                             "frame info",
205b9c1b51eSKate Stone                             eCommandRequiresFrame | eCommandTryTargetAPILock |
206a925974bSAdrian Prantl                                 eCommandProcessMustBeLaunched |
207a925974bSAdrian Prantl                                 eCommandProcessMustBePaused) {}
20830fdc8d8SChris Lattner 
209c8ecc2a9SEugene Zelenko   ~CommandObjectFrameInfo() override = default;
21030fdc8d8SChris Lattner 
2115a988416SJim Ingham protected:
212b9c1b51eSKate Stone   bool DoExecute(Args &command, CommandReturnObject &result) override {
213f9fc609fSGreg Clayton     m_exe_ctx.GetFrameRef().DumpUsingSettingsFormat(&result.GetOutputStream());
21430fdc8d8SChris Lattner     result.SetStatus(eReturnStatusSuccessFinishResult);
21530fdc8d8SChris Lattner     return result.Succeeded();
21630fdc8d8SChris Lattner   }
21730fdc8d8SChris Lattner };
21830fdc8d8SChris Lattner 
21930fdc8d8SChris Lattner #pragma mark CommandObjectFrameSelect
22030fdc8d8SChris Lattner 
22130fdc8d8SChris Lattner // CommandObjectFrameSelect
22230fdc8d8SChris Lattner 
223ec67e734SRaphael Isemann #define LLDB_OPTIONS_frame_select
224ec67e734SRaphael Isemann #include "CommandOptions.inc"
2251f0f5b5bSZachary Turner 
226b9c1b51eSKate Stone class CommandObjectFrameSelect : public CommandObjectParsed {
22730fdc8d8SChris Lattner public:
228b9c1b51eSKate Stone   class CommandOptions : public Options {
229864174e1SGreg Clayton   public:
230b9c1b51eSKate Stone     CommandOptions() : Options() { OptionParsingStarting(nullptr); }
231864174e1SGreg Clayton 
232c8ecc2a9SEugene Zelenko     ~CommandOptions() override = default;
233864174e1SGreg Clayton 
23497206d57SZachary Turner     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
235b9c1b51eSKate Stone                           ExecutionContext *execution_context) override {
23697206d57SZachary Turner       Status error;
2373bcdfc0eSGreg Clayton       const int short_option = m_getopt_table[option_idx].val;
238b9c1b51eSKate Stone       switch (short_option) {
239dab6f074SRaphael Isemann       case 'r': {
240dab6f074SRaphael Isemann         int32_t offset = 0;
241dab6f074SRaphael Isemann         if (option_arg.getAsInteger(0, offset) || offset == INT32_MIN) {
242b9c1b51eSKate Stone           error.SetErrorStringWithFormat("invalid frame offset argument '%s'",
243fe11483bSZachary Turner                                          option_arg.str().c_str());
244dab6f074SRaphael Isemann         } else
245dab6f074SRaphael Isemann           relative_frame_offset = offset;
2465a039d55SRaphael Isemann         break;
247dab6f074SRaphael Isemann       }
248864174e1SGreg Clayton 
249864174e1SGreg Clayton       default:
25036162014SRaphael Isemann         llvm_unreachable("Unimplemented option");
251864174e1SGreg Clayton       }
252864174e1SGreg Clayton 
253864174e1SGreg Clayton       return error;
254864174e1SGreg Clayton     }
255864174e1SGreg Clayton 
2565a039d55SRaphael Isemann     void OptionParsingStarting(ExecutionContext *execution_context) override {
257dab6f074SRaphael Isemann       relative_frame_offset.reset();
2585a039d55SRaphael Isemann     }
259864174e1SGreg Clayton 
2601f0f5b5bSZachary Turner     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
26170602439SZachary Turner       return llvm::makeArrayRef(g_frame_select_options);
2621f0f5b5bSZachary Turner     }
263864174e1SGreg Clayton 
264dab6f074SRaphael Isemann     llvm::Optional<int32_t> relative_frame_offset;
265864174e1SGreg Clayton   };
266864174e1SGreg Clayton 
2677428a18cSKate Stone   CommandObjectFrameSelect(CommandInterpreter &interpreter)
268a925974bSAdrian Prantl       : CommandObjectParsed(interpreter, "frame select",
269a925974bSAdrian Prantl                             "Select the current stack frame by "
270b9c1b51eSKate Stone                             "index from within the current thread "
271b9c1b51eSKate Stone                             "(see 'thread backtrace'.)",
272b9c1b51eSKate Stone                             nullptr,
273b9c1b51eSKate Stone                             eCommandRequiresThread | eCommandTryTargetAPILock |
274a925974bSAdrian Prantl                                 eCommandProcessMustBeLaunched |
275a925974bSAdrian Prantl                                 eCommandProcessMustBePaused),
276b9c1b51eSKate Stone         m_options() {
277405fe67fSCaroline Tice     CommandArgumentEntry arg;
278405fe67fSCaroline Tice     CommandArgumentData index_arg;
279405fe67fSCaroline Tice 
280405fe67fSCaroline Tice     // Define the first (and only) variant of this arg.
281405fe67fSCaroline Tice     index_arg.arg_type = eArgTypeFrameIndex;
282864174e1SGreg Clayton     index_arg.arg_repetition = eArgRepeatOptional;
283405fe67fSCaroline Tice 
284b9c1b51eSKate Stone     // There is only one variant this argument could be; put it into the
285b9c1b51eSKate Stone     // argument entry.
286405fe67fSCaroline Tice     arg.push_back(index_arg);
287405fe67fSCaroline Tice 
288405fe67fSCaroline Tice     // Push the data for the first argument into the m_arguments vector.
289405fe67fSCaroline Tice     m_arguments.push_back(arg);
29030fdc8d8SChris Lattner   }
29130fdc8d8SChris Lattner 
292c8ecc2a9SEugene Zelenko   ~CommandObjectFrameSelect() override = default;
29330fdc8d8SChris Lattner 
294b9c1b51eSKate Stone   Options *GetOptions() override { return &m_options; }
295864174e1SGreg Clayton 
2965a988416SJim Ingham protected:
297b9c1b51eSKate Stone   bool DoExecute(Args &command, CommandReturnObject &result) override {
298b9c1b51eSKate Stone     // No need to check "thread" for validity as eCommandRequiresThread ensures
299b9c1b51eSKate Stone     // it is valid
300f9fc609fSGreg Clayton     Thread *thread = m_exe_ctx.GetThreadPtr();
301f9fc609fSGreg Clayton 
302864174e1SGreg Clayton     uint32_t frame_idx = UINT32_MAX;
303dab6f074SRaphael Isemann     if (m_options.relative_frame_offset.hasValue()) {
304864174e1SGreg Clayton       // The one and only argument is a signed relative frame index
305c14ee32dSGreg Clayton       frame_idx = thread->GetSelectedFrameIndex();
306864174e1SGreg Clayton       if (frame_idx == UINT32_MAX)
307864174e1SGreg Clayton         frame_idx = 0;
308864174e1SGreg Clayton 
309dab6f074SRaphael Isemann       if (*m_options.relative_frame_offset < 0) {
310dab6f074SRaphael Isemann         if (static_cast<int32_t>(frame_idx) >=
311dab6f074SRaphael Isemann             -*m_options.relative_frame_offset)
312dab6f074SRaphael Isemann           frame_idx += *m_options.relative_frame_offset;
313b9c1b51eSKate Stone         else {
314b9c1b51eSKate Stone           if (frame_idx == 0) {
31505097246SAdrian Prantl             // If you are already at the bottom of the stack, then just warn
31605097246SAdrian Prantl             // and don't reset the frame.
3177428a18cSKate Stone             result.AppendError("Already at the bottom of the stack.");
318213b4546SJim Ingham             result.SetStatus(eReturnStatusFailed);
319213b4546SJim Ingham             return false;
320b9c1b51eSKate Stone           } else
321864174e1SGreg Clayton             frame_idx = 0;
322864174e1SGreg Clayton         }
323dab6f074SRaphael Isemann       } else if (*m_options.relative_frame_offset > 0) {
324b9c1b51eSKate Stone         // I don't want "up 20" where "20" takes you past the top of the stack
325b9c1b51eSKate Stone         // to produce
326b9c1b51eSKate Stone         // an error, but rather to just go to the top.  So I have to count the
327b9c1b51eSKate Stone         // stack here...
328b0c72a5fSJim Ingham         const uint32_t num_frames = thread->GetStackFrameCount();
329b9c1b51eSKate Stone         if (static_cast<int32_t>(num_frames - frame_idx) >
330dab6f074SRaphael Isemann             *m_options.relative_frame_offset)
331dab6f074SRaphael Isemann           frame_idx += *m_options.relative_frame_offset;
332b9c1b51eSKate Stone         else {
333b9c1b51eSKate Stone           if (frame_idx == num_frames - 1) {
334b9c1b51eSKate Stone             // If we are already at the top of the stack, just warn and don't
335b9c1b51eSKate Stone             // reset the frame.
3367428a18cSKate Stone             result.AppendError("Already at the top of the stack.");
337213b4546SJim Ingham             result.SetStatus(eReturnStatusFailed);
338213b4546SJim Ingham             return false;
339b9c1b51eSKate Stone           } else
340864174e1SGreg Clayton             frame_idx = num_frames - 1;
341864174e1SGreg Clayton         }
342864174e1SGreg Clayton       }
343b9c1b51eSKate Stone     } else {
344f965cc86SZachary Turner       if (command.GetArgumentCount() > 1) {
345f965cc86SZachary Turner         result.AppendErrorWithFormat(
346f965cc86SZachary Turner             "too many arguments; expected frame-index, saw '%s'.\n",
347867e7d17SZachary Turner             command[0].c_str());
348f965cc86SZachary Turner         m_options.GenerateOptionUsage(
349f965cc86SZachary Turner             result.GetErrorStream(), this,
350f965cc86SZachary Turner             GetCommandInterpreter().GetDebugger().GetTerminalWidth());
351f965cc86SZachary Turner         return false;
352f965cc86SZachary Turner       }
353f965cc86SZachary Turner 
354b9c1b51eSKate Stone       if (command.GetArgumentCount() == 1) {
3550d9a201eSRaphael Isemann         if (command[0].ref().getAsInteger(0, frame_idx)) {
356b9c1b51eSKate Stone           result.AppendErrorWithFormat("invalid frame index argument '%s'.",
357f965cc86SZachary Turner                                        command[0].c_str());
358afbb0af8SJim Ingham           result.SetStatus(eReturnStatusFailed);
359afbb0af8SJim Ingham           return false;
360afbb0af8SJim Ingham         }
361b9c1b51eSKate Stone       } else if (command.GetArgumentCount() == 0) {
36282d4a2b9SJason Molenda         frame_idx = thread->GetSelectedFrameIndex();
363b9c1b51eSKate Stone         if (frame_idx == UINT32_MAX) {
36482d4a2b9SJason Molenda           frame_idx = 0;
36582d4a2b9SJason Molenda         }
366864174e1SGreg Clayton       }
367864174e1SGreg Clayton     }
36830fdc8d8SChris Lattner 
369b9c1b51eSKate Stone     bool success = thread->SetSelectedFrameByIndexNoisily(
370b9c1b51eSKate Stone         frame_idx, result.GetOutputStream());
371b9c1b51eSKate Stone     if (success) {
372f9fc609fSGreg Clayton       m_exe_ctx.SetFrameSP(thread->GetSelectedFrame());
37330fdc8d8SChris Lattner       result.SetStatus(eReturnStatusSuccessFinishResult);
374b9c1b51eSKate Stone     } else {
375b9c1b51eSKate Stone       result.AppendErrorWithFormat("Frame index (%u) out of range.\n",
376b9c1b51eSKate Stone                                    frame_idx);
37730fdc8d8SChris Lattner       result.SetStatus(eReturnStatusFailed);
37893208b86SJim Ingham     }
37993208b86SJim Ingham 
38093208b86SJim Ingham     return result.Succeeded();
38130fdc8d8SChris Lattner   }
382864174e1SGreg Clayton 
383c8ecc2a9SEugene Zelenko protected:
384864174e1SGreg Clayton   CommandOptions m_options;
385864174e1SGreg Clayton };
386864174e1SGreg Clayton 
3876d56d2ceSJim Ingham #pragma mark CommandObjectFrameVariable
3886d56d2ceSJim Ingham // List images with associated information
389b9c1b51eSKate Stone class CommandObjectFrameVariable : public CommandObjectParsed {
3906d56d2ceSJim Ingham public:
3917428a18cSKate Stone   CommandObjectFrameVariable(CommandInterpreter &interpreter)
3927428a18cSKate Stone       : CommandObjectParsed(
393b9c1b51eSKate Stone             interpreter, "frame variable",
394b9c1b51eSKate Stone             "Show variables for the current stack frame. Defaults to all "
3957428a18cSKate Stone             "arguments and local variables in scope. Names of argument, "
3967428a18cSKate Stone             "local, file static and file global variables can be specified. "
397ed8a705cSGreg Clayton             "Children of aggregate variables can be specified such as "
398285ae0c0SJim Ingham             "'var->child.x'.  The -> and [] operators in 'frame variable' do "
399285ae0c0SJim Ingham             "not invoke operator overloads if they exist, but directly access "
400285ae0c0SJim Ingham             "the specified element.  If you want to trigger operator overloads "
401285ae0c0SJim Ingham             "use the expression command to print the variable instead."
402285ae0c0SJim Ingham             "\nIt is worth noting that except for overloaded "
403285ae0c0SJim Ingham             "operators, when printing local variables 'expr local_var' and "
404285ae0c0SJim Ingham             "'frame var local_var' produce the same "
405285ae0c0SJim Ingham             "results.  However, 'frame variable' is more efficient, since it "
406285ae0c0SJim Ingham             "uses debug information and memory reads directly, rather than "
407285ae0c0SJim Ingham             "parsing and evaluating an expression, which may even involve "
408285ae0c0SJim Ingham             "JITing and running code in the target program.",
409a925974bSAdrian Prantl             nullptr,
410a925974bSAdrian Prantl             eCommandRequiresFrame | eCommandTryTargetAPILock |
411a925974bSAdrian Prantl                 eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |
412a925974bSAdrian Prantl                 eCommandRequiresProcess),
413e1cfbc79STodd Fiala         m_option_group(),
414b9c1b51eSKate Stone         m_option_variable(
415b9c1b51eSKate Stone             true), // Include the frame specific options by passing "true"
416a925974bSAdrian Prantl         m_option_format(eFormatDefault), m_varobj_options() {
417405fe67fSCaroline Tice     CommandArgumentEntry arg;
418405fe67fSCaroline Tice     CommandArgumentData var_name_arg;
419405fe67fSCaroline Tice 
420405fe67fSCaroline Tice     // Define the first (and only) variant of this arg.
421405fe67fSCaroline Tice     var_name_arg.arg_type = eArgTypeVarName;
422405fe67fSCaroline Tice     var_name_arg.arg_repetition = eArgRepeatStar;
423405fe67fSCaroline Tice 
424b9c1b51eSKate Stone     // There is only one variant this argument could be; put it into the
425b9c1b51eSKate Stone     // argument entry.
426405fe67fSCaroline Tice     arg.push_back(var_name_arg);
427405fe67fSCaroline Tice 
428405fe67fSCaroline Tice     // Push the data for the first argument into the m_arguments vector.
429405fe67fSCaroline Tice     m_arguments.push_back(arg);
4302837b766SJim Ingham 
431715c2365SGreg Clayton     m_option_group.Append(&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
432b9c1b51eSKate Stone     m_option_group.Append(&m_option_format,
433b9c1b51eSKate Stone                           OptionGroupFormat::OPTION_GROUP_FORMAT |
434b9c1b51eSKate Stone                               OptionGroupFormat::OPTION_GROUP_GDB_FMT,
435b9c1b51eSKate Stone                           LLDB_OPT_SET_1);
4362837b766SJim Ingham     m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
4372837b766SJim Ingham     m_option_group.Finalize();
4386d56d2ceSJim Ingham   }
4396d56d2ceSJim Ingham 
440c8ecc2a9SEugene Zelenko   ~CommandObjectFrameVariable() override = default;
4416d56d2ceSJim Ingham 
442b9c1b51eSKate Stone   Options *GetOptions() override { return &m_option_group; }
443f21feadcSGreg Clayton 
444ae34ed2cSRaphael Isemann   void
445ae34ed2cSRaphael Isemann   HandleArgumentCompletion(CompletionRequest &request,
4462443bbd4SRaphael Isemann                            OptionElementVector &opt_element_vector) override {
447f21feadcSGreg Clayton     // Arguments are the standard source file completer.
448b9c1b51eSKate Stone     CommandCompletions::InvokeCommonCompletionCallbacks(
449b9c1b51eSKate Stone         GetCommandInterpreter(), CommandCompletions::eVariablePathCompletion,
450a2e76c0bSRaphael Isemann         request, nullptr);
451f21feadcSGreg Clayton   }
4526d56d2ceSJim Ingham 
4535a988416SJim Ingham protected:
45473418dfeSEnrico Granata   llvm::StringRef GetScopeString(VariableSP var_sp) {
45573418dfeSEnrico Granata     if (!var_sp)
45673418dfeSEnrico Granata       return llvm::StringRef::withNullAsEmpty(nullptr);
45773418dfeSEnrico Granata 
45873418dfeSEnrico Granata     switch (var_sp->GetScope()) {
45973418dfeSEnrico Granata     case eValueTypeVariableGlobal:
46073418dfeSEnrico Granata       return "GLOBAL: ";
46173418dfeSEnrico Granata     case eValueTypeVariableStatic:
46273418dfeSEnrico Granata       return "STATIC: ";
46373418dfeSEnrico Granata     case eValueTypeVariableArgument:
46473418dfeSEnrico Granata       return "ARG: ";
46573418dfeSEnrico Granata     case eValueTypeVariableLocal:
46673418dfeSEnrico Granata       return "LOCAL: ";
46773418dfeSEnrico Granata     case eValueTypeVariableThreadLocal:
46873418dfeSEnrico Granata       return "THREAD: ";
46973418dfeSEnrico Granata     default:
47073418dfeSEnrico Granata       break;
47173418dfeSEnrico Granata     }
47273418dfeSEnrico Granata 
47373418dfeSEnrico Granata     return llvm::StringRef::withNullAsEmpty(nullptr);
47473418dfeSEnrico Granata   }
47573418dfeSEnrico Granata 
476b9c1b51eSKate Stone   bool DoExecute(Args &command, CommandReturnObject &result) override {
47705097246SAdrian Prantl     // No need to check "frame" for validity as eCommandRequiresFrame ensures
47805097246SAdrian Prantl     // it is valid
479b57e4a1bSJason Molenda     StackFrame *frame = m_exe_ctx.GetFramePtr();
4801e49e5e7SJohnny Chen 
481a134cc1bSGreg Clayton     Stream &s = result.GetOutputStream();
4826d56d2ceSJim Ingham 
483b9c1b51eSKate Stone     // Be careful about the stack frame, if any summary formatter runs code, it
48405097246SAdrian Prantl     // might clear the StackFrameList for the thread.  So hold onto a shared
48505097246SAdrian Prantl     // pointer to the frame so it stays alive.
486650543f9SJim Ingham 
487b9c1b51eSKate Stone     VariableList *variable_list =
488b9c1b51eSKate Stone         frame->GetVariableList(m_option_variable.show_globals);
489a134cc1bSGreg Clayton 
4906d56d2ceSJim Ingham     VariableSP var_sp;
4916d56d2ceSJim Ingham     ValueObjectSP valobj_sp;
49278a685aaSJim Ingham 
493061858ceSEnrico Granata     TypeSummaryImplSP summary_format_sp;
49417b11749SEnrico Granata     if (!m_option_variable.summary.IsCurrentValueEmpty())
495b9c1b51eSKate Stone       DataVisualization::NamedSummaryFormats::GetSummaryFormat(
496b9c1b51eSKate Stone           ConstString(m_option_variable.summary.GetCurrentValue()),
497b9c1b51eSKate Stone           summary_format_sp);
49817b11749SEnrico Granata     else if (!m_option_variable.summary_string.IsCurrentValueEmpty())
499796ac80bSJonas Devlieghere       summary_format_sp = std::make_shared<StringSummaryFormat>(
500b9c1b51eSKate Stone           TypeSummaryImpl::Flags(),
501796ac80bSJonas Devlieghere           m_option_variable.summary_string.GetCurrentValue());
502f9fa6ee5SEnrico Granata 
503b9c1b51eSKate Stone     DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(
504b9c1b51eSKate Stone         eLanguageRuntimeDescriptionDisplayVerbosityFull, eFormatDefault,
505b9c1b51eSKate Stone         summary_format_sp));
506379447a7SEnrico Granata 
507b9c1b51eSKate Stone     const SymbolContext &sym_ctx =
508b9c1b51eSKate Stone         frame->GetSymbolContext(eSymbolContextFunction);
5096754e04fSEnrico Granata     if (sym_ctx.function && sym_ctx.function->IsTopLevelFunction())
5106754e04fSEnrico Granata       m_option_variable.show_globals = true;
5116754e04fSEnrico Granata 
512b9c1b51eSKate Stone     if (variable_list) {
5131deb7962SGreg Clayton       const Format format = m_option_format.GetFormat();
5140c489f58SEnrico Granata       options.SetFormat(format);
5151deb7962SGreg Clayton 
51611eb9c64SZachary Turner       if (!command.empty()) {
51746747022SGreg Clayton         VariableList regex_var_list;
51846747022SGreg Clayton 
51905097246SAdrian Prantl         // If we have any args to the variable command, we will make variable
52005097246SAdrian Prantl         // objects from them...
521f965cc86SZachary Turner         for (auto &entry : command) {
522b9c1b51eSKate Stone           if (m_option_variable.use_regex) {
523c7bece56SGreg Clayton             const size_t regex_start_index = regex_var_list.GetSize();
5240d9a201eSRaphael Isemann             llvm::StringRef name_str = entry.ref();
52595eae423SZachary Turner             RegularExpression regex(name_str);
526f9d90bc5SJan Kratochvil             if (regex.IsValid()) {
52746747022SGreg Clayton               size_t num_matches = 0;
528b9c1b51eSKate Stone               const size_t num_new_regex_vars =
529b9c1b51eSKate Stone                   variable_list->AppendVariablesIfUnique(regex, regex_var_list,
53078a685aaSJim Ingham                                                          num_matches);
531b9c1b51eSKate Stone               if (num_new_regex_vars > 0) {
532b9c1b51eSKate Stone                 for (size_t regex_idx = regex_start_index,
533b9c1b51eSKate Stone                             end_index = regex_var_list.GetSize();
534b9c1b51eSKate Stone                      regex_idx < end_index; ++regex_idx) {
53546747022SGreg Clayton                   var_sp = regex_var_list.GetVariableAtIndex(regex_idx);
536b9c1b51eSKate Stone                   if (var_sp) {
537b9c1b51eSKate Stone                     valobj_sp = frame->GetValueObjectForFrameVariable(
538b9c1b51eSKate Stone                         var_sp, m_varobj_options.use_dynamic);
539b9c1b51eSKate Stone                     if (valobj_sp) {
54073418dfeSEnrico Granata                       std::string scope_string;
54173418dfeSEnrico Granata                       if (m_option_variable.show_scope)
54273418dfeSEnrico Granata                         scope_string = GetScopeString(var_sp).str();
54373418dfeSEnrico Granata 
54473418dfeSEnrico Granata                       if (!scope_string.empty())
545771ef6d4SMalcolm Parsons                         s.PutCString(scope_string);
54673418dfeSEnrico Granata 
547b9c1b51eSKate Stone                       if (m_option_variable.show_decl &&
548b9c1b51eSKate Stone                           var_sp->GetDeclaration().GetFile()) {
54945ba8543SGreg Clayton                         bool show_fullpaths = false;
55045ba8543SGreg Clayton                         bool show_module = true;
551b9c1b51eSKate Stone                         if (var_sp->DumpDeclaration(&s, show_fullpaths,
552b9c1b51eSKate Stone                                                     show_module))
55346747022SGreg Clayton                           s.PutCString(": ");
55446747022SGreg Clayton                       }
5554d93b8cdSEnrico Granata                       valobj_sp->Dump(result.GetOutputStream(), options);
55646747022SGreg Clayton                     }
55746747022SGreg Clayton                   }
55846747022SGreg Clayton                 }
559b9c1b51eSKate Stone               } else if (num_matches == 0) {
560b9c1b51eSKate Stone                 result.GetErrorStream().Printf("error: no variables matched "
561b9c1b51eSKate Stone                                                "the regular expression '%s'.\n",
562f965cc86SZachary Turner                                                entry.c_str());
56346747022SGreg Clayton               }
564b9c1b51eSKate Stone             } else {
5653af3f1e8SJonas Devlieghere               if (llvm::Error err = regex.GetError())
5663af3f1e8SJonas Devlieghere                 result.GetErrorStream().Printf(
5673af3f1e8SJonas Devlieghere                     "error: %s\n", llvm::toString(std::move(err)).c_str());
56846747022SGreg Clayton               else
569b9c1b51eSKate Stone                 result.GetErrorStream().Printf(
570b9c1b51eSKate Stone                     "error: unknown regex error when compiling '%s'\n",
571f965cc86SZachary Turner                     entry.c_str());
57246747022SGreg Clayton             }
573b9c1b51eSKate Stone           } else // No regex, either exact variable names or variable
574b9c1b51eSKate Stone                  // expressions.
57546747022SGreg Clayton           {
57697206d57SZachary Turner             Status error;
577b9c1b51eSKate Stone             uint32_t expr_path_options =
578b9c1b51eSKate Stone                 StackFrame::eExpressionPathOptionCheckPtrVsMember |
57946252398SEnrico Granata                 StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
58046252398SEnrico Granata                 StackFrame::eExpressionPathOptionsInspectAnonymousUnions;
5812837b766SJim Ingham             lldb::VariableSP var_sp;
582b9c1b51eSKate Stone             valobj_sp = frame->GetValueForVariableExpressionPath(
5830d9a201eSRaphael Isemann                 entry.ref(), m_varobj_options.use_dynamic, expr_path_options,
584b9c1b51eSKate Stone                 var_sp, error);
585b9c1b51eSKate Stone             if (valobj_sp) {
58673418dfeSEnrico Granata               std::string scope_string;
58773418dfeSEnrico Granata               if (m_option_variable.show_scope)
58873418dfeSEnrico Granata                 scope_string = GetScopeString(var_sp).str();
58973418dfeSEnrico Granata 
59073418dfeSEnrico Granata               if (!scope_string.empty())
591771ef6d4SMalcolm Parsons                 s.PutCString(scope_string);
592b9c1b51eSKate Stone               if (m_option_variable.show_decl && var_sp &&
593b9c1b51eSKate Stone                   var_sp->GetDeclaration().GetFile()) {
594a134cc1bSGreg Clayton                 var_sp->GetDeclaration().DumpStopContext(&s, false);
595a134cc1bSGreg Clayton                 s.PutCString(": ");
596a134cc1bSGreg Clayton               }
5970c489f58SEnrico Granata 
5980c489f58SEnrico Granata               options.SetFormat(format);
599b9c1b51eSKate Stone               options.SetVariableFormatDisplayLanguage(
600b9c1b51eSKate Stone                   valobj_sp->GetPreferredDisplayLanguage());
601887062aeSJohnny Chen 
602887062aeSJohnny Chen               Stream &output_stream = result.GetOutputStream();
603f965cc86SZachary Turner               options.SetRootValueObjectName(
604f965cc86SZachary Turner                   valobj_sp->GetParent() ? entry.c_str() : nullptr);
6054d93b8cdSEnrico Granata               valobj_sp->Dump(output_stream, options);
606b9c1b51eSKate Stone             } else {
607c8ecc2a9SEugene Zelenko               const char *error_cstr = error.AsCString(nullptr);
60854979cddSGreg Clayton               if (error_cstr)
60954979cddSGreg Clayton                 result.GetErrorStream().Printf("error: %s\n", error_cstr);
61054979cddSGreg Clayton               else
611b9c1b51eSKate Stone                 result.GetErrorStream().Printf("error: unable to find any "
612b9c1b51eSKate Stone                                                "variable expression path that "
613b9c1b51eSKate Stone                                                "matches '%s'.\n",
614f965cc86SZachary Turner                                                entry.c_str());
6156d56d2ceSJim Ingham             }
6166d56d2ceSJim Ingham           }
6176d56d2ceSJim Ingham         }
618b9c1b51eSKate Stone       } else // No command arg specified.  Use variable_list, instead.
6196d56d2ceSJim Ingham       {
620c7bece56SGreg Clayton         const size_t num_variables = variable_list->GetSize();
621b9c1b51eSKate Stone         if (num_variables > 0) {
622b9c1b51eSKate Stone           for (size_t i = 0; i < num_variables; i++) {
6231a65ae11SGreg Clayton             var_sp = variable_list->GetVariableAtIndex(i);
624f955efc0SSylvestre Ledru             switch (var_sp->GetScope()) {
625eb236735SJim Ingham             case eValueTypeVariableGlobal:
626eb236735SJim Ingham               if (!m_option_variable.show_globals)
627eb236735SJim Ingham                 continue;
628eb236735SJim Ingham               break;
629eb236735SJim Ingham             case eValueTypeVariableStatic:
630eb236735SJim Ingham               if (!m_option_variable.show_globals)
631eb236735SJim Ingham                 continue;
632eb236735SJim Ingham               break;
633eb236735SJim Ingham             case eValueTypeVariableArgument:
634eb236735SJim Ingham               if (!m_option_variable.show_args)
635eb236735SJim Ingham                 continue;
636eb236735SJim Ingham               break;
637eb236735SJim Ingham             case eValueTypeVariableLocal:
638eb236735SJim Ingham               if (!m_option_variable.show_locals)
639eb236735SJim Ingham                 continue;
640eb236735SJim Ingham               break;
641eb236735SJim Ingham             default:
642eb236735SJim Ingham               continue;
643eb236735SJim Ingham               break;
644eb236735SJim Ingham             }
645560558ebSEnrico Granata             std::string scope_string;
646eb236735SJim Ingham             if (m_option_variable.show_scope)
64773418dfeSEnrico Granata               scope_string = GetScopeString(var_sp).str();
6486d56d2ceSJim Ingham 
64905097246SAdrian Prantl             // Use the variable object code to make sure we are using the same
65005097246SAdrian Prantl             // APIs as the public API will be using...
651b9c1b51eSKate Stone             valobj_sp = frame->GetValueObjectForFrameVariable(
652b9c1b51eSKate Stone                 var_sp, m_varobj_options.use_dynamic);
653b9c1b51eSKate Stone             if (valobj_sp) {
65405097246SAdrian Prantl               // When dumping all variables, don't print any variables that are
65505097246SAdrian Prantl               // not in scope to avoid extra unneeded output
656b9c1b51eSKate Stone               if (valobj_sp->IsInScope()) {
657b9c1b51eSKate Stone                 if (!valobj_sp->GetTargetSP()
658b9c1b51eSKate Stone                          ->GetDisplayRuntimeSupportValues() &&
659c8ecc2a9SEugene Zelenko                     valobj_sp->IsRuntimeSupportValue())
660560558ebSEnrico Granata                   continue;
661560558ebSEnrico Granata 
662560558ebSEnrico Granata                 if (!scope_string.empty())
663771ef6d4SMalcolm Parsons                   s.PutCString(scope_string);
664560558ebSEnrico Granata 
665b9c1b51eSKate Stone                 if (m_option_variable.show_decl &&
666b9c1b51eSKate Stone                     var_sp->GetDeclaration().GetFile()) {
667a134cc1bSGreg Clayton                   var_sp->GetDeclaration().DumpStopContext(&s, false);
668a134cc1bSGreg Clayton                   s.PutCString(": ");
669a134cc1bSGreg Clayton                 }
6700c489f58SEnrico Granata 
6710c489f58SEnrico Granata                 options.SetFormat(format);
672b9c1b51eSKate Stone                 options.SetVariableFormatDisplayLanguage(
673b9c1b51eSKate Stone                     valobj_sp->GetPreferredDisplayLanguage());
674f965cc86SZachary Turner                 options.SetRootValueObjectName(
675f965cc86SZachary Turner                     var_sp ? var_sp->GetName().AsCString() : nullptr);
6764d93b8cdSEnrico Granata                 valobj_sp->Dump(result.GetOutputStream(), options);
677a134cc1bSGreg Clayton               }
678a134cc1bSGreg Clayton             }
6796d56d2ceSJim Ingham           }
6806d56d2ceSJim Ingham         }
6816d56d2ceSJim Ingham       }
6826d56d2ceSJim Ingham       result.SetStatus(eReturnStatusSuccessFinishResult);
6836d56d2ceSJim Ingham     }
68461a80ba6SEnrico Granata 
68541ae8e74SKuba Mracek     if (m_option_variable.show_recognized_args) {
68641ae8e74SKuba Mracek       auto recognized_frame = frame->GetRecognizedFrame();
68741ae8e74SKuba Mracek       if (recognized_frame) {
68841ae8e74SKuba Mracek         ValueObjectListSP recognized_arg_list =
68941ae8e74SKuba Mracek             recognized_frame->GetRecognizedArguments();
69041ae8e74SKuba Mracek         if (recognized_arg_list) {
69141ae8e74SKuba Mracek           for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
69241ae8e74SKuba Mracek             options.SetFormat(m_option_format.GetFormat());
69341ae8e74SKuba Mracek             options.SetVariableFormatDisplayLanguage(
69441ae8e74SKuba Mracek                 rec_value_sp->GetPreferredDisplayLanguage());
69541ae8e74SKuba Mracek             options.SetRootValueObjectName(rec_value_sp->GetName().AsCString());
69641ae8e74SKuba Mracek             rec_value_sp->Dump(result.GetOutputStream(), options);
69741ae8e74SKuba Mracek           }
69841ae8e74SKuba Mracek         }
69941ae8e74SKuba Mracek       }
70041ae8e74SKuba Mracek     }
70141ae8e74SKuba Mracek 
702b9c1b51eSKate Stone     if (m_interpreter.TruncationWarningNecessary()) {
70361a80ba6SEnrico Granata       result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(),
70461a80ba6SEnrico Granata                                       m_cmd_name.c_str());
70561a80ba6SEnrico Granata       m_interpreter.TruncationWarningGiven();
70661a80ba6SEnrico Granata     }
70761a80ba6SEnrico Granata 
70824fff242SDavide Italiano     // Increment statistics.
70924fff242SDavide Italiano     bool res = result.Succeeded();
710cb2380c9SRaphael Isemann     Target &target = GetSelectedOrDummyTarget();
71124fff242SDavide Italiano     if (res)
712cb2380c9SRaphael Isemann       target.IncrementStats(StatisticKind::FrameVarSuccess);
71324fff242SDavide Italiano     else
714cb2380c9SRaphael Isemann       target.IncrementStats(StatisticKind::FrameVarFailure);
71524fff242SDavide Italiano     return res;
7166d56d2ceSJim Ingham   }
7176d56d2ceSJim Ingham 
718c8ecc2a9SEugene Zelenko protected:
7192837b766SJim Ingham   OptionGroupOptions m_option_group;
720715c2365SGreg Clayton   OptionGroupVariable m_option_variable;
7211deb7962SGreg Clayton   OptionGroupFormat m_option_format;
7222837b766SJim Ingham   OptionGroupValueObjectDisplay m_varobj_options;
7236d56d2ceSJim Ingham };
7246d56d2ceSJim Ingham 
72541ae8e74SKuba Mracek #pragma mark CommandObjectFrameRecognizer
72641ae8e74SKuba Mracek 
727ec67e734SRaphael Isemann #define LLDB_OPTIONS_frame_recognizer_add
728ec67e734SRaphael Isemann #include "CommandOptions.inc"
72941ae8e74SKuba Mracek 
73041ae8e74SKuba Mracek class CommandObjectFrameRecognizerAdd : public CommandObjectParsed {
73141ae8e74SKuba Mracek private:
73241ae8e74SKuba Mracek   class CommandOptions : public Options {
73341ae8e74SKuba Mracek   public:
73441ae8e74SKuba Mracek     CommandOptions() : Options() {}
73541ae8e74SKuba Mracek     ~CommandOptions() override = default;
73641ae8e74SKuba Mracek 
73741ae8e74SKuba Mracek     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
73841ae8e74SKuba Mracek                           ExecutionContext *execution_context) override {
73941ae8e74SKuba Mracek       Status error;
74041ae8e74SKuba Mracek       const int short_option = m_getopt_table[option_idx].val;
74141ae8e74SKuba Mracek 
74241ae8e74SKuba Mracek       switch (short_option) {
74341ae8e74SKuba Mracek       case 'l':
74441ae8e74SKuba Mracek         m_class_name = std::string(option_arg);
74541ae8e74SKuba Mracek         break;
74641ae8e74SKuba Mracek       case 's':
74741ae8e74SKuba Mracek         m_module = std::string(option_arg);
74841ae8e74SKuba Mracek         break;
74941ae8e74SKuba Mracek       case 'n':
75041ae8e74SKuba Mracek         m_function = std::string(option_arg);
75141ae8e74SKuba Mracek         break;
75241ae8e74SKuba Mracek       case 'x':
75341ae8e74SKuba Mracek         m_regex = true;
75441ae8e74SKuba Mracek         break;
75541ae8e74SKuba Mracek       default:
75636162014SRaphael Isemann         llvm_unreachable("Unimplemented option");
75741ae8e74SKuba Mracek       }
75841ae8e74SKuba Mracek 
75941ae8e74SKuba Mracek       return error;
76041ae8e74SKuba Mracek     }
76141ae8e74SKuba Mracek 
76241ae8e74SKuba Mracek     void OptionParsingStarting(ExecutionContext *execution_context) override {
76341ae8e74SKuba Mracek       m_module = "";
76441ae8e74SKuba Mracek       m_function = "";
76541ae8e74SKuba Mracek       m_class_name = "";
76641ae8e74SKuba Mracek       m_regex = false;
76741ae8e74SKuba Mracek     }
76841ae8e74SKuba Mracek 
76941ae8e74SKuba Mracek     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
77041ae8e74SKuba Mracek       return llvm::makeArrayRef(g_frame_recognizer_add_options);
77141ae8e74SKuba Mracek     }
77241ae8e74SKuba Mracek 
77341ae8e74SKuba Mracek     // Instance variables to hold the values for command options.
77441ae8e74SKuba Mracek     std::string m_class_name;
77541ae8e74SKuba Mracek     std::string m_module;
77641ae8e74SKuba Mracek     std::string m_function;
77741ae8e74SKuba Mracek     bool m_regex;
77841ae8e74SKuba Mracek   };
77941ae8e74SKuba Mracek 
78041ae8e74SKuba Mracek   CommandOptions m_options;
78141ae8e74SKuba Mracek 
78241ae8e74SKuba Mracek   Options *GetOptions() override { return &m_options; }
78341ae8e74SKuba Mracek 
78441ae8e74SKuba Mracek protected:
78541ae8e74SKuba Mracek   bool DoExecute(Args &command, CommandReturnObject &result) override;
78641ae8e74SKuba Mracek 
78741ae8e74SKuba Mracek public:
78841ae8e74SKuba Mracek   CommandObjectFrameRecognizerAdd(CommandInterpreter &interpreter)
78941ae8e74SKuba Mracek       : CommandObjectParsed(interpreter, "frame recognizer add",
79041ae8e74SKuba Mracek                             "Add a new frame recognizer.", nullptr),
79141ae8e74SKuba Mracek         m_options() {
79241ae8e74SKuba Mracek     SetHelpLong(R"(
79341ae8e74SKuba Mracek Frame recognizers allow for retrieving information about special frames based on
79441ae8e74SKuba Mracek ABI, arguments or other special properties of that frame, even without source
79541ae8e74SKuba Mracek code or debug info. Currently, one use case is to extract function arguments
79641ae8e74SKuba Mracek that would otherwise be unaccesible, or augment existing arguments.
79741ae8e74SKuba Mracek 
79841ae8e74SKuba Mracek Adding a custom frame recognizer is possible by implementing a Python class
79941ae8e74SKuba Mracek and using the 'frame recognizer add' command. The Python class should have a
80041ae8e74SKuba Mracek 'get_recognized_arguments' method and it will receive an argument of type
80141ae8e74SKuba Mracek lldb.SBFrame representing the current frame that we are trying to recognize.
80241ae8e74SKuba Mracek The method should return a (possibly empty) list of lldb.SBValue objects that
80341ae8e74SKuba Mracek represent the recognized arguments.
80441ae8e74SKuba Mracek 
80541ae8e74SKuba Mracek An example of a recognizer that retrieves the file descriptor values from libc
80641ae8e74SKuba Mracek functions 'read', 'write' and 'close' follows:
80741ae8e74SKuba Mracek 
80841ae8e74SKuba Mracek   class LibcFdRecognizer(object):
80941ae8e74SKuba Mracek     def get_recognized_arguments(self, frame):
81041ae8e74SKuba Mracek       if frame.name in ["read", "write", "close"]:
81141ae8e74SKuba Mracek         fd = frame.EvaluateExpression("$arg1").unsigned
81241ae8e74SKuba Mracek         value = lldb.target.CreateValueFromExpression("fd", "(int)%d" % fd)
81341ae8e74SKuba Mracek         return [value]
81441ae8e74SKuba Mracek       return []
81541ae8e74SKuba Mracek 
81641ae8e74SKuba Mracek The file containing this implementation can be imported via 'command script
81741ae8e74SKuba Mracek import' and then we can register this recognizer with 'frame recognizer add'.
81841ae8e74SKuba Mracek It's important to restrict the recognizer to the libc library (which is
81941ae8e74SKuba Mracek libsystem_kernel.dylib on macOS) to avoid matching functions with the same name
82041ae8e74SKuba Mracek in other modules:
82141ae8e74SKuba Mracek 
82241ae8e74SKuba Mracek (lldb) command script import .../fd_recognizer.py
82341ae8e74SKuba Mracek (lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib
82441ae8e74SKuba Mracek 
82541ae8e74SKuba Mracek When the program is stopped at the beginning of the 'read' function in libc, we
82641ae8e74SKuba Mracek can view the recognizer arguments in 'frame variable':
82741ae8e74SKuba Mracek 
82841ae8e74SKuba Mracek (lldb) b read
82941ae8e74SKuba Mracek (lldb) r
83041ae8e74SKuba Mracek Process 1234 stopped
83141ae8e74SKuba Mracek * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
83241ae8e74SKuba Mracek     frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read
83341ae8e74SKuba Mracek (lldb) frame variable
83441ae8e74SKuba Mracek (int) fd = 3
83541ae8e74SKuba Mracek 
83641ae8e74SKuba Mracek     )");
83741ae8e74SKuba Mracek   }
83841ae8e74SKuba Mracek   ~CommandObjectFrameRecognizerAdd() override = default;
83941ae8e74SKuba Mracek };
84041ae8e74SKuba Mracek 
84141ae8e74SKuba Mracek bool CommandObjectFrameRecognizerAdd::DoExecute(Args &command,
84241ae8e74SKuba Mracek                                                 CommandReturnObject &result) {
8434e26cf2cSJonas Devlieghere #if LLDB_ENABLE_PYTHON
84441ae8e74SKuba Mracek   if (m_options.m_class_name.empty()) {
84541ae8e74SKuba Mracek     result.AppendErrorWithFormat(
84641ae8e74SKuba Mracek         "%s needs a Python class name (-l argument).\n", m_cmd_name.c_str());
84741ae8e74SKuba Mracek     result.SetStatus(eReturnStatusFailed);
84841ae8e74SKuba Mracek     return false;
84941ae8e74SKuba Mracek   }
85041ae8e74SKuba Mracek 
85141ae8e74SKuba Mracek   if (m_options.m_module.empty()) {
85241ae8e74SKuba Mracek     result.AppendErrorWithFormat("%s needs a module name (-s argument).\n",
85341ae8e74SKuba Mracek                                  m_cmd_name.c_str());
85441ae8e74SKuba Mracek     result.SetStatus(eReturnStatusFailed);
85541ae8e74SKuba Mracek     return false;
85641ae8e74SKuba Mracek   }
85741ae8e74SKuba Mracek 
85841ae8e74SKuba Mracek   if (m_options.m_function.empty()) {
85941ae8e74SKuba Mracek     result.AppendErrorWithFormat("%s needs a function name (-n argument).\n",
86041ae8e74SKuba Mracek                                  m_cmd_name.c_str());
86141ae8e74SKuba Mracek     result.SetStatus(eReturnStatusFailed);
86241ae8e74SKuba Mracek     return false;
86341ae8e74SKuba Mracek   }
86441ae8e74SKuba Mracek 
8652b29b432SJonas Devlieghere   ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
86641ae8e74SKuba Mracek 
86741ae8e74SKuba Mracek   if (interpreter &&
86841ae8e74SKuba Mracek       !interpreter->CheckObjectExists(m_options.m_class_name.c_str())) {
869a925974bSAdrian Prantl     result.AppendWarning("The provided class does not exist - please define it "
87041ae8e74SKuba Mracek                          "before attempting to use this frame recognizer");
87141ae8e74SKuba Mracek   }
87241ae8e74SKuba Mracek 
87341ae8e74SKuba Mracek   StackFrameRecognizerSP recognizer_sp =
87441ae8e74SKuba Mracek       StackFrameRecognizerSP(new ScriptedStackFrameRecognizer(
87541ae8e74SKuba Mracek           interpreter, m_options.m_class_name.c_str()));
87641ae8e74SKuba Mracek   if (m_options.m_regex) {
87741ae8e74SKuba Mracek     auto module =
87841ae8e74SKuba Mracek         RegularExpressionSP(new RegularExpression(m_options.m_module));
87941ae8e74SKuba Mracek     auto func =
88041ae8e74SKuba Mracek         RegularExpressionSP(new RegularExpression(m_options.m_function));
88141ae8e74SKuba Mracek     StackFrameRecognizerManager::AddRecognizer(recognizer_sp, module, func);
88241ae8e74SKuba Mracek   } else {
88341ae8e74SKuba Mracek     auto module = ConstString(m_options.m_module);
88441ae8e74SKuba Mracek     auto func = ConstString(m_options.m_function);
88541ae8e74SKuba Mracek     StackFrameRecognizerManager::AddRecognizer(recognizer_sp, module, func);
88641ae8e74SKuba Mracek   }
887f80d2655SKuba Mracek #endif
88841ae8e74SKuba Mracek 
88941ae8e74SKuba Mracek   result.SetStatus(eReturnStatusSuccessFinishNoResult);
89041ae8e74SKuba Mracek   return result.Succeeded();
89141ae8e74SKuba Mracek }
89241ae8e74SKuba Mracek 
89341ae8e74SKuba Mracek class CommandObjectFrameRecognizerClear : public CommandObjectParsed {
89441ae8e74SKuba Mracek public:
89541ae8e74SKuba Mracek   CommandObjectFrameRecognizerClear(CommandInterpreter &interpreter)
89641ae8e74SKuba Mracek       : CommandObjectParsed(interpreter, "frame recognizer clear",
89741ae8e74SKuba Mracek                             "Delete all frame recognizers.", nullptr) {}
89841ae8e74SKuba Mracek 
89941ae8e74SKuba Mracek   ~CommandObjectFrameRecognizerClear() override = default;
90041ae8e74SKuba Mracek 
90141ae8e74SKuba Mracek protected:
90241ae8e74SKuba Mracek   bool DoExecute(Args &command, CommandReturnObject &result) override {
90341ae8e74SKuba Mracek     StackFrameRecognizerManager::RemoveAllRecognizers();
90441ae8e74SKuba Mracek     result.SetStatus(eReturnStatusSuccessFinishResult);
90541ae8e74SKuba Mracek     return result.Succeeded();
90641ae8e74SKuba Mracek   }
90741ae8e74SKuba Mracek };
90841ae8e74SKuba Mracek 
90941ae8e74SKuba Mracek class CommandObjectFrameRecognizerDelete : public CommandObjectParsed {
91041ae8e74SKuba Mracek public:
91141ae8e74SKuba Mracek   CommandObjectFrameRecognizerDelete(CommandInterpreter &interpreter)
91241ae8e74SKuba Mracek       : CommandObjectParsed(interpreter, "frame recognizer delete",
91341ae8e74SKuba Mracek                             "Delete an existing frame recognizer.", nullptr) {}
91441ae8e74SKuba Mracek 
91541ae8e74SKuba Mracek   ~CommandObjectFrameRecognizerDelete() override = default;
91641ae8e74SKuba Mracek 
91741ae8e74SKuba Mracek protected:
91841ae8e74SKuba Mracek   bool DoExecute(Args &command, CommandReturnObject &result) override {
91941ae8e74SKuba Mracek     if (command.GetArgumentCount() == 0) {
92041ae8e74SKuba Mracek       if (!m_interpreter.Confirm(
92141ae8e74SKuba Mracek               "About to delete all frame recognizers, do you want to do that?",
92241ae8e74SKuba Mracek               true)) {
92341ae8e74SKuba Mracek         result.AppendMessage("Operation cancelled...");
92441ae8e74SKuba Mracek         result.SetStatus(eReturnStatusFailed);
92541ae8e74SKuba Mracek         return false;
92641ae8e74SKuba Mracek       }
92741ae8e74SKuba Mracek 
92841ae8e74SKuba Mracek       StackFrameRecognizerManager::RemoveAllRecognizers();
92941ae8e74SKuba Mracek       result.SetStatus(eReturnStatusSuccessFinishResult);
93041ae8e74SKuba Mracek       return result.Succeeded();
93141ae8e74SKuba Mracek     }
93241ae8e74SKuba Mracek 
93341ae8e74SKuba Mracek     if (command.GetArgumentCount() != 1) {
93441ae8e74SKuba Mracek       result.AppendErrorWithFormat("'%s' takes zero or one arguments.\n",
93541ae8e74SKuba Mracek                                    m_cmd_name.c_str());
93641ae8e74SKuba Mracek       result.SetStatus(eReturnStatusFailed);
93741ae8e74SKuba Mracek       return false;
93841ae8e74SKuba Mracek     }
93941ae8e74SKuba Mracek 
94041ae8e74SKuba Mracek     uint32_t recognizer_id =
94141ae8e74SKuba Mracek         StringConvert::ToUInt32(command.GetArgumentAtIndex(0), 0, 0);
94241ae8e74SKuba Mracek 
94341ae8e74SKuba Mracek     StackFrameRecognizerManager::RemoveRecognizerWithID(recognizer_id);
94441ae8e74SKuba Mracek     result.SetStatus(eReturnStatusSuccessFinishResult);
94541ae8e74SKuba Mracek     return result.Succeeded();
94641ae8e74SKuba Mracek   }
94741ae8e74SKuba Mracek };
94841ae8e74SKuba Mracek 
94941ae8e74SKuba Mracek class CommandObjectFrameRecognizerList : public CommandObjectParsed {
95041ae8e74SKuba Mracek public:
95141ae8e74SKuba Mracek   CommandObjectFrameRecognizerList(CommandInterpreter &interpreter)
95241ae8e74SKuba Mracek       : CommandObjectParsed(interpreter, "frame recognizer list",
95341ae8e74SKuba Mracek                             "Show a list of active frame recognizers.",
95441ae8e74SKuba Mracek                             nullptr) {}
95541ae8e74SKuba Mracek 
95641ae8e74SKuba Mracek   ~CommandObjectFrameRecognizerList() override = default;
95741ae8e74SKuba Mracek 
95841ae8e74SKuba Mracek protected:
95941ae8e74SKuba Mracek   bool DoExecute(Args &command, CommandReturnObject &result) override {
96041ae8e74SKuba Mracek     bool any_printed = false;
96141ae8e74SKuba Mracek     StackFrameRecognizerManager::ForEach(
96241ae8e74SKuba Mracek         [&result, &any_printed](uint32_t recognizer_id, std::string name,
96341ae8e74SKuba Mracek                                 std::string function, std::string symbol,
96441ae8e74SKuba Mracek                                 bool regexp) {
965a925974bSAdrian Prantl           if (name == "")
966a925974bSAdrian Prantl             name = "(internal)";
96741ae8e74SKuba Mracek           result.GetOutputStream().Printf(
96841ae8e74SKuba Mracek               "%d: %s, module %s, function %s%s\n", recognizer_id, name.c_str(),
96941ae8e74SKuba Mracek               function.c_str(), symbol.c_str(), regexp ? " (regexp)" : "");
97041ae8e74SKuba Mracek           any_printed = true;
97141ae8e74SKuba Mracek         });
97241ae8e74SKuba Mracek 
97341ae8e74SKuba Mracek     if (any_printed)
97441ae8e74SKuba Mracek       result.SetStatus(eReturnStatusSuccessFinishResult);
97541ae8e74SKuba Mracek     else {
97641ae8e74SKuba Mracek       result.GetOutputStream().PutCString("no matching results found.\n");
97741ae8e74SKuba Mracek       result.SetStatus(eReturnStatusSuccessFinishNoResult);
97841ae8e74SKuba Mracek     }
97941ae8e74SKuba Mracek     return result.Succeeded();
98041ae8e74SKuba Mracek   }
98141ae8e74SKuba Mracek };
98241ae8e74SKuba Mracek 
98341ae8e74SKuba Mracek class CommandObjectFrameRecognizerInfo : public CommandObjectParsed {
98441ae8e74SKuba Mracek public:
98541ae8e74SKuba Mracek   CommandObjectFrameRecognizerInfo(CommandInterpreter &interpreter)
98641ae8e74SKuba Mracek       : CommandObjectParsed(
98741ae8e74SKuba Mracek             interpreter, "frame recognizer info",
98841ae8e74SKuba Mracek             "Show which frame recognizer is applied a stack frame (if any).",
98941ae8e74SKuba Mracek             nullptr) {
99041ae8e74SKuba Mracek     CommandArgumentEntry arg;
99141ae8e74SKuba Mracek     CommandArgumentData index_arg;
99241ae8e74SKuba Mracek 
99341ae8e74SKuba Mracek     // Define the first (and only) variant of this arg.
99441ae8e74SKuba Mracek     index_arg.arg_type = eArgTypeFrameIndex;
99541ae8e74SKuba Mracek     index_arg.arg_repetition = eArgRepeatPlain;
99641ae8e74SKuba Mracek 
99741ae8e74SKuba Mracek     // There is only one variant this argument could be; put it into the
99841ae8e74SKuba Mracek     // argument entry.
99941ae8e74SKuba Mracek     arg.push_back(index_arg);
100041ae8e74SKuba Mracek 
100141ae8e74SKuba Mracek     // Push the data for the first argument into the m_arguments vector.
100241ae8e74SKuba Mracek     m_arguments.push_back(arg);
100341ae8e74SKuba Mracek   }
100441ae8e74SKuba Mracek 
100541ae8e74SKuba Mracek   ~CommandObjectFrameRecognizerInfo() override = default;
100641ae8e74SKuba Mracek 
100741ae8e74SKuba Mracek protected:
100841ae8e74SKuba Mracek   bool DoExecute(Args &command, CommandReturnObject &result) override {
100941ae8e74SKuba Mracek     Process *process = m_exe_ctx.GetProcessPtr();
101041ae8e74SKuba Mracek     if (process == nullptr) {
101141ae8e74SKuba Mracek       result.AppendError("no process");
101241ae8e74SKuba Mracek       result.SetStatus(eReturnStatusFailed);
101341ae8e74SKuba Mracek       return false;
101441ae8e74SKuba Mracek     }
101541ae8e74SKuba Mracek     Thread *thread = m_exe_ctx.GetThreadPtr();
101641ae8e74SKuba Mracek     if (thread == nullptr) {
101741ae8e74SKuba Mracek       result.AppendError("no thread");
101841ae8e74SKuba Mracek       result.SetStatus(eReturnStatusFailed);
101941ae8e74SKuba Mracek       return false;
102041ae8e74SKuba Mracek     }
102141ae8e74SKuba Mracek     if (command.GetArgumentCount() != 1) {
102241ae8e74SKuba Mracek       result.AppendErrorWithFormat(
102341ae8e74SKuba Mracek           "'%s' takes exactly one frame index argument.\n", m_cmd_name.c_str());
102441ae8e74SKuba Mracek       result.SetStatus(eReturnStatusFailed);
102541ae8e74SKuba Mracek       return false;
102641ae8e74SKuba Mracek     }
102741ae8e74SKuba Mracek 
102841ae8e74SKuba Mracek     uint32_t frame_index =
102941ae8e74SKuba Mracek         StringConvert::ToUInt32(command.GetArgumentAtIndex(0), 0, 0);
103041ae8e74SKuba Mracek     StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_index);
103141ae8e74SKuba Mracek     if (!frame_sp) {
103241ae8e74SKuba Mracek       result.AppendErrorWithFormat("no frame with index %u", frame_index);
103341ae8e74SKuba Mracek       result.SetStatus(eReturnStatusFailed);
103441ae8e74SKuba Mracek       return false;
103541ae8e74SKuba Mracek     }
103641ae8e74SKuba Mracek 
103741ae8e74SKuba Mracek     auto recognizer =
103841ae8e74SKuba Mracek         StackFrameRecognizerManager::GetRecognizerForFrame(frame_sp);
103941ae8e74SKuba Mracek 
104041ae8e74SKuba Mracek     Stream &output_stream = result.GetOutputStream();
104141ae8e74SKuba Mracek     output_stream.Printf("frame %d ", frame_index);
104241ae8e74SKuba Mracek     if (recognizer) {
104341ae8e74SKuba Mracek       output_stream << "is recognized by ";
104441ae8e74SKuba Mracek       output_stream << recognizer->GetName();
104541ae8e74SKuba Mracek     } else {
104641ae8e74SKuba Mracek       output_stream << "not recognized by any recognizer";
104741ae8e74SKuba Mracek     }
104841ae8e74SKuba Mracek     output_stream.EOL();
104941ae8e74SKuba Mracek     result.SetStatus(eReturnStatusSuccessFinishResult);
105041ae8e74SKuba Mracek     return result.Succeeded();
105141ae8e74SKuba Mracek   }
105241ae8e74SKuba Mracek };
105341ae8e74SKuba Mracek 
105441ae8e74SKuba Mracek class CommandObjectFrameRecognizer : public CommandObjectMultiword {
105541ae8e74SKuba Mracek public:
105641ae8e74SKuba Mracek   CommandObjectFrameRecognizer(CommandInterpreter &interpreter)
105741ae8e74SKuba Mracek       : CommandObjectMultiword(
105841ae8e74SKuba Mracek             interpreter, "frame recognizer",
105941ae8e74SKuba Mracek             "Commands for editing and viewing frame recognizers.",
106041ae8e74SKuba Mracek             "frame recognizer [<sub-command-options>] ") {
1061a925974bSAdrian Prantl     LoadSubCommand("add", CommandObjectSP(new CommandObjectFrameRecognizerAdd(
1062a925974bSAdrian Prantl                               interpreter)));
106341ae8e74SKuba Mracek     LoadSubCommand(
106441ae8e74SKuba Mracek         "clear",
106541ae8e74SKuba Mracek         CommandObjectSP(new CommandObjectFrameRecognizerClear(interpreter)));
106641ae8e74SKuba Mracek     LoadSubCommand(
106741ae8e74SKuba Mracek         "delete",
106841ae8e74SKuba Mracek         CommandObjectSP(new CommandObjectFrameRecognizerDelete(interpreter)));
1069a925974bSAdrian Prantl     LoadSubCommand("list", CommandObjectSP(new CommandObjectFrameRecognizerList(
1070a925974bSAdrian Prantl                                interpreter)));
1071a925974bSAdrian Prantl     LoadSubCommand("info", CommandObjectSP(new CommandObjectFrameRecognizerInfo(
1072a925974bSAdrian Prantl                                interpreter)));
107341ae8e74SKuba Mracek   }
107441ae8e74SKuba Mracek 
107541ae8e74SKuba Mracek   ~CommandObjectFrameRecognizer() override = default;
107641ae8e74SKuba Mracek };
107741ae8e74SKuba Mracek 
107830fdc8d8SChris Lattner #pragma mark CommandObjectMultiwordFrame
107930fdc8d8SChris Lattner 
108030fdc8d8SChris Lattner // CommandObjectMultiwordFrame
108130fdc8d8SChris Lattner 
1082b9c1b51eSKate Stone CommandObjectMultiwordFrame::CommandObjectMultiwordFrame(
1083b9c1b51eSKate Stone     CommandInterpreter &interpreter)
1084a925974bSAdrian Prantl     : CommandObjectMultiword(interpreter, "frame",
1085a925974bSAdrian Prantl                              "Commands for selecting and "
1086b9c1b51eSKate Stone                              "examing the current "
1087b9c1b51eSKate Stone                              "thread's stack frames.",
1088b9c1b51eSKate Stone                              "frame <subcommand> [<subcommand-options>]") {
1089b9c1b51eSKate Stone   LoadSubCommand("diagnose",
1090b9c1b51eSKate Stone                  CommandObjectSP(new CommandObjectFrameDiagnose(interpreter)));
1091b9c1b51eSKate Stone   LoadSubCommand("info",
1092b9c1b51eSKate Stone                  CommandObjectSP(new CommandObjectFrameInfo(interpreter)));
1093b9c1b51eSKate Stone   LoadSubCommand("select",
1094b9c1b51eSKate Stone                  CommandObjectSP(new CommandObjectFrameSelect(interpreter)));
1095b9c1b51eSKate Stone   LoadSubCommand("variable",
1096b9c1b51eSKate Stone                  CommandObjectSP(new CommandObjectFrameVariable(interpreter)));
10974e26cf2cSJonas Devlieghere #if LLDB_ENABLE_PYTHON
1098a925974bSAdrian Prantl   LoadSubCommand("recognizer", CommandObjectSP(new CommandObjectFrameRecognizer(
1099a925974bSAdrian Prantl                                    interpreter)));
110041ae8e74SKuba Mracek #endif
110130fdc8d8SChris Lattner }
110230fdc8d8SChris Lattner 
1103c8ecc2a9SEugene Zelenko CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame() = default;
1104