xref: /llvm-project/lldb/source/Commands/CommandObjectFrame.cpp (revision 2a82cd5232e32a449eb9a84f002377e516e6f623)
1 //===-- CommandObjectFrame.cpp ----------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "CommandObjectFrame.h"
11 
12 // C Includes
13 // C++ Includes
14 #include <string>
15 // Other libraries and framework includes
16 // Project includes
17 #include "lldb/Core/DataVisualization.h"
18 #include "lldb/Core/Debugger.h"
19 #include "lldb/Core/Module.h"
20 #include "lldb/Core/StreamFile.h"
21 #include "lldb/Core/StreamString.h"
22 #include "lldb/Core/Timer.h"
23 #include "lldb/Core/Value.h"
24 #include "lldb/Core/ValueObject.h"
25 #include "lldb/Core/ValueObjectVariable.h"
26 #include "lldb/Host/Host.h"
27 #include "lldb/Interpreter/Args.h"
28 #include "lldb/Interpreter/CommandInterpreter.h"
29 #include "lldb/Interpreter/CommandReturnObject.h"
30 #include "lldb/Interpreter/Options.h"
31 #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
32 #include "lldb/Interpreter/OptionGroupVariable.h"
33 #include "lldb/Interpreter/OptionGroupWatchpoint.h"
34 #include "lldb/Symbol/ClangASTType.h"
35 #include "lldb/Symbol/ClangASTContext.h"
36 #include "lldb/Symbol/ObjectFile.h"
37 #include "lldb/Symbol/SymbolContext.h"
38 #include "lldb/Symbol/Type.h"
39 #include "lldb/Symbol/Variable.h"
40 #include "lldb/Symbol/VariableList.h"
41 #include "lldb/Target/Process.h"
42 #include "lldb/Target/StackFrame.h"
43 #include "lldb/Target/Thread.h"
44 #include "lldb/Target/Target.h"
45 
46 using namespace lldb;
47 using namespace lldb_private;
48 
49 #pragma mark CommandObjectFrameInfo
50 
51 //-------------------------------------------------------------------------
52 // CommandObjectFrameInfo
53 //-------------------------------------------------------------------------
54 
55 class CommandObjectFrameInfo : public CommandObject
56 {
57 public:
58 
59     CommandObjectFrameInfo (CommandInterpreter &interpreter) :
60         CommandObject (interpreter,
61                        "frame info",
62                        "List information about the currently selected frame in the current thread.",
63                        "frame info",
64                        eFlagProcessMustBeLaunched | eFlagProcessMustBePaused)
65     {
66     }
67 
68     ~CommandObjectFrameInfo ()
69     {
70     }
71 
72     bool
73     Execute (Args& command,
74              CommandReturnObject &result)
75     {
76         ExecutionContext exe_ctx(m_interpreter.GetExecutionContext());
77         if (exe_ctx.frame)
78         {
79             exe_ctx.frame->DumpUsingSettingsFormat (&result.GetOutputStream());
80             result.SetStatus (eReturnStatusSuccessFinishResult);
81         }
82         else
83         {
84             result.AppendError ("no current frame");
85             result.SetStatus (eReturnStatusFailed);
86         }
87         return result.Succeeded();
88     }
89 };
90 
91 #pragma mark CommandObjectFrameSelect
92 
93 //-------------------------------------------------------------------------
94 // CommandObjectFrameSelect
95 //-------------------------------------------------------------------------
96 
97 class CommandObjectFrameSelect : public CommandObject
98 {
99 public:
100 
101    class CommandOptions : public Options
102     {
103     public:
104 
105         CommandOptions (CommandInterpreter &interpreter) :
106             Options(interpreter)
107         {
108             OptionParsingStarting ();
109         }
110 
111         virtual
112         ~CommandOptions ()
113         {
114         }
115 
116         virtual Error
117         SetOptionValue (uint32_t option_idx, const char *option_arg)
118         {
119             Error error;
120             bool success = false;
121             char short_option = (char) m_getopt_table[option_idx].val;
122             switch (short_option)
123             {
124             case 'r':
125                 relative_frame_offset = Args::StringToSInt32 (option_arg, INT32_MIN, 0, &success);
126                 if (!success)
127                     error.SetErrorStringWithFormat ("invalid frame offset argument '%s'.\n", option_arg);
128                 break;
129 
130             default:
131                 error.SetErrorStringWithFormat ("Invalid short option character '%c'.\n", short_option);
132                 break;
133             }
134 
135             return error;
136         }
137 
138         void
139         OptionParsingStarting ()
140         {
141             relative_frame_offset = INT32_MIN;
142         }
143 
144         const OptionDefinition*
145         GetDefinitions ()
146         {
147             return g_option_table;
148         }
149 
150         // Options table: Required for subclasses of Options.
151 
152         static OptionDefinition g_option_table[];
153         int32_t relative_frame_offset;
154     };
155 
156     CommandObjectFrameSelect (CommandInterpreter &interpreter) :
157         CommandObject (interpreter,
158                        "frame select",
159                        "Select a frame by index from within the current thread and make it the current frame.",
160                        NULL,
161                        eFlagProcessMustBeLaunched | eFlagProcessMustBePaused),
162         m_options (interpreter)
163     {
164         CommandArgumentEntry arg;
165         CommandArgumentData index_arg;
166 
167         // Define the first (and only) variant of this arg.
168         index_arg.arg_type = eArgTypeFrameIndex;
169         index_arg.arg_repetition = eArgRepeatOptional;
170 
171         // There is only one variant this argument could be; put it into the argument entry.
172         arg.push_back (index_arg);
173 
174         // Push the data for the first argument into the m_arguments vector.
175         m_arguments.push_back (arg);
176     }
177 
178     ~CommandObjectFrameSelect ()
179     {
180     }
181 
182     virtual
183     Options *
184     GetOptions ()
185     {
186         return &m_options;
187     }
188 
189 
190     bool
191     Execute (Args& command,
192              CommandReturnObject &result)
193     {
194         ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
195         if (exe_ctx.thread)
196         {
197             const uint32_t num_frames = exe_ctx.thread->GetStackFrameCount();
198             uint32_t frame_idx = UINT32_MAX;
199             if (m_options.relative_frame_offset != INT32_MIN)
200             {
201                 // The one and only argument is a signed relative frame index
202                 frame_idx = exe_ctx.thread->GetSelectedFrameIndex ();
203                 if (frame_idx == UINT32_MAX)
204                     frame_idx = 0;
205 
206                 if (m_options.relative_frame_offset < 0)
207                 {
208                     if (frame_idx >= -m_options.relative_frame_offset)
209                         frame_idx += m_options.relative_frame_offset;
210                     else
211                     {
212                         if (frame_idx == 0)
213                         {
214                             //If you are already at the bottom of the stack, then just warn and don't reset the frame.
215                             result.AppendError("Already at the bottom of the stack");
216                             result.SetStatus(eReturnStatusFailed);
217                             return false;
218                         }
219                         else
220                             frame_idx = 0;
221                     }
222                 }
223                 else if (m_options.relative_frame_offset > 0)
224                 {
225                     if (num_frames - frame_idx > m_options.relative_frame_offset)
226                         frame_idx += m_options.relative_frame_offset;
227                     else
228                     {
229                         if (frame_idx == num_frames - 1)
230                         {
231                             //If we are already at the top of the stack, just warn and don't reset the frame.
232                             result.AppendError("Already at the top of the stack");
233                             result.SetStatus(eReturnStatusFailed);
234                             return false;
235                         }
236                         else
237                             frame_idx = num_frames - 1;
238                     }
239                 }
240             }
241             else
242             {
243                 if (command.GetArgumentCount() == 1)
244                 {
245                     const char *frame_idx_cstr = command.GetArgumentAtIndex(0);
246                     frame_idx = Args::StringToUInt32 (frame_idx_cstr, UINT32_MAX, 0);
247                 }
248                 else
249                 {
250                     result.AppendError ("invalid arguments.\n");
251                     m_options.GenerateOptionUsage (result.GetErrorStream(), this);
252                 }
253             }
254 
255             if (frame_idx < num_frames)
256             {
257                 exe_ctx.thread->SetSelectedFrameByIndex (frame_idx);
258                 exe_ctx.frame = exe_ctx.thread->GetSelectedFrame ().get();
259 
260                 if (exe_ctx.frame)
261                 {
262                     bool already_shown = false;
263                     SymbolContext frame_sc(exe_ctx.frame->GetSymbolContext(eSymbolContextLineEntry));
264                     if (m_interpreter.GetDebugger().GetUseExternalEditor() && frame_sc.line_entry.file && frame_sc.line_entry.line != 0)
265                     {
266                         already_shown = Host::OpenFileInExternalEditor (frame_sc.line_entry.file, frame_sc.line_entry.line);
267                     }
268 
269                     bool show_frame_info = true;
270                     bool show_source = !already_shown;
271                     uint32_t source_lines_before = 3;
272                     uint32_t source_lines_after = 3;
273                     if (exe_ctx.frame->GetStatus(result.GetOutputStream(),
274                                                  show_frame_info,
275                                                  show_source,
276                                                  source_lines_before,
277                                                  source_lines_after))
278                     {
279                         result.SetStatus (eReturnStatusSuccessFinishResult);
280                         return result.Succeeded();
281                     }
282                 }
283             }
284             result.AppendErrorWithFormat ("Frame index (%u) out of range.\n", frame_idx);
285         }
286         else
287         {
288             result.AppendError ("no current thread");
289         }
290         result.SetStatus (eReturnStatusFailed);
291         return false;
292     }
293 protected:
294 
295     CommandOptions m_options;
296 };
297 
298 OptionDefinition
299 CommandObjectFrameSelect::CommandOptions::g_option_table[] =
300 {
301 { LLDB_OPT_SET_1, false, "relative", 'r', required_argument, NULL, 0, eArgTypeOffset, "A relative frame index offset from the current frame index."},
302 { 0, false, NULL, 0, 0, NULL, NULL, eArgTypeNone, NULL }
303 };
304 
305 #pragma mark CommandObjectFrameVariable
306 //----------------------------------------------------------------------
307 // List images with associated information
308 //----------------------------------------------------------------------
309 class CommandObjectFrameVariable : public CommandObject
310 {
311 public:
312 
313     CommandObjectFrameVariable (CommandInterpreter &interpreter) :
314         CommandObject (interpreter,
315                        "frame variable",
316                        "Show frame variables. All argument and local variables "
317                        "that are in scope will be shown when no arguments are given. "
318                        "If any arguments are specified, they can be names of "
319                        "argument, local, file static and file global variables. "
320                        "Children of aggregate variables can be specified such as "
321                        "'var->child.x'. "
322                        "NOTE that '-w' option is not working yet!!! "
323                        "You can choose to watch a variable with the '-w' option. "
324                        "Note that hardware resources for watching are often limited.",
325                        NULL,
326                        eFlagProcessMustBeLaunched | eFlagProcessMustBePaused),
327         m_option_group (interpreter),
328         m_option_variable(true), // Include the frame specific options by passing "true"
329         m_option_watchpoint(),
330         m_varobj_options()
331     {
332         CommandArgumentEntry arg;
333         CommandArgumentData var_name_arg;
334 
335         // Define the first (and only) variant of this arg.
336         var_name_arg.arg_type = eArgTypeVarName;
337         var_name_arg.arg_repetition = eArgRepeatStar;
338 
339         // There is only one variant this argument could be; put it into the argument entry.
340         arg.push_back (var_name_arg);
341 
342         // Push the data for the first argument into the m_arguments vector.
343         m_arguments.push_back (arg);
344 
345         m_option_group.Append (&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
346         m_option_group.Append (&m_option_watchpoint, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
347         m_option_group.Append (&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
348         m_option_group.Finalize();
349     }
350 
351     virtual
352     ~CommandObjectFrameVariable ()
353     {
354     }
355 
356     virtual
357     Options *
358     GetOptions ()
359     {
360         return &m_option_group;
361     }
362 
363 
364     virtual bool
365     Execute
366     (
367         Args& command,
368         CommandReturnObject &result
369     )
370     {
371         ExecutionContext exe_ctx(m_interpreter.GetExecutionContext());
372         if (exe_ctx.frame == NULL)
373         {
374             result.AppendError ("you must be stopped in a valid stack frame to view frame variables.");
375             result.SetStatus (eReturnStatusFailed);
376             return false;
377         }
378 
379         Stream &s = result.GetOutputStream();
380 
381         bool get_file_globals = true;
382 
383         // Be careful about the stack frame, if any summary formatter runs code, it might clear the StackFrameList
384         // for the thread.  So hold onto a shared pointer to the frame so it stays alive.
385 
386         StackFrameSP frame_sp = exe_ctx.frame->GetSP();
387 
388         VariableList *variable_list = frame_sp->GetVariableList (get_file_globals);
389 
390         VariableSP var_sp;
391         ValueObjectSP valobj_sp;
392 
393         const char *name_cstr = NULL;
394         size_t idx;
395 
396         SummaryFormatSP summary_format_sp;
397         if (!m_option_variable.summary.empty())
398             DataVisualization::NamedSummaryFormats::GetSummaryFormat(ConstString(m_option_variable.summary.c_str()), summary_format_sp);
399 
400         ValueObject::DumpValueObjectOptions options;
401 
402         options.SetPointerDepth(m_varobj_options.ptr_depth)
403             .SetMaximumDepth(m_varobj_options.max_depth)
404             .SetShowTypes(m_varobj_options.show_types)
405             .SetShowLocation(m_varobj_options.show_location)
406             .SetUseObjectiveC(m_varobj_options.use_objc)
407             .SetUseDynamicType(m_varobj_options.use_dynamic)
408             .SetUseSyntheticValue((lldb::SyntheticValueType)m_varobj_options.use_synth)
409             .SetFlatOutput(m_varobj_options.flat_output)
410             .SetOmitSummaryDepth(m_varobj_options.no_summary_depth)
411             .SetIgnoreCap(m_varobj_options.ignore_cap);
412 
413         if (m_varobj_options.be_raw)
414             options.SetRawDisplay(true);
415 
416         if (variable_list)
417         {
418             // If watching a variable, there are certain restrictions to be followed.
419             if (m_option_watchpoint.watch_variable)
420             {
421                 if (command.GetArgumentCount() != 1) {
422                     result.GetErrorStream().Printf("error: specify exactly one variable when using the '-w' option\n");
423                     result.SetStatus(eReturnStatusFailed);
424                     return false;
425                 } else if (m_option_variable.use_regex) {
426                     result.GetErrorStream().Printf("error: specify your variable name exactly (no regex) when using the '-w' option\n");
427                     result.SetStatus(eReturnStatusFailed);
428                     return false;
429                 }
430 
431                 // Things have checked out ok...
432                 // m_option_watchpoint.watch_mode specifies the mode for watching.
433             }
434             if (command.GetArgumentCount() > 0)
435             {
436                 VariableList regex_var_list;
437 
438                 // If we have any args to the variable command, we will make
439                 // variable objects from them...
440                 for (idx = 0; (name_cstr = command.GetArgumentAtIndex(idx)) != NULL; ++idx)
441                 {
442                     if (m_option_variable.use_regex)
443                     {
444                         const uint32_t regex_start_index = regex_var_list.GetSize();
445                         RegularExpression regex (name_cstr);
446                         if (regex.Compile(name_cstr))
447                         {
448                             size_t num_matches = 0;
449                             const size_t num_new_regex_vars = variable_list->AppendVariablesIfUnique(regex,
450                                                                                                      regex_var_list,
451                                                                                                      num_matches);
452                             if (num_new_regex_vars > 0)
453                             {
454                                 for (uint32_t regex_idx = regex_start_index, end_index = regex_var_list.GetSize();
455                                      regex_idx < end_index;
456                                      ++regex_idx)
457                                 {
458                                     var_sp = regex_var_list.GetVariableAtIndex (regex_idx);
459                                     if (var_sp)
460                                     {
461                                         valobj_sp = frame_sp->GetValueObjectForFrameVariable (var_sp, m_varobj_options.use_dynamic);
462                                         if (valobj_sp)
463                                         {
464                                             if (m_option_variable.format != eFormatDefault)
465                                                 valobj_sp->SetFormat (m_option_variable.format);
466 
467                                             if (m_option_variable.show_decl && var_sp->GetDeclaration ().GetFile())
468                                             {
469                                                 bool show_fullpaths = false;
470                                                 bool show_module = true;
471                                                 if (var_sp->DumpDeclaration(&s, show_fullpaths, show_module))
472                                                     s.PutCString (": ");
473                                             }
474                                             if (summary_format_sp)
475                                                 valobj_sp->SetCustomSummaryFormat(summary_format_sp);
476                                             ValueObject::DumpValueObject (result.GetOutputStream(),
477                                                                           valobj_sp.get(),
478                                                                           options);
479                                         }
480                                     }
481                                 }
482                             }
483                             else if (num_matches == 0)
484                             {
485                                 result.GetErrorStream().Printf ("error: no variables matched the regular expression '%s'.\n", name_cstr);
486                             }
487                         }
488                         else
489                         {
490                             char regex_error[1024];
491                             if (regex.GetErrorAsCString(regex_error, sizeof(regex_error)))
492                                 result.GetErrorStream().Printf ("error: %s\n", regex_error);
493                             else
494                                 result.GetErrorStream().Printf ("error: unkown regex error when compiling '%s'\n", name_cstr);
495                         }
496                     }
497                     else // No regex, either exact variable names or variable expressions.
498                     {
499                         Error error;
500                         uint32_t expr_path_options = StackFrame::eExpressionPathOptionCheckPtrVsMember;
501                         lldb::VariableSP var_sp;
502                         valobj_sp = frame_sp->GetValueForVariableExpressionPath (name_cstr,
503                                                                                  m_varobj_options.use_dynamic,
504                                                                                  expr_path_options,
505                                                                                  var_sp,
506                                                                                  error);
507                         if (valobj_sp)
508                         {
509                             if (m_option_variable.format != eFormatDefault)
510                                 valobj_sp->SetFormat (m_option_variable.format);
511                             if (m_option_variable.show_decl && var_sp && var_sp->GetDeclaration ().GetFile())
512                             {
513                                 var_sp->GetDeclaration ().DumpStopContext (&s, false);
514                                 s.PutCString (": ");
515                             }
516                             if (summary_format_sp)
517                                 valobj_sp->SetCustomSummaryFormat(summary_format_sp);
518 
519                             Stream &output_stream = result.GetOutputStream();
520                             ValueObject::DumpValueObject (output_stream,
521                                                           valobj_sp.get(),
522                                                           valobj_sp->GetParent() ? name_cstr : NULL,
523                                                           options);
524                             // Process watchpoint if necessary.
525                             if (m_option_watchpoint.watch_variable)
526                             {
527                                 AddressType addr_type;
528                                 lldb::addr_t addr = valobj_sp->GetAddressOf(false, &addr_type);
529                                 size_t size = 0;
530                                 if (addr_type == eAddressTypeLoad) {
531                                     // We're in business.
532                                     // Find out the size of this variable.
533                                     size = valobj_sp->GetByteSize();
534                                 }
535                                 uint32_t watch_type = m_option_watchpoint.watch_type;
536                                 WatchpointLocation *wp_loc =
537                                     exe_ctx.target->CreateWatchpointLocation(addr, size, watch_type).get();
538                                 if (wp_loc)
539                                 {
540                                     if (var_sp && var_sp->GetDeclaration().GetFile())
541                                     {
542                                         StreamString ss;
543                                         // True to show fullpath for declrarion file.
544                                         var_sp->GetDeclaration().DumpStopContext(&ss, true);
545                                         wp_loc->SetDeclInfo(ss.GetString());
546                                     }
547                                     StreamString ss;
548                                     output_stream.Printf("Watchpoint created: ");
549                                     wp_loc->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
550                                     output_stream.EOL();
551                                     result.SetStatus(eReturnStatusSuccessFinishResult);
552                                 }
553                                 else
554                                 {
555                                     result.AppendErrorWithFormat("Watchpoint creation failed.\n");
556                                     result.SetStatus(eReturnStatusFailed);
557                                 }
558                                 return (wp_loc != NULL);
559                             }
560                         }
561                         else
562                         {
563                             const char *error_cstr = error.AsCString(NULL);
564                             if (error_cstr)
565                                 result.GetErrorStream().Printf("error: %s\n", error_cstr);
566                             else
567                                 result.GetErrorStream().Printf ("error: unable to find any variable expression path that matches '%s'\n", name_cstr);
568                         }
569                     }
570                 }
571             }
572             else // No command arg specified.  Use variable_list, instead.
573             {
574                 const uint32_t num_variables = variable_list->GetSize();
575                 if (num_variables > 0)
576                 {
577                     for (uint32_t i=0; i<num_variables; i++)
578                     {
579                         var_sp = variable_list->GetVariableAtIndex(i);
580                         bool dump_variable = true;
581                         switch (var_sp->GetScope())
582                         {
583                             case eValueTypeVariableGlobal:
584                                 dump_variable = m_option_variable.show_globals;
585                                 if (dump_variable && m_option_variable.show_scope)
586                                     s.PutCString("GLOBAL: ");
587                                 break;
588 
589                             case eValueTypeVariableStatic:
590                                 dump_variable = m_option_variable.show_globals;
591                                 if (dump_variable && m_option_variable.show_scope)
592                                     s.PutCString("STATIC: ");
593                                 break;
594 
595                             case eValueTypeVariableArgument:
596                                 dump_variable = m_option_variable.show_args;
597                                 if (dump_variable && m_option_variable.show_scope)
598                                     s.PutCString("   ARG: ");
599                                 break;
600 
601                             case eValueTypeVariableLocal:
602                                 dump_variable = m_option_variable.show_locals;
603                                 if (dump_variable && m_option_variable.show_scope)
604                                     s.PutCString(" LOCAL: ");
605                                 break;
606 
607                             default:
608                                 break;
609                         }
610 
611                         if (dump_variable)
612                         {
613                             // Use the variable object code to make sure we are
614                             // using the same APIs as the the public API will be
615                             // using...
616                             valobj_sp = frame_sp->GetValueObjectForFrameVariable (var_sp,
617                                                                                   m_varobj_options.use_dynamic);
618                             if (valobj_sp)
619                             {
620                                 if (m_option_variable.format != eFormatDefault)
621                                     valobj_sp->SetFormat (m_option_variable.format);
622 
623                                 // When dumping all variables, don't print any variables
624                                 // that are not in scope to avoid extra unneeded output
625                                 if (valobj_sp->IsInScope ())
626                                 {
627                                     if (m_option_variable.show_decl && var_sp->GetDeclaration ().GetFile())
628                                     {
629                                         var_sp->GetDeclaration ().DumpStopContext (&s, false);
630                                         s.PutCString (": ");
631                                     }
632                                     if (summary_format_sp)
633                                         valobj_sp->SetCustomSummaryFormat(summary_format_sp);
634                                     ValueObject::DumpValueObject (result.GetOutputStream(),
635                                                                   valobj_sp.get(),
636                                                                   name_cstr,
637                                                                   options);
638                                 }
639                             }
640                         }
641                     }
642                 }
643             }
644             result.SetStatus (eReturnStatusSuccessFinishResult);
645         }
646 
647         if (m_interpreter.TruncationWarningNecessary())
648         {
649             result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(),
650                                             m_cmd_name.c_str());
651             m_interpreter.TruncationWarningGiven();
652         }
653 
654         return result.Succeeded();
655     }
656 protected:
657 
658     OptionGroupOptions m_option_group;
659     OptionGroupVariable m_option_variable;
660     OptionGroupWatchpoint m_option_watchpoint;
661     OptionGroupValueObjectDisplay m_varobj_options;
662 };
663 
664 
665 #pragma mark CommandObjectMultiwordFrame
666 
667 //-------------------------------------------------------------------------
668 // CommandObjectMultiwordFrame
669 //-------------------------------------------------------------------------
670 
671 CommandObjectMultiwordFrame::CommandObjectMultiwordFrame (CommandInterpreter &interpreter) :
672     CommandObjectMultiword (interpreter,
673                             "frame",
674                             "A set of commands for operating on the current thread's frames.",
675                             "frame <subcommand> [<subcommand-options>]")
676 {
677     LoadSubCommand ("info",   CommandObjectSP (new CommandObjectFrameInfo (interpreter)));
678     LoadSubCommand ("select", CommandObjectSP (new CommandObjectFrameSelect (interpreter)));
679     LoadSubCommand ("variable", CommandObjectSP (new CommandObjectFrameVariable (interpreter)));
680 }
681 
682 CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame ()
683 {
684 }
685 
686