xref: /llvm-project/lldb/source/Commands/CommandObjectLog.cpp (revision bd68a052f292b5df7c5717bd880d796ac7507fc0)
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         error.SetErrorStringWithFormat("unrecognized option '%c'",
117                                        short_option);
118         break;
119       }
120 
121       return error;
122     }
123 
124     void OptionParsingStarting(ExecutionContext *execution_context) override {
125       log_file.Clear();
126       log_options = 0;
127     }
128 
129     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
130       return llvm::makeArrayRef(g_log_options);
131     }
132 
133     // Instance variables to hold the values for command options.
134 
135     FileSpec log_file;
136     uint32_t log_options;
137   };
138 
139 protected:
140   bool DoExecute(Args &args, CommandReturnObject &result) override {
141     if (args.GetArgumentCount() < 2) {
142       result.AppendErrorWithFormat(
143           "%s takes a log channel and one or more log types.\n",
144           m_cmd_name.c_str());
145       return false;
146     }
147 
148     // Store into a std::string since we're about to shift the channel off.
149     const std::string channel = args[0].ref;
150     args.Shift(); // Shift off the channel
151     char log_file[PATH_MAX];
152     if (m_options.log_file)
153       m_options.log_file.GetPath(log_file, sizeof(log_file));
154     else
155       log_file[0] = '\0';
156 
157     std::string error;
158     llvm::raw_string_ostream error_stream(error);
159     bool success =
160         GetDebugger().EnableLog(channel, args.GetArgumentArrayRef(), log_file,
161                                 m_options.log_options, error_stream);
162     result.GetErrorStream() << error_stream.str();
163 
164     if (success)
165       result.SetStatus(eReturnStatusSuccessFinishNoResult);
166     else
167       result.SetStatus(eReturnStatusFailed);
168     return result.Succeeded();
169   }
170 
171   CommandOptions m_options;
172 };
173 
174 class CommandObjectLogDisable : public CommandObjectParsed {
175 public:
176   // Constructors and Destructors
177   CommandObjectLogDisable(CommandInterpreter &interpreter)
178       : CommandObjectParsed(interpreter, "log disable",
179                             "Disable one or more log channel categories.",
180                             nullptr) {
181     CommandArgumentEntry arg1;
182     CommandArgumentEntry arg2;
183     CommandArgumentData channel_arg;
184     CommandArgumentData category_arg;
185 
186     // Define the first (and only) variant of this arg.
187     channel_arg.arg_type = eArgTypeLogChannel;
188     channel_arg.arg_repetition = eArgRepeatPlain;
189 
190     // There is only one variant this argument could be; put it into the
191     // argument entry.
192     arg1.push_back(channel_arg);
193 
194     category_arg.arg_type = eArgTypeLogCategory;
195     category_arg.arg_repetition = eArgRepeatPlus;
196 
197     arg2.push_back(category_arg);
198 
199     // Push the data for the first argument into the m_arguments vector.
200     m_arguments.push_back(arg1);
201     m_arguments.push_back(arg2);
202   }
203 
204   ~CommandObjectLogDisable() override = default;
205 
206 protected:
207   bool DoExecute(Args &args, CommandReturnObject &result) override {
208     if (args.empty()) {
209       result.AppendErrorWithFormat(
210           "%s takes a log channel and one or more log types.\n",
211           m_cmd_name.c_str());
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