xref: /llvm-project/lldb/source/Commands/CommandObjectLog.cpp (revision 90505a0a2afb6ea9e8a8c1b326f25ca2282e4036)
1 //===-- CommandObjectLog.cpp ------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // C Includes
11 // C++ Includes
12 // Other libraries and framework includes
13 // Project includes
14 #include "CommandObjectLog.h"
15 #include "lldb/Core/Debugger.h"
16 #include "lldb/Core/Debugger.h"
17 #include "lldb/Core/Log.h"
18 #include "lldb/Core/Module.h"
19 #include "lldb/Core/StreamFile.h"
20 #include "lldb/Core/Timer.h"
21 #include "lldb/Host/FileSpec.h"
22 #include "lldb/Host/StringConvert.h"
23 #include "lldb/Interpreter/Args.h"
24 #include "lldb/Interpreter/CommandInterpreter.h"
25 #include "lldb/Interpreter/CommandReturnObject.h"
26 #include "lldb/Interpreter/Options.h"
27 #include "lldb/Symbol/LineTable.h"
28 #include "lldb/Symbol/ObjectFile.h"
29 #include "lldb/Symbol/SymbolFile.h"
30 #include "lldb/Symbol/SymbolVendor.h"
31 #include "lldb/Target/Process.h"
32 #include "lldb/Target/Target.h"
33 #include "lldb/Utility/RegularExpression.h"
34 #include "lldb/Utility/Stream.h"
35 
36 using namespace lldb;
37 using namespace lldb_private;
38 
39 static OptionDefinition g_log_options[] = {
40     // clang-format off
41   { LLDB_OPT_SET_1, false, "file",       'f', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFilename, "Set the destination file to log to." },
42   { LLDB_OPT_SET_1, false, "threadsafe", 't', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,     "Enable thread safe logging to avoid interweaved log lines." },
43   { LLDB_OPT_SET_1, false, "verbose",    'v', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,     "Enable verbose logging." },
44   { LLDB_OPT_SET_1, false, "debug",      'g', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,     "Enable debug logging." },
45   { LLDB_OPT_SET_1, false, "sequence",   's', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,     "Prepend all log lines with an increasing integer sequence id." },
46   { LLDB_OPT_SET_1, false, "timestamp",  'T', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,     "Prepend all log lines with a timestamp." },
47   { LLDB_OPT_SET_1, false, "pid-tid",    'p', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,     "Prepend all log lines with the process and thread ID that generates the log line." },
48   { LLDB_OPT_SET_1, false, "thread-name",'n', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,     "Prepend all log lines with the thread name for the thread that generates the log line." },
49   { LLDB_OPT_SET_1, false, "stack",      'S', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,     "Append a stack backtrace to each log line." },
50   { LLDB_OPT_SET_1, false, "append",     'a', OptionParser::eNoArgument,       nullptr, nullptr, 0, eArgTypeNone,     "Append to the log file instead of overwriting." },
51   { LLDB_OPT_SET_1, false, "file-function",'F',OptionParser::eNoArgument,      nullptr, nullptr, 0, eArgTypeNone,     "Prepend the names of files and function that generate the logs." },
52     // clang-format on
53 };
54 
55 class CommandObjectLogEnable : public CommandObjectParsed {
56 public:
57   //------------------------------------------------------------------
58   // Constructors and Destructors
59   //------------------------------------------------------------------
60   CommandObjectLogEnable(CommandInterpreter &interpreter)
61       : CommandObjectParsed(interpreter, "log enable",
62                             "Enable logging for a single log channel.",
63                             nullptr),
64         m_options() {
65     CommandArgumentEntry arg1;
66     CommandArgumentEntry arg2;
67     CommandArgumentData channel_arg;
68     CommandArgumentData category_arg;
69 
70     // Define the first (and only) variant of this arg.
71     channel_arg.arg_type = eArgTypeLogChannel;
72     channel_arg.arg_repetition = eArgRepeatPlain;
73 
74     // There is only one variant this argument could be; put it into the
75     // argument entry.
76     arg1.push_back(channel_arg);
77 
78     category_arg.arg_type = eArgTypeLogCategory;
79     category_arg.arg_repetition = eArgRepeatPlus;
80 
81     arg2.push_back(category_arg);
82 
83     // Push the data for the first argument into the m_arguments vector.
84     m_arguments.push_back(arg1);
85     m_arguments.push_back(arg2);
86   }
87 
88   ~CommandObjectLogEnable() override = default;
89 
90   Options *GetOptions() override { return &m_options; }
91 
92   class CommandOptions : public Options {
93   public:
94     CommandOptions() : Options(), log_file(), log_options(0) {}
95 
96     ~CommandOptions() override = default;
97 
98     Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
99                          ExecutionContext *execution_context) override {
100       Error error;
101       const int short_option = m_getopt_table[option_idx].val;
102 
103       switch (short_option) {
104       case 'f':
105         log_file.SetFile(option_arg, true);
106         break;
107       case 't':
108         log_options |= LLDB_LOG_OPTION_THREADSAFE;
109         break;
110       case 'v':
111         log_options |= LLDB_LOG_OPTION_VERBOSE;
112         break;
113       case 'g':
114         log_options |= LLDB_LOG_OPTION_DEBUG;
115         break;
116       case 's':
117         log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE;
118         break;
119       case 'T':
120         log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP;
121         break;
122       case 'p':
123         log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD;
124         break;
125       case 'n':
126         log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME;
127         break;
128       case 'S':
129         log_options |= LLDB_LOG_OPTION_BACKTRACE;
130         break;
131       case 'a':
132         log_options |= LLDB_LOG_OPTION_APPEND;
133         break;
134       case 'F':
135         log_options |= LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION;
136         break;
137       default:
138         error.SetErrorStringWithFormat("unrecognized option '%c'",
139                                        short_option);
140         break;
141       }
142 
143       return error;
144     }
145 
146     void OptionParsingStarting(ExecutionContext *execution_context) override {
147       log_file.Clear();
148       log_options = 0;
149     }
150 
151     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
152       return llvm::makeArrayRef(g_log_options);
153     }
154 
155     // Instance variables to hold the values for command options.
156 
157     FileSpec log_file;
158     uint32_t log_options;
159   };
160 
161 protected:
162   bool DoExecute(Args &args, CommandReturnObject &result) override {
163     if (args.GetArgumentCount() < 2) {
164       result.AppendErrorWithFormat(
165           "%s takes a log channel and one or more log types.\n",
166           m_cmd_name.c_str());
167       return false;
168     }
169 
170     // Store into a std::string since we're about to shift the channel off.
171     const std::string channel = args[0].ref;
172     args.Shift(); // Shift off the channel
173     char log_file[PATH_MAX];
174     if (m_options.log_file)
175       m_options.log_file.GetPath(log_file, sizeof(log_file));
176     else
177       log_file[0] = '\0';
178     bool success = m_interpreter.GetDebugger().EnableLog(
179         channel.c_str(), args.GetConstArgumentVector(), log_file,
180         m_options.log_options, result.GetErrorStream());
181     if (success)
182       result.SetStatus(eReturnStatusSuccessFinishNoResult);
183     else
184       result.SetStatus(eReturnStatusFailed);
185     return result.Succeeded();
186   }
187 
188   CommandOptions m_options;
189 };
190 
191 class CommandObjectLogDisable : public CommandObjectParsed {
192 public:
193   //------------------------------------------------------------------
194   // Constructors and Destructors
195   //------------------------------------------------------------------
196   CommandObjectLogDisable(CommandInterpreter &interpreter)
197       : CommandObjectParsed(interpreter, "log disable",
198                             "Disable one or more log channel categories.",
199                             nullptr) {
200     CommandArgumentEntry arg1;
201     CommandArgumentEntry arg2;
202     CommandArgumentData channel_arg;
203     CommandArgumentData category_arg;
204 
205     // Define the first (and only) variant of this arg.
206     channel_arg.arg_type = eArgTypeLogChannel;
207     channel_arg.arg_repetition = eArgRepeatPlain;
208 
209     // There is only one variant this argument could be; put it into the
210     // argument entry.
211     arg1.push_back(channel_arg);
212 
213     category_arg.arg_type = eArgTypeLogCategory;
214     category_arg.arg_repetition = eArgRepeatPlus;
215 
216     arg2.push_back(category_arg);
217 
218     // Push the data for the first argument into the m_arguments vector.
219     m_arguments.push_back(arg1);
220     m_arguments.push_back(arg2);
221   }
222 
223   ~CommandObjectLogDisable() override = default;
224 
225 protected:
226   bool DoExecute(Args &args, CommandReturnObject &result) override {
227     if (args.empty()) {
228       result.AppendErrorWithFormat(
229           "%s takes a log channel and one or more log types.\n",
230           m_cmd_name.c_str());
231       return false;
232     }
233 
234     Log::Callbacks log_callbacks;
235 
236     const std::string channel = args[0].ref;
237     args.Shift(); // Shift off the channel
238     if (Log::GetLogChannelCallbacks(ConstString(channel), log_callbacks)) {
239       log_callbacks.disable(args.GetConstArgumentVector(),
240                             &result.GetErrorStream());
241       result.SetStatus(eReturnStatusSuccessFinishNoResult);
242     } else if (channel == "all") {
243       Log::DisableAllLogChannels(&result.GetErrorStream());
244     } else {
245       LogChannelSP log_channel_sp(LogChannel::FindPlugin(channel.data()));
246       if (log_channel_sp) {
247         log_channel_sp->Disable(args.GetConstArgumentVector(),
248                                 &result.GetErrorStream());
249         result.SetStatus(eReturnStatusSuccessFinishNoResult);
250       } else
251         result.AppendErrorWithFormat("Invalid log channel '%s'.\n",
252                                      channel.data());
253     }
254     return result.Succeeded();
255   }
256 };
257 
258 class CommandObjectLogList : public CommandObjectParsed {
259 public:
260   //------------------------------------------------------------------
261   // Constructors and Destructors
262   //------------------------------------------------------------------
263   CommandObjectLogList(CommandInterpreter &interpreter)
264       : CommandObjectParsed(interpreter, "log list",
265                             "List the log categories for one or more log "
266                             "channels.  If none specified, lists them all.",
267                             nullptr) {
268     CommandArgumentEntry arg;
269     CommandArgumentData channel_arg;
270 
271     // Define the first (and only) variant of this arg.
272     channel_arg.arg_type = eArgTypeLogChannel;
273     channel_arg.arg_repetition = eArgRepeatStar;
274 
275     // There is only one variant this argument could be; put it into the
276     // argument entry.
277     arg.push_back(channel_arg);
278 
279     // Push the data for the first argument into the m_arguments vector.
280     m_arguments.push_back(arg);
281   }
282 
283   ~CommandObjectLogList() override = default;
284 
285 protected:
286   bool DoExecute(Args &args, CommandReturnObject &result) override {
287     if (args.empty()) {
288       Log::ListAllLogChannels(&result.GetOutputStream());
289       result.SetStatus(eReturnStatusSuccessFinishResult);
290     } else {
291       for (auto &entry : args.entries()) {
292         Log::Callbacks log_callbacks;
293 
294         if (Log::GetLogChannelCallbacks(ConstString(entry.ref),
295                                         log_callbacks)) {
296           log_callbacks.list_categories(&result.GetOutputStream());
297           result.SetStatus(eReturnStatusSuccessFinishResult);
298         } else if (entry.ref == "all") {
299           Log::ListAllLogChannels(&result.GetOutputStream());
300           result.SetStatus(eReturnStatusSuccessFinishResult);
301         } else {
302           LogChannelSP log_channel_sp(LogChannel::FindPlugin(entry.c_str()));
303           if (log_channel_sp) {
304             log_channel_sp->ListCategories(&result.GetOutputStream());
305             result.SetStatus(eReturnStatusSuccessFinishNoResult);
306           } else
307             result.AppendErrorWithFormat("Invalid log channel '%s'.\n",
308                                          entry.c_str());
309         }
310       }
311     }
312     return result.Succeeded();
313   }
314 };
315 
316 class CommandObjectLogTimer : public CommandObjectParsed {
317 public:
318   //------------------------------------------------------------------
319   // Constructors and Destructors
320   //------------------------------------------------------------------
321   CommandObjectLogTimer(CommandInterpreter &interpreter)
322       : CommandObjectParsed(interpreter, "log timers",
323                             "Enable, disable, dump, and reset LLDB internal "
324                             "performance timers.",
325                             "log timers < enable <depth> | disable | dump | "
326                             "increment <bool> | reset >") {}
327 
328   ~CommandObjectLogTimer() override = default;
329 
330 protected:
331   bool DoExecute(Args &args, CommandReturnObject &result) override {
332     result.SetStatus(eReturnStatusFailed);
333 
334     if (args.GetArgumentCount() == 1) {
335       auto sub_command = args[0].ref;
336 
337       if (sub_command.equals_lower("enable")) {
338         Timer::SetDisplayDepth(UINT32_MAX);
339         result.SetStatus(eReturnStatusSuccessFinishNoResult);
340       } else if (sub_command.equals_lower("disable")) {
341         Timer::DumpCategoryTimes(&result.GetOutputStream());
342         Timer::SetDisplayDepth(0);
343         result.SetStatus(eReturnStatusSuccessFinishResult);
344       } else if (sub_command.equals_lower("dump")) {
345         Timer::DumpCategoryTimes(&result.GetOutputStream());
346         result.SetStatus(eReturnStatusSuccessFinishResult);
347       } else if (sub_command.equals_lower("reset")) {
348         Timer::ResetCategoryTimes();
349         result.SetStatus(eReturnStatusSuccessFinishResult);
350       }
351     } else if (args.GetArgumentCount() == 2) {
352       auto sub_command = args[0].ref;
353       auto param = args[1].ref;
354 
355       if (sub_command.equals_lower("enable")) {
356         uint32_t depth;
357         if (param.consumeInteger(0, depth)) {
358           result.AppendError(
359               "Could not convert enable depth to an unsigned integer.");
360         } else {
361           Timer::SetDisplayDepth(depth);
362           result.SetStatus(eReturnStatusSuccessFinishNoResult);
363         }
364       } else if (sub_command.equals_lower("increment")) {
365         bool success;
366         bool increment = Args::StringToBoolean(param, false, &success);
367         if (success) {
368           Timer::SetQuiet(!increment);
369           result.SetStatus(eReturnStatusSuccessFinishNoResult);
370         } else
371           result.AppendError("Could not convert increment value to boolean.");
372       }
373     }
374 
375     if (!result.Succeeded()) {
376       result.AppendError("Missing subcommand");
377       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
378     }
379     return result.Succeeded();
380   }
381 };
382 
383 CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter)
384     : CommandObjectMultiword(interpreter, "log",
385                              "Commands controlling LLDB internal logging.",
386                              "log <subcommand> [<command-options>]") {
387   LoadSubCommand("enable",
388                  CommandObjectSP(new CommandObjectLogEnable(interpreter)));
389   LoadSubCommand("disable",
390                  CommandObjectSP(new CommandObjectLogDisable(interpreter)));
391   LoadSubCommand("list",
392                  CommandObjectSP(new CommandObjectLogList(interpreter)));
393   LoadSubCommand("timers",
394                  CommandObjectSP(new CommandObjectLogTimer(interpreter)));
395 }
396 
397 CommandObjectLog::~CommandObjectLog() = default;
398