1254721Semaste//===-- CommandObjectLog.cpp ------------------------------------*- C++ -*-===//
2254721Semaste//
3254721Semaste//                     The LLVM Compiler Infrastructure
4254721Semaste//
5254721Semaste// This file is distributed under the University of Illinois Open Source
6254721Semaste// License. See LICENSE.TXT for details.
7254721Semaste//
8254721Semaste//===----------------------------------------------------------------------===//
9254721Semaste
10254721Semaste#include "lldb/lldb-python.h"
11254721Semaste
12254721Semaste#include "CommandObjectLog.h"
13254721Semaste
14254721Semaste// C Includes
15254721Semaste// C++ Includes
16254721Semaste// Other libraries and framework includes
17254721Semaste// Project includes
18254721Semaste#include "lldb/lldb-private-log.h"
19254721Semaste
20254721Semaste#include "lldb/Interpreter/Args.h"
21254721Semaste#include "lldb/Core/Debugger.h"
22254721Semaste#include "lldb/Host/FileSpec.h"
23254721Semaste#include "lldb/Core/Log.h"
24254721Semaste#include "lldb/Core/Module.h"
25254721Semaste#include "lldb/Interpreter/Options.h"
26254721Semaste#include "lldb/Core/RegularExpression.h"
27254721Semaste#include "lldb/Core/Stream.h"
28254721Semaste#include "lldb/Core/StreamFile.h"
29254721Semaste#include "lldb/Core/Timer.h"
30254721Semaste
31254721Semaste#include "lldb/Core/Debugger.h"
32254721Semaste#include "lldb/Interpreter/CommandInterpreter.h"
33254721Semaste#include "lldb/Interpreter/CommandReturnObject.h"
34254721Semaste
35254721Semaste#include "lldb/Symbol/LineTable.h"
36254721Semaste#include "lldb/Symbol/ObjectFile.h"
37254721Semaste#include "lldb/Symbol/SymbolFile.h"
38254721Semaste#include "lldb/Symbol/SymbolVendor.h"
39254721Semaste
40254721Semaste#include "lldb/Target/Process.h"
41254721Semaste#include "lldb/Target/Target.h"
42254721Semaste
43254721Semasteusing namespace lldb;
44254721Semasteusing namespace lldb_private;
45254721Semaste
46254721Semaste
47254721Semasteclass CommandObjectLogEnable : public CommandObjectParsed
48254721Semaste{
49254721Semastepublic:
50254721Semaste    //------------------------------------------------------------------
51254721Semaste    // Constructors and Destructors
52254721Semaste    //------------------------------------------------------------------
53254721Semaste    CommandObjectLogEnable(CommandInterpreter &interpreter) :
54254721Semaste        CommandObjectParsed (interpreter,
55254721Semaste                             "log enable",
56254721Semaste                             "Enable logging for a single log channel.",
57254721Semaste                             NULL),
58254721Semaste        m_options (interpreter)
59254721Semaste    {
60254721Semaste
61254721Semaste        CommandArgumentEntry arg1;
62254721Semaste        CommandArgumentEntry arg2;
63254721Semaste        CommandArgumentData channel_arg;
64254721Semaste        CommandArgumentData category_arg;
65254721Semaste
66254721Semaste        // Define the first (and only) variant of this arg.
67254721Semaste        channel_arg.arg_type = eArgTypeLogChannel;
68254721Semaste        channel_arg.arg_repetition = eArgRepeatPlain;
69254721Semaste
70254721Semaste        // There is only one variant this argument could be; put it into the argument entry.
71254721Semaste        arg1.push_back (channel_arg);
72254721Semaste
73254721Semaste        category_arg.arg_type = eArgTypeLogCategory;
74254721Semaste        category_arg.arg_repetition = eArgRepeatPlus;
75254721Semaste
76254721Semaste        arg2.push_back (category_arg);
77254721Semaste
78254721Semaste        // Push the data for the first argument into the m_arguments vector.
79254721Semaste        m_arguments.push_back (arg1);
80254721Semaste        m_arguments.push_back (arg2);
81254721Semaste    }
82254721Semaste
83254721Semaste    virtual
84254721Semaste    ~CommandObjectLogEnable()
85254721Semaste    {
86254721Semaste    }
87254721Semaste
88254721Semaste    Options *
89254721Semaste    GetOptions ()
90254721Semaste    {
91254721Semaste        return &m_options;
92254721Semaste    }
93254721Semaste
94254721Semaste//    int
95254721Semaste//    HandleArgumentCompletion (Args &input,
96254721Semaste//                              int &cursor_index,
97254721Semaste//                              int &cursor_char_position,
98254721Semaste//                              OptionElementVector &opt_element_vector,
99254721Semaste//                              int match_start_point,
100254721Semaste//                              int max_return_elements,
101254721Semaste//                              bool &word_complete,
102254721Semaste//                              StringList &matches)
103254721Semaste//    {
104254721Semaste//        std::string completion_str (input.GetArgumentAtIndex(cursor_index));
105254721Semaste//        completion_str.erase (cursor_char_position);
106254721Semaste//
107254721Semaste//        if (cursor_index == 1)
108254721Semaste//        {
109254721Semaste//            //
110254721Semaste//            Log::AutoCompleteChannelName (completion_str.c_str(), matches);
111254721Semaste//        }
112254721Semaste//        return matches.GetSize();
113254721Semaste//    }
114254721Semaste//
115254721Semaste
116254721Semaste    class CommandOptions : public Options
117254721Semaste    {
118254721Semaste    public:
119254721Semaste
120254721Semaste        CommandOptions (CommandInterpreter &interpreter) :
121254721Semaste            Options (interpreter),
122254721Semaste            log_file (),
123254721Semaste            log_options (0)
124254721Semaste        {
125254721Semaste        }
126254721Semaste
127254721Semaste
128254721Semaste        virtual
129254721Semaste        ~CommandOptions ()
130254721Semaste        {
131254721Semaste        }
132254721Semaste
133254721Semaste        virtual Error
134254721Semaste        SetOptionValue (uint32_t option_idx, const char *option_arg)
135254721Semaste        {
136254721Semaste            Error error;
137254721Semaste            const int short_option = m_getopt_table[option_idx].val;
138254721Semaste
139254721Semaste            switch (short_option)
140254721Semaste            {
141254721Semaste            case 'f':  log_file.SetFile(option_arg, true);                    break;
142254721Semaste            case 't':  log_options |= LLDB_LOG_OPTION_THREADSAFE;             break;
143254721Semaste            case 'v':  log_options |= LLDB_LOG_OPTION_VERBOSE;                break;
144254721Semaste            case 'g':  log_options |= LLDB_LOG_OPTION_DEBUG;                  break;
145254721Semaste            case 's':  log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE;       break;
146254721Semaste            case 'T':  log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP;      break;
147254721Semaste            case 'p':  log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD;break;
148254721Semaste            case 'n':  log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME;    break;
149254721Semaste            case 'S':  log_options |= LLDB_LOG_OPTION_BACKTRACE;              break;
150254721Semaste            default:
151254721Semaste                error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option);
152254721Semaste                break;
153254721Semaste            }
154254721Semaste
155254721Semaste            return error;
156254721Semaste        }
157254721Semaste
158254721Semaste        void
159254721Semaste        OptionParsingStarting ()
160254721Semaste        {
161254721Semaste            log_file.Clear();
162254721Semaste            log_options = 0;
163254721Semaste        }
164254721Semaste
165254721Semaste        const OptionDefinition*
166254721Semaste        GetDefinitions ()
167254721Semaste        {
168254721Semaste            return g_option_table;
169254721Semaste        }
170254721Semaste
171254721Semaste        // Options table: Required for subclasses of Options.
172254721Semaste
173254721Semaste        static OptionDefinition g_option_table[];
174254721Semaste
175254721Semaste        // Instance variables to hold the values for command options.
176254721Semaste
177254721Semaste        FileSpec log_file;
178254721Semaste        uint32_t log_options;
179254721Semaste    };
180254721Semaste
181254721Semasteprotected:
182254721Semaste    virtual bool
183254721Semaste    DoExecute (Args& args,
184254721Semaste             CommandReturnObject &result)
185254721Semaste    {
186254721Semaste        if (args.GetArgumentCount() < 2)
187254721Semaste        {
188254721Semaste            result.AppendErrorWithFormat("%s takes a log channel and one or more log types.\n", m_cmd_name.c_str());
189254721Semaste        }
190254721Semaste        else
191254721Semaste        {
192254721Semaste            std::string channel(args.GetArgumentAtIndex(0));
193254721Semaste            args.Shift ();  // Shift off the channel
194254721Semaste            char log_file[PATH_MAX];
195254721Semaste            if (m_options.log_file)
196254721Semaste                m_options.log_file.GetPath(log_file, sizeof(log_file));
197254721Semaste            else
198254721Semaste                log_file[0] = '\0';
199254721Semaste            bool success = m_interpreter.GetDebugger().EnableLog (channel.c_str(),
200254721Semaste                                                                  args.GetConstArgumentVector(),
201254721Semaste                                                                  log_file,
202254721Semaste                                                                  m_options.log_options,
203254721Semaste                                                                  result.GetErrorStream());
204254721Semaste            if (success)
205254721Semaste                result.SetStatus (eReturnStatusSuccessFinishNoResult);
206254721Semaste            else
207254721Semaste                result.SetStatus (eReturnStatusFailed);
208254721Semaste        }
209254721Semaste        return result.Succeeded();
210254721Semaste    }
211254721Semaste
212254721Semaste    CommandOptions m_options;
213254721Semaste};
214254721Semaste
215254721SemasteOptionDefinition
216254721SemasteCommandObjectLogEnable::CommandOptions::g_option_table[] =
217254721Semaste{
218263363Semaste{ LLDB_OPT_SET_1, false, "file",       'f', OptionParser::eRequiredArgument, NULL, 0, eArgTypeFilename,   "Set the destination file to log to."},
219263363Semaste{ LLDB_OPT_SET_1, false, "threadsafe", 't', OptionParser::eNoArgument,       NULL, 0, eArgTypeNone,        "Enable thread safe logging to avoid interweaved log lines." },
220263363Semaste{ LLDB_OPT_SET_1, false, "verbose",    'v', OptionParser::eNoArgument,       NULL, 0, eArgTypeNone,       "Enable verbose logging." },
221263363Semaste{ LLDB_OPT_SET_1, false, "debug",      'g', OptionParser::eNoArgument,       NULL, 0, eArgTypeNone,       "Enable debug logging." },
222263363Semaste{ LLDB_OPT_SET_1, false, "sequence",   's', OptionParser::eNoArgument,       NULL, 0, eArgTypeNone,       "Prepend all log lines with an increasing integer sequence id." },
223263363Semaste{ LLDB_OPT_SET_1, false, "timestamp",  'T', OptionParser::eNoArgument,       NULL, 0, eArgTypeNone,       "Prepend all log lines with a timestamp." },
224263363Semaste{ LLDB_OPT_SET_1, false, "pid-tid",    'p', OptionParser::eNoArgument,       NULL, 0, eArgTypeNone,       "Prepend all log lines with the process and thread ID that generates the log line." },
225263363Semaste{ LLDB_OPT_SET_1, false, "thread-name",'n', OptionParser::eNoArgument,       NULL, 0, eArgTypeNone,       "Prepend all log lines with the thread name for the thread that generates the log line." },
226263363Semaste{ LLDB_OPT_SET_1, false, "stack",      'S', OptionParser::eNoArgument,       NULL, 0, eArgTypeNone,       "Append a stack backtrace to each log line." },
227254721Semaste{ 0, false, NULL,                       0,  0,                 NULL, 0, eArgTypeNone,       NULL }
228254721Semaste};
229254721Semaste
230254721Semasteclass CommandObjectLogDisable : public CommandObjectParsed
231254721Semaste{
232254721Semastepublic:
233254721Semaste    //------------------------------------------------------------------
234254721Semaste    // Constructors and Destructors
235254721Semaste    //------------------------------------------------------------------
236254721Semaste    CommandObjectLogDisable(CommandInterpreter &interpreter) :
237254721Semaste        CommandObjectParsed (interpreter,
238254721Semaste                             "log disable",
239254721Semaste                             "Disable one or more log channel categories.",
240254721Semaste                             NULL)
241254721Semaste    {
242254721Semaste        CommandArgumentEntry arg1;
243254721Semaste        CommandArgumentEntry arg2;
244254721Semaste        CommandArgumentData channel_arg;
245254721Semaste        CommandArgumentData category_arg;
246254721Semaste
247254721Semaste        // Define the first (and only) variant of this arg.
248254721Semaste        channel_arg.arg_type = eArgTypeLogChannel;
249254721Semaste        channel_arg.arg_repetition = eArgRepeatPlain;
250254721Semaste
251254721Semaste        // There is only one variant this argument could be; put it into the argument entry.
252254721Semaste        arg1.push_back (channel_arg);
253254721Semaste
254254721Semaste        category_arg.arg_type = eArgTypeLogCategory;
255254721Semaste        category_arg.arg_repetition = eArgRepeatPlus;
256254721Semaste
257254721Semaste        arg2.push_back (category_arg);
258254721Semaste
259254721Semaste        // Push the data for the first argument into the m_arguments vector.
260254721Semaste        m_arguments.push_back (arg1);
261254721Semaste        m_arguments.push_back (arg2);
262254721Semaste    }
263254721Semaste
264254721Semaste    virtual
265254721Semaste    ~CommandObjectLogDisable()
266254721Semaste    {
267254721Semaste    }
268254721Semaste
269254721Semasteprotected:
270254721Semaste    virtual bool
271254721Semaste    DoExecute (Args& args,
272254721Semaste             CommandReturnObject &result)
273254721Semaste    {
274254721Semaste        const size_t argc = args.GetArgumentCount();
275254721Semaste        if (argc == 0)
276254721Semaste        {
277254721Semaste            result.AppendErrorWithFormat("%s takes a log channel and one or more log types.\n", m_cmd_name.c_str());
278254721Semaste        }
279254721Semaste        else
280254721Semaste        {
281254721Semaste            Log::Callbacks log_callbacks;
282254721Semaste
283254721Semaste            std::string channel(args.GetArgumentAtIndex(0));
284254721Semaste            args.Shift ();  // Shift off the channel
285254721Semaste            if (Log::GetLogChannelCallbacks (ConstString(channel.c_str()), log_callbacks))
286254721Semaste            {
287254721Semaste                log_callbacks.disable (args.GetConstArgumentVector(), &result.GetErrorStream());
288254721Semaste                result.SetStatus(eReturnStatusSuccessFinishNoResult);
289254721Semaste            }
290254721Semaste            else if (channel == "all")
291254721Semaste            {
292254721Semaste                Log::DisableAllLogChannels(&result.GetErrorStream());
293254721Semaste            }
294254721Semaste            else
295254721Semaste            {
296254721Semaste                LogChannelSP log_channel_sp (LogChannel::FindPlugin(channel.c_str()));
297254721Semaste                if (log_channel_sp)
298254721Semaste                {
299254721Semaste                    log_channel_sp->Disable(args.GetConstArgumentVector(), &result.GetErrorStream());
300254721Semaste                    result.SetStatus(eReturnStatusSuccessFinishNoResult);
301254721Semaste                }
302254721Semaste                else
303254721Semaste                    result.AppendErrorWithFormat("Invalid log channel '%s'.\n", args.GetArgumentAtIndex(0));
304254721Semaste            }
305254721Semaste        }
306254721Semaste        return result.Succeeded();
307254721Semaste    }
308254721Semaste};
309254721Semaste
310254721Semasteclass CommandObjectLogList : public CommandObjectParsed
311254721Semaste{
312254721Semastepublic:
313254721Semaste    //------------------------------------------------------------------
314254721Semaste    // Constructors and Destructors
315254721Semaste    //------------------------------------------------------------------
316254721Semaste    CommandObjectLogList(CommandInterpreter &interpreter) :
317254721Semaste        CommandObjectParsed (interpreter,
318254721Semaste                             "log list",
319254721Semaste                             "List the log categories for one or more log channels.  If none specified, lists them all.",
320254721Semaste                             NULL)
321254721Semaste    {
322254721Semaste        CommandArgumentEntry arg;
323254721Semaste        CommandArgumentData channel_arg;
324254721Semaste
325254721Semaste        // Define the first (and only) variant of this arg.
326254721Semaste        channel_arg.arg_type = eArgTypeLogChannel;
327254721Semaste        channel_arg.arg_repetition = eArgRepeatStar;
328254721Semaste
329254721Semaste        // There is only one variant this argument could be; put it into the argument entry.
330254721Semaste        arg.push_back (channel_arg);
331254721Semaste
332254721Semaste        // Push the data for the first argument into the m_arguments vector.
333254721Semaste        m_arguments.push_back (arg);
334254721Semaste    }
335254721Semaste
336254721Semaste    virtual
337254721Semaste    ~CommandObjectLogList()
338254721Semaste    {
339254721Semaste    }
340254721Semaste
341254721Semasteprotected:
342254721Semaste    virtual bool
343254721Semaste    DoExecute (Args& args,
344254721Semaste             CommandReturnObject &result)
345254721Semaste    {
346254721Semaste        const size_t argc = args.GetArgumentCount();
347254721Semaste        if (argc == 0)
348254721Semaste        {
349254721Semaste            Log::ListAllLogChannels (&result.GetOutputStream());
350254721Semaste            result.SetStatus(eReturnStatusSuccessFinishResult);
351254721Semaste        }
352254721Semaste        else
353254721Semaste        {
354254721Semaste            for (size_t i=0; i<argc; ++i)
355254721Semaste            {
356254721Semaste                Log::Callbacks log_callbacks;
357254721Semaste
358254721Semaste                std::string channel(args.GetArgumentAtIndex(i));
359254721Semaste                if (Log::GetLogChannelCallbacks (ConstString(channel.c_str()), log_callbacks))
360254721Semaste                {
361254721Semaste                    log_callbacks.list_categories (&result.GetOutputStream());
362254721Semaste                    result.SetStatus(eReturnStatusSuccessFinishResult);
363254721Semaste                }
364254721Semaste                else if (channel == "all")
365254721Semaste                {
366254721Semaste                    Log::ListAllLogChannels (&result.GetOutputStream());
367254721Semaste                    result.SetStatus(eReturnStatusSuccessFinishResult);
368254721Semaste                }
369254721Semaste                else
370254721Semaste                {
371254721Semaste                    LogChannelSP log_channel_sp (LogChannel::FindPlugin(channel.c_str()));
372254721Semaste                    if (log_channel_sp)
373254721Semaste                    {
374254721Semaste                        log_channel_sp->ListCategories(&result.GetOutputStream());
375254721Semaste                        result.SetStatus(eReturnStatusSuccessFinishNoResult);
376254721Semaste                    }
377254721Semaste                    else
378254721Semaste                        result.AppendErrorWithFormat("Invalid log channel '%s'.\n", args.GetArgumentAtIndex(0));
379254721Semaste                }
380254721Semaste            }
381254721Semaste        }
382254721Semaste        return result.Succeeded();
383254721Semaste    }
384254721Semaste};
385254721Semaste
386254721Semasteclass CommandObjectLogTimer : public CommandObjectParsed
387254721Semaste{
388254721Semastepublic:
389254721Semaste    //------------------------------------------------------------------
390254721Semaste    // Constructors and Destructors
391254721Semaste    //------------------------------------------------------------------
392254721Semaste    CommandObjectLogTimer(CommandInterpreter &interpreter) :
393254721Semaste        CommandObjectParsed (interpreter,
394254721Semaste                           "log timers",
395254721Semaste                           "Enable, disable, dump, and reset LLDB internal performance timers.",
396254721Semaste                           "log timers < enable <depth> | disable | dump | increment <bool> | reset >")
397254721Semaste    {
398254721Semaste    }
399254721Semaste
400254721Semaste    virtual
401254721Semaste    ~CommandObjectLogTimer()
402254721Semaste    {
403254721Semaste    }
404254721Semaste
405254721Semasteprotected:
406254721Semaste    virtual bool
407254721Semaste    DoExecute (Args& args,
408254721Semaste             CommandReturnObject &result)
409254721Semaste    {
410254721Semaste        const size_t argc = args.GetArgumentCount();
411254721Semaste        result.SetStatus(eReturnStatusFailed);
412254721Semaste
413254721Semaste        if (argc == 1)
414254721Semaste        {
415254721Semaste            const char *sub_command = args.GetArgumentAtIndex(0);
416254721Semaste
417254721Semaste            if (strcasecmp(sub_command, "enable") == 0)
418254721Semaste            {
419254721Semaste                Timer::SetDisplayDepth (UINT32_MAX);
420254721Semaste                result.SetStatus(eReturnStatusSuccessFinishNoResult);
421254721Semaste            }
422254721Semaste            else if (strcasecmp(sub_command, "disable") == 0)
423254721Semaste            {
424254721Semaste                Timer::DumpCategoryTimes (&result.GetOutputStream());
425254721Semaste                Timer::SetDisplayDepth (0);
426254721Semaste                result.SetStatus(eReturnStatusSuccessFinishResult);
427254721Semaste            }
428254721Semaste            else if (strcasecmp(sub_command, "dump") == 0)
429254721Semaste            {
430254721Semaste                Timer::DumpCategoryTimes (&result.GetOutputStream());
431254721Semaste                result.SetStatus(eReturnStatusSuccessFinishResult);
432254721Semaste            }
433254721Semaste            else if (strcasecmp(sub_command, "reset") == 0)
434254721Semaste            {
435254721Semaste                Timer::ResetCategoryTimes ();
436254721Semaste                result.SetStatus(eReturnStatusSuccessFinishResult);
437254721Semaste            }
438254721Semaste
439254721Semaste        }
440254721Semaste        else if (argc == 2)
441254721Semaste        {
442254721Semaste            const char *sub_command = args.GetArgumentAtIndex(0);
443254721Semaste
444254721Semaste            if (strcasecmp(sub_command, "enable") == 0)
445254721Semaste            {
446254721Semaste                bool success;
447254721Semaste                uint32_t depth = Args::StringToUInt32(args.GetArgumentAtIndex(1), 0, 0, &success);
448254721Semaste                if (success)
449254721Semaste                {
450254721Semaste                    Timer::SetDisplayDepth (depth);
451254721Semaste                    result.SetStatus(eReturnStatusSuccessFinishNoResult);
452254721Semaste                }
453254721Semaste                else
454254721Semaste                    result.AppendError("Could not convert enable depth to an unsigned integer.");
455254721Semaste            }
456254721Semaste            if (strcasecmp(sub_command, "increment") == 0)
457254721Semaste            {
458254721Semaste                bool success;
459254721Semaste                bool increment = Args::StringToBoolean(args.GetArgumentAtIndex(1), false, &success);
460254721Semaste                if (success)
461254721Semaste                {
462254721Semaste                    Timer::SetQuiet (!increment);
463254721Semaste                    result.SetStatus(eReturnStatusSuccessFinishNoResult);
464254721Semaste                }
465254721Semaste                else
466254721Semaste                    result.AppendError("Could not convert increment value to boolean.");
467254721Semaste            }
468254721Semaste        }
469254721Semaste
470254721Semaste        if (!result.Succeeded())
471254721Semaste        {
472254721Semaste            result.AppendError("Missing subcommand");
473254721Semaste            result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
474254721Semaste        }
475254721Semaste        return result.Succeeded();
476254721Semaste    }
477254721Semaste};
478254721Semaste
479254721Semaste//----------------------------------------------------------------------
480254721Semaste// CommandObjectLog constructor
481254721Semaste//----------------------------------------------------------------------
482254721SemasteCommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter) :
483254721Semaste    CommandObjectMultiword (interpreter,
484254721Semaste                            "log",
485254721Semaste                            "A set of commands for operating on logs.",
486254721Semaste                            "log <command> [<command-options>]")
487254721Semaste{
488254721Semaste    LoadSubCommand ("enable",  CommandObjectSP (new CommandObjectLogEnable (interpreter)));
489254721Semaste    LoadSubCommand ("disable", CommandObjectSP (new CommandObjectLogDisable (interpreter)));
490254721Semaste    LoadSubCommand ("list",    CommandObjectSP (new CommandObjectLogList (interpreter)));
491254721Semaste    LoadSubCommand ("timers",  CommandObjectSP (new CommandObjectLogTimer (interpreter)));
492254721Semaste}
493254721Semaste
494254721Semaste//----------------------------------------------------------------------
495254721Semaste// Destructor
496254721Semaste//----------------------------------------------------------------------
497254721SemasteCommandObjectLog::~CommandObjectLog()
498254721Semaste{
499254721Semaste}
500254721Semaste
501254721Semaste
502254721Semaste
503254721Semaste
504