xref: /llvm-project/lldb/source/Commands/CommandObjectLog.cpp (revision 434b81d0a26a69df5b6862d09c0feca8f99ae391)
1 //===-- CommandObjectLog.cpp ------------------------------------*- C++ -*-===//
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/Core/Module.h"
12 #include "lldb/Core/StreamFile.h"
13 #include "lldb/Host/OptionParser.h"
14 #include "lldb/Interpreter/CommandInterpreter.h"
15 #include "lldb/Interpreter/CommandReturnObject.h"
16 #include "lldb/Interpreter/OptionArgParser.h"
17 #include "lldb/Interpreter/Options.h"
18 #include "lldb/Symbol/LineTable.h"
19 #include "lldb/Symbol/ObjectFile.h"
20 #include "lldb/Symbol/SymbolFile.h"
21 #include "lldb/Symbol/SymbolVendor.h"
22 #include "lldb/Target/Process.h"
23 #include "lldb/Target/Target.h"
24 #include "lldb/Utility/Args.h"
25 #include "lldb/Utility/FileSpec.h"
26 #include "lldb/Utility/Log.h"
27 #include "lldb/Utility/RegularExpression.h"
28 #include "lldb/Utility/Stream.h"
29 #include "lldb/Utility/Timer.h"
30 
31 using namespace lldb;
32 using namespace lldb_private;
33 
34 #define LLDB_OPTIONS_log
35 #include "CommandOptions.inc"
36 
37 class CommandObjectLogEnable : public CommandObjectParsed {
38 public:
39   // Constructors and Destructors
40   CommandObjectLogEnable(CommandInterpreter &interpreter)
41       : CommandObjectParsed(interpreter, "log enable",
42                             "Enable logging for a single log channel.",
43                             nullptr),
44         m_options() {
45     CommandArgumentEntry arg1;
46     CommandArgumentEntry arg2;
47     CommandArgumentData channel_arg;
48     CommandArgumentData category_arg;
49 
50     // Define the first (and only) variant of this arg.
51     channel_arg.arg_type = eArgTypeLogChannel;
52     channel_arg.arg_repetition = eArgRepeatPlain;
53 
54     // There is only one variant this argument could be; put it into the
55     // argument entry.
56     arg1.push_back(channel_arg);
57 
58     category_arg.arg_type = eArgTypeLogCategory;
59     category_arg.arg_repetition = eArgRepeatPlus;
60 
61     arg2.push_back(category_arg);
62 
63     // Push the data for the first argument into the m_arguments vector.
64     m_arguments.push_back(arg1);
65     m_arguments.push_back(arg2);
66   }
67 
68   ~CommandObjectLogEnable() override = default;
69 
70   Options *GetOptions() override { return &m_options; }
71 
72   class CommandOptions : public Options {
73   public:
74     CommandOptions() : Options(), log_file(), log_options(0) {}
75 
76     ~CommandOptions() override = default;
77 
78     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
79                           ExecutionContext *execution_context) override {
80       Status error;
81       const int short_option = m_getopt_table[option_idx].val;
82 
83       switch (short_option) {
84       case 'f':
85         log_file.SetFile(option_arg, FileSpec::Style::native);
86         FileSystem::Instance().Resolve(log_file);
87         break;
88       case 't':
89         log_options |= LLDB_LOG_OPTION_THREADSAFE;
90         break;
91       case 'v':
92         log_options |= LLDB_LOG_OPTION_VERBOSE;
93         break;
94       case 's':
95         log_options |= LLDB_LOG_OPTION_PREPEND_SEQUENCE;
96         break;
97       case 'T':
98         log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP;
99         break;
100       case 'p':
101         log_options |= LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD;
102         break;
103       case 'n':
104         log_options |= LLDB_LOG_OPTION_PREPEND_THREAD_NAME;
105         break;
106       case 'S':
107         log_options |= LLDB_LOG_OPTION_BACKTRACE;
108         break;
109       case 'a':
110         log_options |= LLDB_LOG_OPTION_APPEND;
111         break;
112       case 'F':
113         log_options |= LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION;
114         break;
115       default:
116         llvm_unreachable("Unimplemented option");
117       }
118 
119       return error;
120     }
121 
122     void OptionParsingStarting(ExecutionContext *execution_context) override {
123       log_file.Clear();
124       log_options = 0;
125     }
126 
127     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
128       return llvm::makeArrayRef(g_log_options);
129     }
130 
131     // Instance variables to hold the values for command options.
132 
133     FileSpec log_file;
134     uint32_t log_options;
135   };
136 
137 protected:
138   bool DoExecute(Args &args, CommandReturnObject &result) override {
139     if (args.GetArgumentCount() < 2) {
140       result.AppendErrorWithFormat(
141           "%s takes a log channel and one or more log types.\n",
142           m_cmd_name.c_str());
143       result.SetStatus(eReturnStatusFailed);
144       return false;
145     }
146 
147     // Store into a std::string since we're about to shift the channel off.
148     const std::string channel = args[0].ref;
149     args.Shift(); // Shift off the channel
150     char log_file[PATH_MAX];
151     if (m_options.log_file)
152       m_options.log_file.GetPath(log_file, sizeof(log_file));
153     else
154       log_file[0] = '\0';
155 
156     std::string error;
157     llvm::raw_string_ostream error_stream(error);
158     bool success =
159         GetDebugger().EnableLog(channel, args.GetArgumentArrayRef(), log_file,
160                                 m_options.log_options, error_stream);
161     result.GetErrorStream() << error_stream.str();
162 
163     if (success)
164       result.SetStatus(eReturnStatusSuccessFinishNoResult);
165     else
166       result.SetStatus(eReturnStatusFailed);
167     return result.Succeeded();
168   }
169 
170   CommandOptions m_options;
171 };
172 
173 class CommandObjectLogDisable : public CommandObjectParsed {
174 public:
175   // Constructors and Destructors
176   CommandObjectLogDisable(CommandInterpreter &interpreter)
177       : CommandObjectParsed(interpreter, "log disable",
178                             "Disable one or more log channel categories.",
179                             nullptr) {
180     CommandArgumentEntry arg1;
181     CommandArgumentEntry arg2;
182     CommandArgumentData channel_arg;
183     CommandArgumentData category_arg;
184 
185     // Define the first (and only) variant of this arg.
186     channel_arg.arg_type = eArgTypeLogChannel;
187     channel_arg.arg_repetition = eArgRepeatPlain;
188 
189     // There is only one variant this argument could be; put it into the
190     // argument entry.
191     arg1.push_back(channel_arg);
192 
193     category_arg.arg_type = eArgTypeLogCategory;
194     category_arg.arg_repetition = eArgRepeatPlus;
195 
196     arg2.push_back(category_arg);
197 
198     // Push the data for the first argument into the m_arguments vector.
199     m_arguments.push_back(arg1);
200     m_arguments.push_back(arg2);
201   }
202 
203   ~CommandObjectLogDisable() override = default;
204 
205 protected:
206   bool DoExecute(Args &args, CommandReturnObject &result) override {
207     if (args.empty()) {
208       result.AppendErrorWithFormat(
209           "%s takes a log channel and one or more log types.\n",
210           m_cmd_name.c_str());
211       result.SetStatus(eReturnStatusFailed);
212       return false;
213     }
214 
215     const std::string channel = args[0].ref;
216     args.Shift(); // Shift off the channel
217     if (channel == "all") {
218       Log::DisableAllLogChannels();
219       result.SetStatus(eReturnStatusSuccessFinishNoResult);
220     } else {
221       std::string error;
222       llvm::raw_string_ostream error_stream(error);
223       if (Log::DisableLogChannel(channel, args.GetArgumentArrayRef(),
224                                  error_stream))
225         result.SetStatus(eReturnStatusSuccessFinishNoResult);
226       result.GetErrorStream() << error_stream.str();
227     }
228     return result.Succeeded();
229   }
230 };
231 
232 class CommandObjectLogList : public CommandObjectParsed {
233 public:
234   // Constructors and Destructors
235   CommandObjectLogList(CommandInterpreter &interpreter)
236       : CommandObjectParsed(interpreter, "log list",
237                             "List the log categories for one or more log "
238                             "channels.  If none specified, lists them all.",
239                             nullptr) {
240     CommandArgumentEntry arg;
241     CommandArgumentData channel_arg;
242 
243     // Define the first (and only) variant of this arg.
244     channel_arg.arg_type = eArgTypeLogChannel;
245     channel_arg.arg_repetition = eArgRepeatStar;
246 
247     // There is only one variant this argument could be; put it into the
248     // argument entry.
249     arg.push_back(channel_arg);
250 
251     // Push the data for the first argument into the m_arguments vector.
252     m_arguments.push_back(arg);
253   }
254 
255   ~CommandObjectLogList() override = default;
256 
257 protected:
258   bool DoExecute(Args &args, CommandReturnObject &result) override {
259     std::string output;
260     llvm::raw_string_ostream output_stream(output);
261     if (args.empty()) {
262       Log::ListAllLogChannels(output_stream);
263       result.SetStatus(eReturnStatusSuccessFinishResult);
264     } else {
265       bool success = true;
266       for (const auto &entry : args.entries())
267         success =
268             success && Log::ListChannelCategories(entry.ref, output_stream);
269       if (success)
270         result.SetStatus(eReturnStatusSuccessFinishResult);
271     }
272     result.GetOutputStream() << output_stream.str();
273     return result.Succeeded();
274   }
275 };
276 
277 class CommandObjectLogTimer : public CommandObjectParsed {
278 public:
279   // Constructors and Destructors
280   CommandObjectLogTimer(CommandInterpreter &interpreter)
281       : CommandObjectParsed(interpreter, "log timers",
282                             "Enable, disable, dump, and reset LLDB internal "
283                             "performance timers.",
284                             "log timers < enable <depth> | disable | dump | "
285                             "increment <bool> | reset >") {}
286 
287   ~CommandObjectLogTimer() override = default;
288 
289 protected:
290   bool DoExecute(Args &args, CommandReturnObject &result) override {
291     result.SetStatus(eReturnStatusFailed);
292 
293     if (args.GetArgumentCount() == 1) {
294       auto sub_command = args[0].ref;
295 
296       if (sub_command.equals_lower("enable")) {
297         Timer::SetDisplayDepth(UINT32_MAX);
298         result.SetStatus(eReturnStatusSuccessFinishNoResult);
299       } else if (sub_command.equals_lower("disable")) {
300         Timer::DumpCategoryTimes(&result.GetOutputStream());
301         Timer::SetDisplayDepth(0);
302         result.SetStatus(eReturnStatusSuccessFinishResult);
303       } else if (sub_command.equals_lower("dump")) {
304         Timer::DumpCategoryTimes(&result.GetOutputStream());
305         result.SetStatus(eReturnStatusSuccessFinishResult);
306       } else if (sub_command.equals_lower("reset")) {
307         Timer::ResetCategoryTimes();
308         result.SetStatus(eReturnStatusSuccessFinishResult);
309       }
310     } else if (args.GetArgumentCount() == 2) {
311       auto sub_command = args[0].ref;
312       auto param = args[1].ref;
313 
314       if (sub_command.equals_lower("enable")) {
315         uint32_t depth;
316         if (param.consumeInteger(0, depth)) {
317           result.AppendError(
318               "Could not convert enable depth to an unsigned integer.");
319         } else {
320           Timer::SetDisplayDepth(depth);
321           result.SetStatus(eReturnStatusSuccessFinishNoResult);
322         }
323       } else if (sub_command.equals_lower("increment")) {
324         bool success;
325         bool increment = OptionArgParser::ToBoolean(param, false, &success);
326         if (success) {
327           Timer::SetQuiet(!increment);
328           result.SetStatus(eReturnStatusSuccessFinishNoResult);
329         } else
330           result.AppendError("Could not convert increment value to boolean.");
331       }
332     }
333 
334     if (!result.Succeeded()) {
335       result.AppendError("Missing subcommand");
336       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
337     }
338     return result.Succeeded();
339   }
340 };
341 
342 CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter)
343     : CommandObjectMultiword(interpreter, "log",
344                              "Commands controlling LLDB internal logging.",
345                              "log <subcommand> [<command-options>]") {
346   LoadSubCommand("enable",
347                  CommandObjectSP(new CommandObjectLogEnable(interpreter)));
348   LoadSubCommand("disable",
349                  CommandObjectSP(new CommandObjectLogDisable(interpreter)));
350   LoadSubCommand("list",
351                  CommandObjectSP(new CommandObjectLogList(interpreter)));
352   LoadSubCommand("timers",
353                  CommandObjectSP(new CommandObjectLogTimer(interpreter)));
354 }
355 
356 CommandObjectLog::~CommandObjectLog() = default;
357