xref: /llvm-project/lldb/source/Commands/CommandObjectLog.cpp (revision 36162014c4697c30af588197d7cdeb8d2930abbf)
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       return false;
144     }
145 
146     // Store into a std::string since we're about to shift the channel off.
147     const std::string channel = args[0].ref;
148     args.Shift(); // Shift off the channel
149     char log_file[PATH_MAX];
150     if (m_options.log_file)
151       m_options.log_file.GetPath(log_file, sizeof(log_file));
152     else
153       log_file[0] = '\0';
154 
155     std::string error;
156     llvm::raw_string_ostream error_stream(error);
157     bool success =
158         GetDebugger().EnableLog(channel, args.GetArgumentArrayRef(), log_file,
159                                 m_options.log_options, error_stream);
160     result.GetErrorStream() << error_stream.str();
161 
162     if (success)
163       result.SetStatus(eReturnStatusSuccessFinishNoResult);
164     else
165       result.SetStatus(eReturnStatusFailed);
166     return result.Succeeded();
167   }
168 
169   CommandOptions m_options;
170 };
171 
172 class CommandObjectLogDisable : public CommandObjectParsed {
173 public:
174   // Constructors and Destructors
175   CommandObjectLogDisable(CommandInterpreter &interpreter)
176       : CommandObjectParsed(interpreter, "log disable",
177                             "Disable one or more log channel categories.",
178                             nullptr) {
179     CommandArgumentEntry arg1;
180     CommandArgumentEntry arg2;
181     CommandArgumentData channel_arg;
182     CommandArgumentData category_arg;
183 
184     // Define the first (and only) variant of this arg.
185     channel_arg.arg_type = eArgTypeLogChannel;
186     channel_arg.arg_repetition = eArgRepeatPlain;
187 
188     // There is only one variant this argument could be; put it into the
189     // argument entry.
190     arg1.push_back(channel_arg);
191 
192     category_arg.arg_type = eArgTypeLogCategory;
193     category_arg.arg_repetition = eArgRepeatPlus;
194 
195     arg2.push_back(category_arg);
196 
197     // Push the data for the first argument into the m_arguments vector.
198     m_arguments.push_back(arg1);
199     m_arguments.push_back(arg2);
200   }
201 
202   ~CommandObjectLogDisable() override = default;
203 
204 protected:
205   bool DoExecute(Args &args, CommandReturnObject &result) override {
206     if (args.empty()) {
207       result.AppendErrorWithFormat(
208           "%s takes a log channel and one or more log types.\n",
209           m_cmd_name.c_str());
210       return false;
211     }
212 
213     const std::string channel = args[0].ref;
214     args.Shift(); // Shift off the channel
215     if (channel == "all") {
216       Log::DisableAllLogChannels();
217       result.SetStatus(eReturnStatusSuccessFinishNoResult);
218     } else {
219       std::string error;
220       llvm::raw_string_ostream error_stream(error);
221       if (Log::DisableLogChannel(channel, args.GetArgumentArrayRef(),
222                                  error_stream))
223         result.SetStatus(eReturnStatusSuccessFinishNoResult);
224       result.GetErrorStream() << error_stream.str();
225     }
226     return result.Succeeded();
227   }
228 };
229 
230 class CommandObjectLogList : public CommandObjectParsed {
231 public:
232   // Constructors and Destructors
233   CommandObjectLogList(CommandInterpreter &interpreter)
234       : CommandObjectParsed(interpreter, "log list",
235                             "List the log categories for one or more log "
236                             "channels.  If none specified, lists them all.",
237                             nullptr) {
238     CommandArgumentEntry arg;
239     CommandArgumentData channel_arg;
240 
241     // Define the first (and only) variant of this arg.
242     channel_arg.arg_type = eArgTypeLogChannel;
243     channel_arg.arg_repetition = eArgRepeatStar;
244 
245     // There is only one variant this argument could be; put it into the
246     // argument entry.
247     arg.push_back(channel_arg);
248 
249     // Push the data for the first argument into the m_arguments vector.
250     m_arguments.push_back(arg);
251   }
252 
253   ~CommandObjectLogList() override = default;
254 
255 protected:
256   bool DoExecute(Args &args, CommandReturnObject &result) override {
257     std::string output;
258     llvm::raw_string_ostream output_stream(output);
259     if (args.empty()) {
260       Log::ListAllLogChannels(output_stream);
261       result.SetStatus(eReturnStatusSuccessFinishResult);
262     } else {
263       bool success = true;
264       for (const auto &entry : args.entries())
265         success =
266             success && Log::ListChannelCategories(entry.ref, output_stream);
267       if (success)
268         result.SetStatus(eReturnStatusSuccessFinishResult);
269     }
270     result.GetOutputStream() << output_stream.str();
271     return result.Succeeded();
272   }
273 };
274 
275 class CommandObjectLogTimer : public CommandObjectParsed {
276 public:
277   // Constructors and Destructors
278   CommandObjectLogTimer(CommandInterpreter &interpreter)
279       : CommandObjectParsed(interpreter, "log timers",
280                             "Enable, disable, dump, and reset LLDB internal "
281                             "performance timers.",
282                             "log timers < enable <depth> | disable | dump | "
283                             "increment <bool> | reset >") {}
284 
285   ~CommandObjectLogTimer() override = default;
286 
287 protected:
288   bool DoExecute(Args &args, CommandReturnObject &result) override {
289     result.SetStatus(eReturnStatusFailed);
290 
291     if (args.GetArgumentCount() == 1) {
292       auto sub_command = args[0].ref;
293 
294       if (sub_command.equals_lower("enable")) {
295         Timer::SetDisplayDepth(UINT32_MAX);
296         result.SetStatus(eReturnStatusSuccessFinishNoResult);
297       } else if (sub_command.equals_lower("disable")) {
298         Timer::DumpCategoryTimes(&result.GetOutputStream());
299         Timer::SetDisplayDepth(0);
300         result.SetStatus(eReturnStatusSuccessFinishResult);
301       } else if (sub_command.equals_lower("dump")) {
302         Timer::DumpCategoryTimes(&result.GetOutputStream());
303         result.SetStatus(eReturnStatusSuccessFinishResult);
304       } else if (sub_command.equals_lower("reset")) {
305         Timer::ResetCategoryTimes();
306         result.SetStatus(eReturnStatusSuccessFinishResult);
307       }
308     } else if (args.GetArgumentCount() == 2) {
309       auto sub_command = args[0].ref;
310       auto param = args[1].ref;
311 
312       if (sub_command.equals_lower("enable")) {
313         uint32_t depth;
314         if (param.consumeInteger(0, depth)) {
315           result.AppendError(
316               "Could not convert enable depth to an unsigned integer.");
317         } else {
318           Timer::SetDisplayDepth(depth);
319           result.SetStatus(eReturnStatusSuccessFinishNoResult);
320         }
321       } else if (sub_command.equals_lower("increment")) {
322         bool success;
323         bool increment = OptionArgParser::ToBoolean(param, false, &success);
324         if (success) {
325           Timer::SetQuiet(!increment);
326           result.SetStatus(eReturnStatusSuccessFinishNoResult);
327         } else
328           result.AppendError("Could not convert increment value to boolean.");
329       }
330     }
331 
332     if (!result.Succeeded()) {
333       result.AppendError("Missing subcommand");
334       result.AppendErrorWithFormat("Usage: %s\n", m_cmd_syntax.c_str());
335     }
336     return result.Succeeded();
337   }
338 };
339 
340 CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter)
341     : CommandObjectMultiword(interpreter, "log",
342                              "Commands controlling LLDB internal logging.",
343                              "log <subcommand> [<command-options>]") {
344   LoadSubCommand("enable",
345                  CommandObjectSP(new CommandObjectLogEnable(interpreter)));
346   LoadSubCommand("disable",
347                  CommandObjectSP(new CommandObjectLogDisable(interpreter)));
348   LoadSubCommand("list",
349                  CommandObjectSP(new CommandObjectLogList(interpreter)));
350   LoadSubCommand("timers",
351                  CommandObjectSP(new CommandObjectLogTimer(interpreter)));
352 }
353 
354 CommandObjectLog::~CommandObjectLog() = default;
355