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