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