xref: /llvm-project/lldb/source/Commands/CommandObjectLog.cpp (revision 107d9bbd6c7280959556da864c0c67dc9d951e14)
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/RegularExpression.h"
20 #include "lldb/Core/Stream.h"
21 #include "lldb/Core/StreamFile.h"
22 #include "lldb/Core/Timer.h"
23 #include "lldb/Host/FileSpec.h"
24 #include "lldb/Host/StringConvert.h"
25 #include "lldb/Interpreter/Args.h"
26 #include "lldb/Interpreter/CommandInterpreter.h"
27 #include "lldb/Interpreter/CommandReturnObject.h"
28 #include "lldb/Interpreter/Options.h"
29 #include "lldb/Symbol/LineTable.h"
30 #include "lldb/Symbol/ObjectFile.h"
31 #include "lldb/Symbol/SymbolFile.h"
32 #include "lldb/Symbol/SymbolVendor.h"
33 #include "lldb/Target/Process.h"
34 #include "lldb/Target/Target.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       default:
137         error.SetErrorStringWithFormat("unrecognized option '%c'",
138                                        short_option);
139         break;
140       }
141 
142       return error;
143     }
144 
145     void OptionParsingStarting(ExecutionContext *execution_context) override {
146       log_file.Clear();
147       log_options = 0;
148     }
149 
150     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
151       return llvm::makeArrayRef(g_log_options);
152     }
153 
154     // Instance variables to hold the values for command options.
155 
156     FileSpec log_file;
157     uint32_t log_options;
158   };
159 
160 protected:
161   bool DoExecute(Args &args, CommandReturnObject &result) override {
162     if (args.GetArgumentCount() < 2) {
163       result.AppendErrorWithFormat(
164           "%s takes a log channel and one or more log types.\n",
165           m_cmd_name.c_str());
166       return false;
167     }
168 
169     // Store into a std::string since we're about to shift the channel off.
170     const std::string channel = args[0].ref;
171     args.Shift(); // Shift off the channel
172     char log_file[PATH_MAX];
173     if (m_options.log_file)
174       m_options.log_file.GetPath(log_file, sizeof(log_file));
175     else
176       log_file[0] = '\0';
177     bool success = m_interpreter.GetDebugger().EnableLog(
178         channel.c_str(), args.GetConstArgumentVector(), log_file,
179         m_options.log_options, result.GetErrorStream());
180     if (success)
181       result.SetStatus(eReturnStatusSuccessFinishNoResult);
182     else
183       result.SetStatus(eReturnStatusFailed);
184     return result.Succeeded();
185   }
186 
187   CommandOptions m_options;
188 };
189 
190 class CommandObjectLogDisable : public CommandObjectParsed {
191 public:
192   //------------------------------------------------------------------
193   // Constructors and Destructors
194   //------------------------------------------------------------------
195   CommandObjectLogDisable(CommandInterpreter &interpreter)
196       : CommandObjectParsed(interpreter, "log disable",
197                             "Disable one or more log channel categories.",
198                             nullptr) {
199     CommandArgumentEntry arg1;
200     CommandArgumentEntry arg2;
201     CommandArgumentData channel_arg;
202     CommandArgumentData category_arg;
203 
204     // Define the first (and only) variant of this arg.
205     channel_arg.arg_type = eArgTypeLogChannel;
206     channel_arg.arg_repetition = eArgRepeatPlain;
207 
208     // There is only one variant this argument could be; put it into the
209     // argument entry.
210     arg1.push_back(channel_arg);
211 
212     category_arg.arg_type = eArgTypeLogCategory;
213     category_arg.arg_repetition = eArgRepeatPlus;
214 
215     arg2.push_back(category_arg);
216 
217     // Push the data for the first argument into the m_arguments vector.
218     m_arguments.push_back(arg1);
219     m_arguments.push_back(arg2);
220   }
221 
222   ~CommandObjectLogDisable() override = default;
223 
224 protected:
225   bool DoExecute(Args &args, CommandReturnObject &result) override {
226     if (args.empty()) {
227       result.AppendErrorWithFormat(
228           "%s takes a log channel and one or more log types.\n",
229           m_cmd_name.c_str());
230       return false;
231     }
232 
233     Log::Callbacks log_callbacks;
234 
235     const std::string channel = args[0].ref;
236     args.Shift(); // Shift off the channel
237     if (Log::GetLogChannelCallbacks(ConstString(channel), log_callbacks)) {
238       log_callbacks.disable(args.GetConstArgumentVector(),
239                             &result.GetErrorStream());
240       result.SetStatus(eReturnStatusSuccessFinishNoResult);
241     } else if (channel == "all") {
242       Log::DisableAllLogChannels(&result.GetErrorStream());
243     } else {
244       LogChannelSP log_channel_sp(LogChannel::FindPlugin(channel.data()));
245       if (log_channel_sp) {
246         log_channel_sp->Disable(args.GetConstArgumentVector(),
247                                 &result.GetErrorStream());
248         result.SetStatus(eReturnStatusSuccessFinishNoResult);
249       } else
250         result.AppendErrorWithFormat("Invalid log channel '%s'.\n",
251                                      channel.data());
252     }
253     return result.Succeeded();
254   }
255 };
256 
257 class CommandObjectLogList : public CommandObjectParsed {
258 public:
259   //------------------------------------------------------------------
260   // Constructors and Destructors
261   //------------------------------------------------------------------
262   CommandObjectLogList(CommandInterpreter &interpreter)
263       : CommandObjectParsed(interpreter, "log list",
264                             "List the log categories for one or more log "
265                             "channels.  If none specified, lists them all.",
266                             nullptr) {
267     CommandArgumentEntry arg;
268     CommandArgumentData channel_arg;
269 
270     // Define the first (and only) variant of this arg.
271     channel_arg.arg_type = eArgTypeLogChannel;
272     channel_arg.arg_repetition = eArgRepeatStar;
273 
274     // There is only one variant this argument could be; put it into the
275     // argument entry.
276     arg.push_back(channel_arg);
277 
278     // Push the data for the first argument into the m_arguments vector.
279     m_arguments.push_back(arg);
280   }
281 
282   ~CommandObjectLogList() override = default;
283 
284 protected:
285   bool DoExecute(Args &args, CommandReturnObject &result) override {
286     if (args.empty()) {
287       Log::ListAllLogChannels(&result.GetOutputStream());
288       result.SetStatus(eReturnStatusSuccessFinishResult);
289     } else {
290       for (auto &entry : args.entries()) {
291         Log::Callbacks log_callbacks;
292 
293         if (Log::GetLogChannelCallbacks(ConstString(entry.ref),
294                                         log_callbacks)) {
295           log_callbacks.list_categories(&result.GetOutputStream());
296           result.SetStatus(eReturnStatusSuccessFinishResult);
297         } else if (entry.ref == "all") {
298           Log::ListAllLogChannels(&result.GetOutputStream());
299           result.SetStatus(eReturnStatusSuccessFinishResult);
300         } else {
301           LogChannelSP log_channel_sp(LogChannel::FindPlugin(entry.c_str()));
302           if (log_channel_sp) {
303             log_channel_sp->ListCategories(&result.GetOutputStream());
304             result.SetStatus(eReturnStatusSuccessFinishNoResult);
305           } else
306             result.AppendErrorWithFormat("Invalid log channel '%s'.\n",
307                                          entry.c_str());
308         }
309       }
310     }
311     return result.Succeeded();
312   }
313 };
314 
315 class CommandObjectLogTimer : public CommandObjectParsed {
316 public:
317   //------------------------------------------------------------------
318   // Constructors and Destructors
319   //------------------------------------------------------------------
320   CommandObjectLogTimer(CommandInterpreter &interpreter)
321       : CommandObjectParsed(interpreter, "log timers",
322                             "Enable, disable, dump, and reset LLDB internal "
323                             "performance timers.",
324                             "log timers < enable <depth> | disable | dump | "
325                             "increment <bool> | reset >") {}
326 
327   ~CommandObjectLogTimer() override = default;
328 
329 protected:
330   bool DoExecute(Args &args, CommandReturnObject &result) override {
331     result.SetStatus(eReturnStatusFailed);
332 
333     if (args.GetArgumentCount() == 1) {
334       auto sub_command = args[0].ref;
335 
336       if (sub_command.equals_lower("enable")) {
337         Timer::SetDisplayDepth(UINT32_MAX);
338         result.SetStatus(eReturnStatusSuccessFinishNoResult);
339       } else if (sub_command.equals_lower("disable")) {
340         Timer::DumpCategoryTimes(&result.GetOutputStream());
341         Timer::SetDisplayDepth(0);
342         result.SetStatus(eReturnStatusSuccessFinishResult);
343       } else if (sub_command.equals_lower("dump")) {
344         Timer::DumpCategoryTimes(&result.GetOutputStream());
345         result.SetStatus(eReturnStatusSuccessFinishResult);
346       } else if (sub_command.equals_lower("reset")) {
347         Timer::ResetCategoryTimes();
348         result.SetStatus(eReturnStatusSuccessFinishResult);
349       }
350     } else if (args.GetArgumentCount() == 2) {
351       auto sub_command = args[0].ref;
352       auto param = args[1].ref;
353 
354       if (sub_command.equals_lower("enable")) {
355         uint32_t depth;
356         if (param.consumeInteger(0, depth)) {
357           result.AppendError(
358               "Could not convert enable depth to an unsigned integer.");
359         } else {
360           Timer::SetDisplayDepth(depth);
361           result.SetStatus(eReturnStatusSuccessFinishNoResult);
362         }
363       } else if (sub_command.equals_lower("increment")) {
364         bool success;
365         bool increment = Args::StringToBoolean(param, false, &success);
366         if (success) {
367           Timer::SetQuiet(!increment);
368           result.SetStatus(eReturnStatusSuccessFinishNoResult);
369         } else
370           result.AppendError("Could not convert increment value to boolean.");
371       }
372     }
373 
374     if (!result.Succeeded()) {
375       result.AppendError("Missing subcommand");
376       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
377     }
378     return result.Succeeded();
379   }
380 };
381 
382 CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter)
383     : CommandObjectMultiword(interpreter, "log",
384                              "Commands controlling LLDB internal logging.",
385                              "log <subcommand> [<command-options>]") {
386   LoadSubCommand("enable",
387                  CommandObjectSP(new CommandObjectLogEnable(interpreter)));
388   LoadSubCommand("disable",
389                  CommandObjectSP(new CommandObjectLogDisable(interpreter)));
390   LoadSubCommand("list",
391                  CommandObjectSP(new CommandObjectLogList(interpreter)));
392   LoadSubCommand("timers",
393                  CommandObjectSP(new CommandObjectLogTimer(interpreter)));
394 }
395 
396 CommandObjectLog::~CommandObjectLog() = default;
397