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/CommandReturnObject.h"
13 #include "lldb/Interpreter/OptionArgParser.h"
14 #include "lldb/Interpreter/Options.h"
15 #include "lldb/Utility/Args.h"
16 #include "lldb/Utility/FileSpec.h"
17 #include "lldb/Utility/Log.h"
18 #include "lldb/Utility/Stream.h"
19 #include "lldb/Utility/Timer.h"
20
21 using namespace lldb;
22 using namespace lldb_private;
23
24 #define LLDB_OPTIONS_log
25 #include "CommandOptions.inc"
26
27 /// Common completion logic for log enable/disable.
CompleteEnableDisable(CompletionRequest & request)28 static void CompleteEnableDisable(CompletionRequest &request) {
29 size_t arg_index = request.GetCursorIndex();
30 if (arg_index == 0) { // We got: log enable/disable x[tab]
31 for (llvm::StringRef channel : Log::ListChannels())
32 request.TryCompleteCurrentArg(channel);
33 } else if (arg_index >= 1) { // We got: log enable/disable channel x[tab]
34 llvm::StringRef channel = request.GetParsedLine().GetArgumentAtIndex(0);
35 Log::ForEachChannelCategory(
36 channel, [&request](llvm::StringRef name, llvm::StringRef desc) {
37 request.TryCompleteCurrentArg(name, desc);
38 });
39 }
40 }
41
42 class CommandObjectLogEnable : public CommandObjectParsed {
43 public:
44 // Constructors and Destructors
CommandObjectLogEnable(CommandInterpreter & interpreter)45 CommandObjectLogEnable(CommandInterpreter &interpreter)
46 : CommandObjectParsed(interpreter, "log enable",
47 "Enable logging for a single log channel.",
48 nullptr),
49 m_options() {
50 CommandArgumentEntry arg1;
51 CommandArgumentEntry arg2;
52 CommandArgumentData channel_arg;
53 CommandArgumentData category_arg;
54
55 // Define the first (and only) variant of this arg.
56 channel_arg.arg_type = eArgTypeLogChannel;
57 channel_arg.arg_repetition = eArgRepeatPlain;
58
59 // There is only one variant this argument could be; put it into the
60 // argument entry.
61 arg1.push_back(channel_arg);
62
63 category_arg.arg_type = eArgTypeLogCategory;
64 category_arg.arg_repetition = eArgRepeatPlus;
65
66 arg2.push_back(category_arg);
67
68 // Push the data for the first argument into the m_arguments vector.
69 m_arguments.push_back(arg1);
70 m_arguments.push_back(arg2);
71 }
72
73 ~CommandObjectLogEnable() override = default;
74
GetOptions()75 Options *GetOptions() override { return &m_options; }
76
77 class CommandOptions : public Options {
78 public:
CommandOptions()79 CommandOptions() : Options(), log_file() {}
80
81 ~CommandOptions() override = default;
82
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)83 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
84 ExecutionContext *execution_context) override {
85 Status error;
86 const int short_option = m_getopt_table[option_idx].val;
87
88 switch (short_option) {
89 case 'f':
90 log_file.SetFile(option_arg, FileSpec::Style::native);
91 FileSystem::Instance().Resolve(log_file);
92 break;
93 case 't':
94 log_options |= LLDB_LOG_OPTION_THREADSAFE;
95 break;
96 case 'v':
97 log_options |= LLDB_LOG_OPTION_VERBOSE;
98 break;
99 case 's':
100 log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE;
101 break;
102 case 'T':
103 log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP;
104 break;
105 case 'p':
106 log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD;
107 break;
108 case 'n':
109 log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME;
110 break;
111 case 'S':
112 log_options |= LLDB_LOG_OPTION_BACKTRACE;
113 break;
114 case 'a':
115 log_options |= LLDB_LOG_OPTION_APPEND;
116 break;
117 case 'F':
118 log_options |= LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION;
119 break;
120 default:
121 llvm_unreachable("Unimplemented option");
122 }
123
124 return error;
125 }
126
OptionParsingStarting(ExecutionContext * execution_context)127 void OptionParsingStarting(ExecutionContext *execution_context) override {
128 log_file.Clear();
129 log_options = 0;
130 }
131
GetDefinitions()132 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
133 return llvm::makeArrayRef(g_log_options);
134 }
135
136 // Instance variables to hold the values for command options.
137
138 FileSpec log_file;
139 uint32_t log_options = 0;
140 };
141
142 void
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)143 HandleArgumentCompletion(CompletionRequest &request,
144 OptionElementVector &opt_element_vector) override {
145 CompleteEnableDisable(request);
146 }
147
148 protected:
DoExecute(Args & args,CommandReturnObject & result)149 bool DoExecute(Args &args, CommandReturnObject &result) override {
150 if (args.GetArgumentCount() < 2) {
151 result.AppendErrorWithFormat(
152 "%s takes a log channel and one or more log types.\n",
153 m_cmd_name.c_str());
154 return false;
155 }
156
157 // Store into a std::string since we're about to shift the channel off.
158 const std::string channel = std::string(args[0].ref());
159 args.Shift(); // Shift off the channel
160 char log_file[PATH_MAX];
161 if (m_options.log_file)
162 m_options.log_file.GetPath(log_file, sizeof(log_file));
163 else
164 log_file[0] = '\0';
165
166 std::string error;
167 llvm::raw_string_ostream error_stream(error);
168 bool success =
169 GetDebugger().EnableLog(channel, args.GetArgumentArrayRef(), log_file,
170 m_options.log_options, error_stream);
171 result.GetErrorStream() << error_stream.str();
172
173 if (success)
174 result.SetStatus(eReturnStatusSuccessFinishNoResult);
175 else
176 result.SetStatus(eReturnStatusFailed);
177 return result.Succeeded();
178 }
179
180 CommandOptions m_options;
181 };
182
183 class CommandObjectLogDisable : public CommandObjectParsed {
184 public:
185 // Constructors and Destructors
CommandObjectLogDisable(CommandInterpreter & interpreter)186 CommandObjectLogDisable(CommandInterpreter &interpreter)
187 : CommandObjectParsed(interpreter, "log disable",
188 "Disable one or more log channel categories.",
189 nullptr) {
190 CommandArgumentEntry arg1;
191 CommandArgumentEntry arg2;
192 CommandArgumentData channel_arg;
193 CommandArgumentData category_arg;
194
195 // Define the first (and only) variant of this arg.
196 channel_arg.arg_type = eArgTypeLogChannel;
197 channel_arg.arg_repetition = eArgRepeatPlain;
198
199 // There is only one variant this argument could be; put it into the
200 // argument entry.
201 arg1.push_back(channel_arg);
202
203 category_arg.arg_type = eArgTypeLogCategory;
204 category_arg.arg_repetition = eArgRepeatPlus;
205
206 arg2.push_back(category_arg);
207
208 // Push the data for the first argument into the m_arguments vector.
209 m_arguments.push_back(arg1);
210 m_arguments.push_back(arg2);
211 }
212
213 ~CommandObjectLogDisable() override = default;
214
215 void
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)216 HandleArgumentCompletion(CompletionRequest &request,
217 OptionElementVector &opt_element_vector) override {
218 CompleteEnableDisable(request);
219 }
220
221 protected:
DoExecute(Args & args,CommandReturnObject & result)222 bool DoExecute(Args &args, CommandReturnObject &result) override {
223 if (args.empty()) {
224 result.AppendErrorWithFormat(
225 "%s takes a log channel and one or more log types.\n",
226 m_cmd_name.c_str());
227 return false;
228 }
229
230 const std::string channel = std::string(args[0].ref());
231 args.Shift(); // Shift off the channel
232 if (channel == "all") {
233 Log::DisableAllLogChannels();
234 result.SetStatus(eReturnStatusSuccessFinishNoResult);
235 } else {
236 std::string error;
237 llvm::raw_string_ostream error_stream(error);
238 if (Log::DisableLogChannel(channel, args.GetArgumentArrayRef(),
239 error_stream))
240 result.SetStatus(eReturnStatusSuccessFinishNoResult);
241 result.GetErrorStream() << error_stream.str();
242 }
243 return result.Succeeded();
244 }
245 };
246
247 class CommandObjectLogList : public CommandObjectParsed {
248 public:
249 // Constructors and Destructors
CommandObjectLogList(CommandInterpreter & interpreter)250 CommandObjectLogList(CommandInterpreter &interpreter)
251 : CommandObjectParsed(interpreter, "log list",
252 "List the log categories for one or more log "
253 "channels. If none specified, lists them all.",
254 nullptr) {
255 CommandArgumentEntry arg;
256 CommandArgumentData channel_arg;
257
258 // Define the first (and only) variant of this arg.
259 channel_arg.arg_type = eArgTypeLogChannel;
260 channel_arg.arg_repetition = eArgRepeatStar;
261
262 // There is only one variant this argument could be; put it into the
263 // argument entry.
264 arg.push_back(channel_arg);
265
266 // Push the data for the first argument into the m_arguments vector.
267 m_arguments.push_back(arg);
268 }
269
270 ~CommandObjectLogList() override = default;
271
272 void
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)273 HandleArgumentCompletion(CompletionRequest &request,
274 OptionElementVector &opt_element_vector) override {
275 for (llvm::StringRef channel : Log::ListChannels())
276 request.TryCompleteCurrentArg(channel);
277 }
278
279 protected:
DoExecute(Args & args,CommandReturnObject & result)280 bool DoExecute(Args &args, CommandReturnObject &result) override {
281 std::string output;
282 llvm::raw_string_ostream output_stream(output);
283 if (args.empty()) {
284 Log::ListAllLogChannels(output_stream);
285 result.SetStatus(eReturnStatusSuccessFinishResult);
286 } else {
287 bool success = true;
288 for (const auto &entry : args.entries())
289 success =
290 success && Log::ListChannelCategories(entry.ref(), output_stream);
291 if (success)
292 result.SetStatus(eReturnStatusSuccessFinishResult);
293 }
294 result.GetOutputStream() << output_stream.str();
295 return result.Succeeded();
296 }
297 };
298
299 class CommandObjectLogTimerEnable : public CommandObjectParsed {
300 public:
301 // Constructors and Destructors
CommandObjectLogTimerEnable(CommandInterpreter & interpreter)302 CommandObjectLogTimerEnable(CommandInterpreter &interpreter)
303 : CommandObjectParsed(interpreter, "log timers enable",
304 "enable LLDB internal performance timers",
305 "log timers enable <depth>") {
306 CommandArgumentEntry arg;
307 CommandArgumentData depth_arg;
308
309 // Define the first (and only) variant of this arg.
310 depth_arg.arg_type = eArgTypeCount;
311 depth_arg.arg_repetition = eArgRepeatOptional;
312
313 // There is only one variant this argument could be; put it into the
314 // argument entry.
315 arg.push_back(depth_arg);
316
317 // Push the data for the first argument into the m_arguments vector.
318 m_arguments.push_back(arg);
319 }
320
321 ~CommandObjectLogTimerEnable() override = default;
322
323 protected:
DoExecute(Args & args,CommandReturnObject & result)324 bool DoExecute(Args &args, CommandReturnObject &result) override {
325 result.SetStatus(eReturnStatusFailed);
326
327 if (args.GetArgumentCount() == 0) {
328 Timer::SetDisplayDepth(UINT32_MAX);
329 result.SetStatus(eReturnStatusSuccessFinishNoResult);
330 } else if (args.GetArgumentCount() == 1) {
331 uint32_t depth;
332 if (args[0].ref().consumeInteger(0, depth)) {
333 result.AppendError(
334 "Could not convert enable depth to an unsigned integer.");
335 } else {
336 Timer::SetDisplayDepth(depth);
337 result.SetStatus(eReturnStatusSuccessFinishNoResult);
338 }
339 }
340
341 if (!result.Succeeded()) {
342 result.AppendError("Missing subcommand");
343 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
344 }
345 return result.Succeeded();
346 }
347 };
348
349 class CommandObjectLogTimerDisable : public CommandObjectParsed {
350 public:
351 // Constructors and Destructors
CommandObjectLogTimerDisable(CommandInterpreter & interpreter)352 CommandObjectLogTimerDisable(CommandInterpreter &interpreter)
353 : CommandObjectParsed(interpreter, "log timers disable",
354 "disable LLDB internal performance timers",
355 nullptr) {}
356
357 ~CommandObjectLogTimerDisable() override = default;
358
359 protected:
DoExecute(Args & args,CommandReturnObject & result)360 bool DoExecute(Args &args, CommandReturnObject &result) override {
361 Timer::DumpCategoryTimes(&result.GetOutputStream());
362 Timer::SetDisplayDepth(0);
363 result.SetStatus(eReturnStatusSuccessFinishResult);
364
365 if (!result.Succeeded()) {
366 result.AppendError("Missing subcommand");
367 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
368 }
369 return result.Succeeded();
370 }
371 };
372
373 class CommandObjectLogTimerDump : public CommandObjectParsed {
374 public:
375 // Constructors and Destructors
CommandObjectLogTimerDump(CommandInterpreter & interpreter)376 CommandObjectLogTimerDump(CommandInterpreter &interpreter)
377 : CommandObjectParsed(interpreter, "log timers dump",
378 "dump LLDB internal performance timers", nullptr) {}
379
380 ~CommandObjectLogTimerDump() override = default;
381
382 protected:
DoExecute(Args & args,CommandReturnObject & result)383 bool DoExecute(Args &args, CommandReturnObject &result) override {
384 Timer::DumpCategoryTimes(&result.GetOutputStream());
385 result.SetStatus(eReturnStatusSuccessFinishResult);
386
387 if (!result.Succeeded()) {
388 result.AppendError("Missing subcommand");
389 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
390 }
391 return result.Succeeded();
392 }
393 };
394
395 class CommandObjectLogTimerReset : public CommandObjectParsed {
396 public:
397 // Constructors and Destructors
CommandObjectLogTimerReset(CommandInterpreter & interpreter)398 CommandObjectLogTimerReset(CommandInterpreter &interpreter)
399 : CommandObjectParsed(interpreter, "log timers reset",
400 "reset LLDB internal performance timers", nullptr) {
401 }
402
403 ~CommandObjectLogTimerReset() override = default;
404
405 protected:
DoExecute(Args & args,CommandReturnObject & result)406 bool DoExecute(Args &args, CommandReturnObject &result) override {
407 Timer::ResetCategoryTimes();
408 result.SetStatus(eReturnStatusSuccessFinishResult);
409
410 if (!result.Succeeded()) {
411 result.AppendError("Missing subcommand");
412 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
413 }
414 return result.Succeeded();
415 }
416 };
417
418 class CommandObjectLogTimerIncrement : public CommandObjectParsed {
419 public:
420 // Constructors and Destructors
CommandObjectLogTimerIncrement(CommandInterpreter & interpreter)421 CommandObjectLogTimerIncrement(CommandInterpreter &interpreter)
422 : CommandObjectParsed(interpreter, "log timers increment",
423 "increment LLDB internal performance timers",
424 "log timers increment <bool>") {
425 CommandArgumentEntry arg;
426 CommandArgumentData bool_arg;
427
428 // Define the first (and only) variant of this arg.
429 bool_arg.arg_type = eArgTypeBoolean;
430 bool_arg.arg_repetition = eArgRepeatPlain;
431
432 // There is only one variant this argument could be; put it into the
433 // argument entry.
434 arg.push_back(bool_arg);
435
436 // Push the data for the first argument into the m_arguments vector.
437 m_arguments.push_back(arg);
438 }
439
440 ~CommandObjectLogTimerIncrement() override = default;
441
442 void
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)443 HandleArgumentCompletion(CompletionRequest &request,
444 OptionElementVector &opt_element_vector) override {
445 request.TryCompleteCurrentArg("true");
446 request.TryCompleteCurrentArg("false");
447 }
448
449 protected:
DoExecute(Args & args,CommandReturnObject & result)450 bool DoExecute(Args &args, CommandReturnObject &result) override {
451 result.SetStatus(eReturnStatusFailed);
452
453 if (args.GetArgumentCount() == 1) {
454 bool success;
455 bool increment =
456 OptionArgParser::ToBoolean(args[0].ref(), false, &success);
457
458 if (success) {
459 Timer::SetQuiet(!increment);
460 result.SetStatus(eReturnStatusSuccessFinishNoResult);
461 } else
462 result.AppendError("Could not convert increment value to boolean.");
463 }
464
465 if (!result.Succeeded()) {
466 result.AppendError("Missing subcommand");
467 result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
468 }
469 return result.Succeeded();
470 }
471 };
472
473 class CommandObjectLogTimer : public CommandObjectMultiword {
474 public:
CommandObjectLogTimer(CommandInterpreter & interpreter)475 CommandObjectLogTimer(CommandInterpreter &interpreter)
476 : CommandObjectMultiword(interpreter, "log timers",
477 "Enable, disable, dump, and reset LLDB internal "
478 "performance timers.",
479 "log timers < enable <depth> | disable | dump | "
480 "increment <bool> | reset >") {
481 LoadSubCommand("enable", CommandObjectSP(
482 new CommandObjectLogTimerEnable(interpreter)));
483 LoadSubCommand("disable", CommandObjectSP(new CommandObjectLogTimerDisable(
484 interpreter)));
485 LoadSubCommand("dump",
486 CommandObjectSP(new CommandObjectLogTimerDump(interpreter)));
487 LoadSubCommand(
488 "reset", CommandObjectSP(new CommandObjectLogTimerReset(interpreter)));
489 LoadSubCommand(
490 "increment",
491 CommandObjectSP(new CommandObjectLogTimerIncrement(interpreter)));
492 }
493
494 ~CommandObjectLogTimer() override = default;
495 };
496
CommandObjectLog(CommandInterpreter & interpreter)497 CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter)
498 : CommandObjectMultiword(interpreter, "log",
499 "Commands controlling LLDB internal logging.",
500 "log <subcommand> [<command-options>]") {
501 LoadSubCommand("enable",
502 CommandObjectSP(new CommandObjectLogEnable(interpreter)));
503 LoadSubCommand("disable",
504 CommandObjectSP(new CommandObjectLogDisable(interpreter)));
505 LoadSubCommand("list",
506 CommandObjectSP(new CommandObjectLogList(interpreter)));
507 LoadSubCommand("timers",
508 CommandObjectSP(new CommandObjectLogTimer(interpreter)));
509 }
510
511 CommandObjectLog::~CommandObjectLog() = default;
512