1 | //===-- CommandObjectLog.cpp ----------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "CommandObjectLog.h" |
10 | #include "lldb/Core/Debugger.h" |
11 | #include "lldb/Host/OptionParser.h" |
12 | #include "lldb/Interpreter/CommandOptionArgumentTable.h" |
13 | #include "lldb/Interpreter/CommandReturnObject.h" |
14 | #include "lldb/Interpreter/OptionArgParser.h" |
15 | #include "lldb/Interpreter/OptionValueEnumeration.h" |
16 | #include "lldb/Interpreter/OptionValueUInt64.h" |
17 | #include "lldb/Interpreter/Options.h" |
18 | #include "lldb/Utility/Args.h" |
19 | #include "lldb/Utility/FileSpec.h" |
20 | #include "lldb/Utility/Log.h" |
21 | #include "lldb/Utility/Stream.h" |
22 | #include "lldb/Utility/Timer.h" |
23 | |
24 | using namespace lldb; |
25 | using namespace lldb_private; |
26 | |
27 | #define LLDB_OPTIONS_log_enable |
28 | #include "CommandOptions.inc" |
29 | |
30 | #define LLDB_OPTIONS_log_dump |
31 | #include "CommandOptions.inc" |
32 | |
33 | /// Common completion logic for log enable/disable. |
34 | static void CompleteEnableDisable(CompletionRequest &request) { |
35 | size_t arg_index = request.GetCursorIndex(); |
36 | if (arg_index == 0) { // We got: log enable/disable x[tab] |
37 | for (llvm::StringRef channel : Log::ListChannels()) |
38 | request.TryCompleteCurrentArg(completion: channel); |
39 | } else if (arg_index >= 1) { // We got: log enable/disable channel x[tab] |
40 | llvm::StringRef channel = request.GetParsedLine().GetArgumentAtIndex(idx: 0); |
41 | Log::ForEachChannelCategory( |
42 | channel, lambda: [&request](llvm::StringRef name, llvm::StringRef desc) { |
43 | request.TryCompleteCurrentArg(completion: name, description: desc); |
44 | }); |
45 | } |
46 | } |
47 | |
48 | class CommandObjectLogEnable : public CommandObjectParsed { |
49 | public: |
50 | // Constructors and Destructors |
51 | CommandObjectLogEnable(CommandInterpreter &interpreter) |
52 | : CommandObjectParsed(interpreter, "log enable" , |
53 | "Enable logging for a single log channel." , |
54 | nullptr) { |
55 | CommandArgumentEntry arg1; |
56 | CommandArgumentEntry arg2; |
57 | CommandArgumentData channel_arg; |
58 | CommandArgumentData category_arg; |
59 | |
60 | // Define the first (and only) variant of this arg. |
61 | channel_arg.arg_type = eArgTypeLogChannel; |
62 | channel_arg.arg_repetition = eArgRepeatPlain; |
63 | |
64 | // There is only one variant this argument could be; put it into the |
65 | // argument entry. |
66 | arg1.push_back(x: channel_arg); |
67 | |
68 | category_arg.arg_type = eArgTypeLogCategory; |
69 | category_arg.arg_repetition = eArgRepeatPlus; |
70 | |
71 | arg2.push_back(x: category_arg); |
72 | |
73 | // Push the data for the first argument into the m_arguments vector. |
74 | m_arguments.push_back(x: arg1); |
75 | m_arguments.push_back(x: arg2); |
76 | } |
77 | |
78 | ~CommandObjectLogEnable() override = default; |
79 | |
80 | Options *GetOptions() override { return &m_options; } |
81 | |
82 | class CommandOptions : public Options { |
83 | public: |
84 | CommandOptions() = default; |
85 | |
86 | ~CommandOptions() override = default; |
87 | |
88 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
89 | ExecutionContext *execution_context) override { |
90 | Status error; |
91 | const int short_option = m_getopt_table[option_idx].val; |
92 | |
93 | switch (short_option) { |
94 | case 'f': |
95 | log_file.SetFile(path: option_arg, style: FileSpec::Style::native); |
96 | FileSystem::Instance().Resolve(file_spec&: log_file); |
97 | break; |
98 | case 'h': |
99 | handler = (LogHandlerKind)OptionArgParser::ToOptionEnum( |
100 | s: option_arg, enum_values: GetDefinitions()[option_idx].enum_values, fail_value: 0, error); |
101 | if (!error.Success()) |
102 | error.SetErrorStringWithFormat( |
103 | "unrecognized value for log handler '%s'" , |
104 | option_arg.str().c_str()); |
105 | break; |
106 | case 'b': |
107 | error = |
108 | buffer_size.SetValueFromString(value: option_arg, op: eVarSetOperationAssign); |
109 | break; |
110 | case 'v': |
111 | log_options |= LLDB_LOG_OPTION_VERBOSE; |
112 | break; |
113 | case 's': |
114 | log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE; |
115 | break; |
116 | case 'T': |
117 | log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP; |
118 | break; |
119 | case 'p': |
120 | log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD; |
121 | break; |
122 | case 'n': |
123 | log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME; |
124 | break; |
125 | case 'S': |
126 | log_options |= LLDB_LOG_OPTION_BACKTRACE; |
127 | break; |
128 | case 'a': |
129 | log_options |= LLDB_LOG_OPTION_APPEND; |
130 | break; |
131 | case 'F': |
132 | log_options |= LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION; |
133 | break; |
134 | default: |
135 | llvm_unreachable("Unimplemented option" ); |
136 | } |
137 | |
138 | return error; |
139 | } |
140 | |
141 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
142 | log_file.Clear(); |
143 | buffer_size.Clear(); |
144 | handler = eLogHandlerStream; |
145 | log_options = 0; |
146 | } |
147 | |
148 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
149 | return llvm::ArrayRef(g_log_enable_options); |
150 | } |
151 | |
152 | FileSpec log_file; |
153 | OptionValueUInt64 buffer_size; |
154 | LogHandlerKind handler = eLogHandlerStream; |
155 | uint32_t log_options = 0; |
156 | }; |
157 | |
158 | void |
159 | HandleArgumentCompletion(CompletionRequest &request, |
160 | OptionElementVector &opt_element_vector) override { |
161 | CompleteEnableDisable(request); |
162 | } |
163 | |
164 | protected: |
165 | void DoExecute(Args &args, CommandReturnObject &result) override { |
166 | if (args.GetArgumentCount() < 2) { |
167 | result.AppendErrorWithFormat( |
168 | format: "%s takes a log channel and one or more log types.\n" , |
169 | m_cmd_name.c_str()); |
170 | return; |
171 | } |
172 | |
173 | if (m_options.handler == eLogHandlerCircular && |
174 | m_options.buffer_size.GetCurrentValue() == 0) { |
175 | result.AppendError( |
176 | in_string: "the circular buffer handler requires a non-zero buffer size.\n" ); |
177 | return; |
178 | } |
179 | |
180 | if ((m_options.handler != eLogHandlerCircular && |
181 | m_options.handler != eLogHandlerStream) && |
182 | m_options.buffer_size.GetCurrentValue() != 0) { |
183 | result.AppendError(in_string: "a buffer size can only be specified for the circular " |
184 | "and stream buffer handler.\n" ); |
185 | return; |
186 | } |
187 | |
188 | if (m_options.handler != eLogHandlerStream && m_options.log_file) { |
189 | result.AppendError( |
190 | in_string: "a file name can only be specified for the stream handler.\n" ); |
191 | return; |
192 | } |
193 | |
194 | // Store into a std::string since we're about to shift the channel off. |
195 | const std::string channel = std::string(args[0].ref()); |
196 | args.Shift(); // Shift off the channel |
197 | char log_file[PATH_MAX]; |
198 | if (m_options.log_file) |
199 | m_options.log_file.GetPath(path: log_file, max_path_length: sizeof(log_file)); |
200 | else |
201 | log_file[0] = '\0'; |
202 | |
203 | std::string error; |
204 | llvm::raw_string_ostream error_stream(error); |
205 | bool success = GetDebugger().EnableLog( |
206 | channel, categories: args.GetArgumentArrayRef(), log_file, log_options: m_options.log_options, |
207 | buffer_size: m_options.buffer_size.GetCurrentValue(), log_handler_kind: m_options.handler, |
208 | error_stream); |
209 | result.GetErrorStream() << error_stream.str(); |
210 | |
211 | if (success) |
212 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
213 | else |
214 | result.SetStatus(eReturnStatusFailed); |
215 | } |
216 | |
217 | CommandOptions m_options; |
218 | }; |
219 | |
220 | class CommandObjectLogDisable : public CommandObjectParsed { |
221 | public: |
222 | // Constructors and Destructors |
223 | CommandObjectLogDisable(CommandInterpreter &interpreter) |
224 | : CommandObjectParsed(interpreter, "log disable" , |
225 | "Disable one or more log channel categories." , |
226 | nullptr) { |
227 | CommandArgumentEntry arg1; |
228 | CommandArgumentEntry arg2; |
229 | CommandArgumentData channel_arg; |
230 | CommandArgumentData category_arg; |
231 | |
232 | // Define the first (and only) variant of this arg. |
233 | channel_arg.arg_type = eArgTypeLogChannel; |
234 | channel_arg.arg_repetition = eArgRepeatPlain; |
235 | |
236 | // There is only one variant this argument could be; put it into the |
237 | // argument entry. |
238 | arg1.push_back(x: channel_arg); |
239 | |
240 | category_arg.arg_type = eArgTypeLogCategory; |
241 | category_arg.arg_repetition = eArgRepeatPlus; |
242 | |
243 | arg2.push_back(x: category_arg); |
244 | |
245 | // Push the data for the first argument into the m_arguments vector. |
246 | m_arguments.push_back(x: arg1); |
247 | m_arguments.push_back(x: arg2); |
248 | } |
249 | |
250 | ~CommandObjectLogDisable() override = default; |
251 | |
252 | void |
253 | HandleArgumentCompletion(CompletionRequest &request, |
254 | OptionElementVector &opt_element_vector) override { |
255 | CompleteEnableDisable(request); |
256 | } |
257 | |
258 | protected: |
259 | void DoExecute(Args &args, CommandReturnObject &result) override { |
260 | if (args.empty()) { |
261 | result.AppendErrorWithFormat( |
262 | format: "%s takes a log channel and one or more log types.\n" , |
263 | m_cmd_name.c_str()); |
264 | return; |
265 | } |
266 | |
267 | const std::string channel = std::string(args[0].ref()); |
268 | args.Shift(); // Shift off the channel |
269 | if (channel == "all" ) { |
270 | Log::DisableAllLogChannels(); |
271 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
272 | } else { |
273 | std::string error; |
274 | llvm::raw_string_ostream error_stream(error); |
275 | if (Log::DisableLogChannel(channel, categories: args.GetArgumentArrayRef(), |
276 | error_stream)) |
277 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
278 | result.GetErrorStream() << error_stream.str(); |
279 | } |
280 | } |
281 | }; |
282 | |
283 | class CommandObjectLogList : public CommandObjectParsed { |
284 | public: |
285 | // Constructors and Destructors |
286 | CommandObjectLogList(CommandInterpreter &interpreter) |
287 | : CommandObjectParsed(interpreter, "log list" , |
288 | "List the log categories for one or more log " |
289 | "channels. If none specified, lists them all." , |
290 | nullptr) { |
291 | AddSimpleArgumentList(arg_type: eArgTypeLogChannel, repetition_type: eArgRepeatStar); |
292 | } |
293 | |
294 | ~CommandObjectLogList() override = default; |
295 | |
296 | void |
297 | HandleArgumentCompletion(CompletionRequest &request, |
298 | OptionElementVector &opt_element_vector) override { |
299 | for (llvm::StringRef channel : Log::ListChannels()) |
300 | request.TryCompleteCurrentArg(completion: channel); |
301 | } |
302 | |
303 | protected: |
304 | void DoExecute(Args &args, CommandReturnObject &result) override { |
305 | std::string output; |
306 | llvm::raw_string_ostream output_stream(output); |
307 | if (args.empty()) { |
308 | Log::ListAllLogChannels(stream&: output_stream); |
309 | result.SetStatus(eReturnStatusSuccessFinishResult); |
310 | } else { |
311 | bool success = true; |
312 | for (const auto &entry : args.entries()) |
313 | success = |
314 | success && Log::ListChannelCategories(channel: entry.ref(), stream&: output_stream); |
315 | if (success) |
316 | result.SetStatus(eReturnStatusSuccessFinishResult); |
317 | } |
318 | result.GetOutputStream() << output_stream.str(); |
319 | } |
320 | }; |
321 | class CommandObjectLogDump : public CommandObjectParsed { |
322 | public: |
323 | CommandObjectLogDump(CommandInterpreter &interpreter) |
324 | : CommandObjectParsed(interpreter, "log dump" , |
325 | "dump circular buffer logs" , nullptr) { |
326 | AddSimpleArgumentList(arg_type: eArgTypeLogChannel); |
327 | } |
328 | |
329 | ~CommandObjectLogDump() override = default; |
330 | |
331 | Options *GetOptions() override { return &m_options; } |
332 | |
333 | class CommandOptions : public Options { |
334 | public: |
335 | CommandOptions() = default; |
336 | |
337 | ~CommandOptions() override = default; |
338 | |
339 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
340 | ExecutionContext *execution_context) override { |
341 | Status error; |
342 | const int short_option = m_getopt_table[option_idx].val; |
343 | |
344 | switch (short_option) { |
345 | case 'f': |
346 | log_file.SetFile(path: option_arg, style: FileSpec::Style::native); |
347 | FileSystem::Instance().Resolve(file_spec&: log_file); |
348 | break; |
349 | default: |
350 | llvm_unreachable("Unimplemented option" ); |
351 | } |
352 | |
353 | return error; |
354 | } |
355 | |
356 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
357 | log_file.Clear(); |
358 | } |
359 | |
360 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
361 | return llvm::ArrayRef(g_log_dump_options); |
362 | } |
363 | |
364 | FileSpec log_file; |
365 | }; |
366 | |
367 | void |
368 | HandleArgumentCompletion(CompletionRequest &request, |
369 | OptionElementVector &opt_element_vector) override { |
370 | CompleteEnableDisable(request); |
371 | } |
372 | |
373 | protected: |
374 | void DoExecute(Args &args, CommandReturnObject &result) override { |
375 | if (args.empty()) { |
376 | result.AppendErrorWithFormat( |
377 | format: "%s takes a log channel and one or more log types.\n" , |
378 | m_cmd_name.c_str()); |
379 | return; |
380 | } |
381 | |
382 | std::unique_ptr<llvm::raw_ostream> stream_up; |
383 | if (m_options.log_file) { |
384 | const File::OpenOptions flags = File::eOpenOptionWriteOnly | |
385 | File::eOpenOptionCanCreate | |
386 | File::eOpenOptionTruncate; |
387 | llvm::Expected<FileUP> file = FileSystem::Instance().Open( |
388 | file_spec: m_options.log_file, options: flags, permissions: lldb::eFilePermissionsFileDefault, should_close_fd: false); |
389 | if (!file) { |
390 | result.AppendErrorWithFormat(format: "Unable to open log file '%s': %s" , |
391 | m_options.log_file.GetPath().c_str(), |
392 | llvm::toString(E: file.takeError()).c_str()); |
393 | return; |
394 | } |
395 | stream_up = std::make_unique<llvm::raw_fd_ostream>( |
396 | args: (*file)->GetDescriptor(), /*shouldClose=*/args: true); |
397 | } else { |
398 | stream_up = std::make_unique<llvm::raw_fd_ostream>( |
399 | args: GetDebugger().GetOutputFile().GetDescriptor(), /*shouldClose=*/args: false); |
400 | } |
401 | |
402 | const std::string channel = std::string(args[0].ref()); |
403 | std::string error; |
404 | llvm::raw_string_ostream error_stream(error); |
405 | if (Log::DumpLogChannel(channel, output_stream&: *stream_up, error_stream)) { |
406 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
407 | } else { |
408 | result.SetStatus(eReturnStatusFailed); |
409 | result.GetErrorStream() << error_stream.str(); |
410 | } |
411 | } |
412 | |
413 | CommandOptions m_options; |
414 | }; |
415 | |
416 | class CommandObjectLogTimerEnable : public CommandObjectParsed { |
417 | public: |
418 | // Constructors and Destructors |
419 | CommandObjectLogTimerEnable(CommandInterpreter &interpreter) |
420 | : CommandObjectParsed(interpreter, "log timers enable" , |
421 | "enable LLDB internal performance timers" , |
422 | "log timers enable <depth>" ) { |
423 | AddSimpleArgumentList(arg_type: eArgTypeCount, repetition_type: eArgRepeatOptional); |
424 | } |
425 | |
426 | ~CommandObjectLogTimerEnable() override = default; |
427 | |
428 | protected: |
429 | void DoExecute(Args &args, CommandReturnObject &result) override { |
430 | result.SetStatus(eReturnStatusFailed); |
431 | |
432 | if (args.GetArgumentCount() == 0) { |
433 | Timer::SetDisplayDepth(UINT32_MAX); |
434 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
435 | } else if (args.GetArgumentCount() == 1) { |
436 | uint32_t depth; |
437 | if (args[0].ref().consumeInteger(Radix: 0, Result&: depth)) { |
438 | result.AppendError( |
439 | in_string: "Could not convert enable depth to an unsigned integer." ); |
440 | } else { |
441 | Timer::SetDisplayDepth(depth); |
442 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
443 | } |
444 | } |
445 | |
446 | if (!result.Succeeded()) { |
447 | result.AppendError(in_string: "Missing subcommand" ); |
448 | result.AppendErrorWithFormat(format: "Usage: %s\n" , m_cmd_syntax.c_str()); |
449 | } |
450 | } |
451 | }; |
452 | |
453 | class CommandObjectLogTimerDisable : public CommandObjectParsed { |
454 | public: |
455 | // Constructors and Destructors |
456 | CommandObjectLogTimerDisable(CommandInterpreter &interpreter) |
457 | : CommandObjectParsed(interpreter, "log timers disable" , |
458 | "disable LLDB internal performance timers" , |
459 | nullptr) {} |
460 | |
461 | ~CommandObjectLogTimerDisable() override = default; |
462 | |
463 | protected: |
464 | void DoExecute(Args &args, CommandReturnObject &result) override { |
465 | Timer::DumpCategoryTimes(s&: result.GetOutputStream()); |
466 | Timer::SetDisplayDepth(0); |
467 | result.SetStatus(eReturnStatusSuccessFinishResult); |
468 | |
469 | if (!result.Succeeded()) { |
470 | result.AppendError(in_string: "Missing subcommand" ); |
471 | result.AppendErrorWithFormat(format: "Usage: %s\n" , m_cmd_syntax.c_str()); |
472 | } |
473 | } |
474 | }; |
475 | |
476 | class CommandObjectLogTimerDump : public CommandObjectParsed { |
477 | public: |
478 | // Constructors and Destructors |
479 | CommandObjectLogTimerDump(CommandInterpreter &interpreter) |
480 | : CommandObjectParsed(interpreter, "log timers dump" , |
481 | "dump LLDB internal performance timers" , nullptr) {} |
482 | |
483 | ~CommandObjectLogTimerDump() override = default; |
484 | |
485 | protected: |
486 | void DoExecute(Args &args, CommandReturnObject &result) override { |
487 | Timer::DumpCategoryTimes(s&: result.GetOutputStream()); |
488 | result.SetStatus(eReturnStatusSuccessFinishResult); |
489 | |
490 | if (!result.Succeeded()) { |
491 | result.AppendError(in_string: "Missing subcommand" ); |
492 | result.AppendErrorWithFormat(format: "Usage: %s\n" , m_cmd_syntax.c_str()); |
493 | } |
494 | } |
495 | }; |
496 | |
497 | class CommandObjectLogTimerReset : public CommandObjectParsed { |
498 | public: |
499 | // Constructors and Destructors |
500 | CommandObjectLogTimerReset(CommandInterpreter &interpreter) |
501 | : CommandObjectParsed(interpreter, "log timers reset" , |
502 | "reset LLDB internal performance timers" , nullptr) { |
503 | } |
504 | |
505 | ~CommandObjectLogTimerReset() override = default; |
506 | |
507 | protected: |
508 | void DoExecute(Args &args, CommandReturnObject &result) override { |
509 | Timer::ResetCategoryTimes(); |
510 | result.SetStatus(eReturnStatusSuccessFinishResult); |
511 | |
512 | if (!result.Succeeded()) { |
513 | result.AppendError(in_string: "Missing subcommand" ); |
514 | result.AppendErrorWithFormat(format: "Usage: %s\n" , m_cmd_syntax.c_str()); |
515 | } |
516 | } |
517 | }; |
518 | |
519 | class CommandObjectLogTimerIncrement : public CommandObjectParsed { |
520 | public: |
521 | // Constructors and Destructors |
522 | CommandObjectLogTimerIncrement(CommandInterpreter &interpreter) |
523 | : CommandObjectParsed(interpreter, "log timers increment" , |
524 | "increment LLDB internal performance timers" , |
525 | "log timers increment <bool>" ) { |
526 | AddSimpleArgumentList(arg_type: eArgTypeBoolean); |
527 | } |
528 | |
529 | ~CommandObjectLogTimerIncrement() override = default; |
530 | |
531 | void |
532 | HandleArgumentCompletion(CompletionRequest &request, |
533 | OptionElementVector &opt_element_vector) override { |
534 | request.TryCompleteCurrentArg(completion: "true" ); |
535 | request.TryCompleteCurrentArg(completion: "false" ); |
536 | } |
537 | |
538 | protected: |
539 | void DoExecute(Args &args, CommandReturnObject &result) override { |
540 | result.SetStatus(eReturnStatusFailed); |
541 | |
542 | if (args.GetArgumentCount() == 1) { |
543 | bool success; |
544 | bool increment = |
545 | OptionArgParser::ToBoolean(s: args[0].ref(), fail_value: false, success_ptr: &success); |
546 | |
547 | if (success) { |
548 | Timer::SetQuiet(!increment); |
549 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
550 | } else |
551 | result.AppendError(in_string: "Could not convert increment value to boolean." ); |
552 | } |
553 | |
554 | if (!result.Succeeded()) { |
555 | result.AppendError(in_string: "Missing subcommand" ); |
556 | result.AppendErrorWithFormat(format: "Usage: %s\n" , m_cmd_syntax.c_str()); |
557 | } |
558 | } |
559 | }; |
560 | |
561 | class CommandObjectLogTimer : public CommandObjectMultiword { |
562 | public: |
563 | CommandObjectLogTimer(CommandInterpreter &interpreter) |
564 | : CommandObjectMultiword(interpreter, "log timers" , |
565 | "Enable, disable, dump, and reset LLDB internal " |
566 | "performance timers." , |
567 | "log timers < enable <depth> | disable | dump | " |
568 | "increment <bool> | reset >" ) { |
569 | LoadSubCommand(cmd_name: "enable" , command_obj: CommandObjectSP( |
570 | new CommandObjectLogTimerEnable(interpreter))); |
571 | LoadSubCommand(cmd_name: "disable" , command_obj: CommandObjectSP(new CommandObjectLogTimerDisable( |
572 | interpreter))); |
573 | LoadSubCommand(cmd_name: "dump" , |
574 | command_obj: CommandObjectSP(new CommandObjectLogTimerDump(interpreter))); |
575 | LoadSubCommand( |
576 | cmd_name: "reset" , command_obj: CommandObjectSP(new CommandObjectLogTimerReset(interpreter))); |
577 | LoadSubCommand( |
578 | cmd_name: "increment" , |
579 | command_obj: CommandObjectSP(new CommandObjectLogTimerIncrement(interpreter))); |
580 | } |
581 | |
582 | ~CommandObjectLogTimer() override = default; |
583 | }; |
584 | |
585 | CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter) |
586 | : CommandObjectMultiword(interpreter, "log" , |
587 | "Commands controlling LLDB internal logging." , |
588 | "log <subcommand> [<command-options>]" ) { |
589 | LoadSubCommand(cmd_name: "enable" , |
590 | command_obj: CommandObjectSP(new CommandObjectLogEnable(interpreter))); |
591 | LoadSubCommand(cmd_name: "disable" , |
592 | command_obj: CommandObjectSP(new CommandObjectLogDisable(interpreter))); |
593 | LoadSubCommand(cmd_name: "list" , |
594 | command_obj: CommandObjectSP(new CommandObjectLogList(interpreter))); |
595 | LoadSubCommand(cmd_name: "dump" , |
596 | command_obj: CommandObjectSP(new CommandObjectLogDump(interpreter))); |
597 | LoadSubCommand(cmd_name: "timers" , |
598 | command_obj: CommandObjectSP(new CommandObjectLogTimer(interpreter))); |
599 | } |
600 | |
601 | CommandObjectLog::~CommandObjectLog() = default; |
602 | |