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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of lldb/source/Commands/CommandObjectLog.cpp