Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Commands/CommandObjectLog.cpp
39587 views
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(channel);
39
} else if (arg_index >= 1) { // We got: log enable/disable channel x[tab]
40
llvm::StringRef channel = request.GetParsedLine().GetArgumentAtIndex(0);
41
Log::ForEachChannelCategory(
42
channel, [&request](llvm::StringRef name, llvm::StringRef desc) {
43
request.TryCompleteCurrentArg(name, 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(channel_arg);
67
68
category_arg.arg_type = eArgTypeLogCategory;
69
category_arg.arg_repetition = eArgRepeatPlus;
70
71
arg2.push_back(category_arg);
72
73
// Push the data for the first argument into the m_arguments vector.
74
m_arguments.push_back(arg1);
75
m_arguments.push_back(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(option_arg, FileSpec::Style::native);
96
FileSystem::Instance().Resolve(log_file);
97
break;
98
case 'h':
99
handler = (LogHandlerKind)OptionArgParser::ToOptionEnum(
100
option_arg, GetDefinitions()[option_idx].enum_values, 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(option_arg, 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
"%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
"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("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
"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(log_file, 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, args.GetArgumentArrayRef(), log_file, m_options.log_options,
207
m_options.buffer_size.GetCurrentValue(), 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(channel_arg);
239
240
category_arg.arg_type = eArgTypeLogCategory;
241
category_arg.arg_repetition = eArgRepeatPlus;
242
243
arg2.push_back(category_arg);
244
245
// Push the data for the first argument into the m_arguments vector.
246
m_arguments.push_back(arg1);
247
m_arguments.push_back(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
"%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, 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(eArgTypeLogChannel, 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(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(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(entry.ref(), 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(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(option_arg, FileSpec::Style::native);
347
FileSystem::Instance().Resolve(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
"%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
m_options.log_file, flags, lldb::eFilePermissionsFileDefault, false);
389
if (!file) {
390
result.AppendErrorWithFormat("Unable to open log file '%s': %s",
391
m_options.log_file.GetPath().c_str(),
392
llvm::toString(file.takeError()).c_str());
393
return;
394
}
395
stream_up = std::make_unique<llvm::raw_fd_ostream>(
396
(*file)->GetDescriptor(), /*shouldClose=*/true);
397
} else {
398
stream_up = std::make_unique<llvm::raw_fd_ostream>(
399
GetDebugger().GetOutputFile().GetDescriptor(), /*shouldClose=*/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, *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(eArgTypeCount, 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(0, depth)) {
438
result.AppendError(
439
"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("Missing subcommand");
448
result.AppendErrorWithFormat("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(result.GetOutputStream());
466
Timer::SetDisplayDepth(0);
467
result.SetStatus(eReturnStatusSuccessFinishResult);
468
469
if (!result.Succeeded()) {
470
result.AppendError("Missing subcommand");
471
result.AppendErrorWithFormat("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(result.GetOutputStream());
488
result.SetStatus(eReturnStatusSuccessFinishResult);
489
490
if (!result.Succeeded()) {
491
result.AppendError("Missing subcommand");
492
result.AppendErrorWithFormat("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("Missing subcommand");
514
result.AppendErrorWithFormat("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(eArgTypeBoolean);
527
}
528
529
~CommandObjectLogTimerIncrement() override = default;
530
531
void
532
HandleArgumentCompletion(CompletionRequest &request,
533
OptionElementVector &opt_element_vector) override {
534
request.TryCompleteCurrentArg("true");
535
request.TryCompleteCurrentArg("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(args[0].ref(), false, &success);
546
547
if (success) {
548
Timer::SetQuiet(!increment);
549
result.SetStatus(eReturnStatusSuccessFinishNoResult);
550
} else
551
result.AppendError("Could not convert increment value to boolean.");
552
}
553
554
if (!result.Succeeded()) {
555
result.AppendError("Missing subcommand");
556
result.AppendErrorWithFormat("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("enable", CommandObjectSP(
570
new CommandObjectLogTimerEnable(interpreter)));
571
LoadSubCommand("disable", CommandObjectSP(new CommandObjectLogTimerDisable(
572
interpreter)));
573
LoadSubCommand("dump",
574
CommandObjectSP(new CommandObjectLogTimerDump(interpreter)));
575
LoadSubCommand(
576
"reset", CommandObjectSP(new CommandObjectLogTimerReset(interpreter)));
577
LoadSubCommand(
578
"increment",
579
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("enable",
590
CommandObjectSP(new CommandObjectLogEnable(interpreter)));
591
LoadSubCommand("disable",
592
CommandObjectSP(new CommandObjectLogDisable(interpreter)));
593
LoadSubCommand("list",
594
CommandObjectSP(new CommandObjectLogList(interpreter)));
595
LoadSubCommand("dump",
596
CommandObjectSP(new CommandObjectLogDump(interpreter)));
597
LoadSubCommand("timers",
598
CommandObjectSP(new CommandObjectLogTimer(interpreter)));
599
}
600
601
CommandObjectLog::~CommandObjectLog() = default;
602
603