xref: /openbsd-src/gnu/llvm/lldb/tools/driver/Driver.cpp (revision 101d251d5caf88a9341f3045ab62e122abae1b90)
1061da546Spatrick //===-- Driver.cpp ----------------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "Driver.h"
10061da546Spatrick 
11061da546Spatrick #include "lldb/API/SBCommandInterpreter.h"
12dda28197Spatrick #include "lldb/API/SBCommandInterpreterRunOptions.h"
13061da546Spatrick #include "lldb/API/SBCommandReturnObject.h"
14061da546Spatrick #include "lldb/API/SBDebugger.h"
15061da546Spatrick #include "lldb/API/SBFile.h"
16061da546Spatrick #include "lldb/API/SBHostOS.h"
17061da546Spatrick #include "lldb/API/SBLanguageRuntime.h"
18061da546Spatrick #include "lldb/API/SBStream.h"
19061da546Spatrick #include "lldb/API/SBStringList.h"
20*101d251dSrobert #include "lldb/API/SBStructuredData.h"
21061da546Spatrick 
22061da546Spatrick #include "llvm/ADT/StringRef.h"
23061da546Spatrick #include "llvm/Support/Format.h"
24061da546Spatrick #include "llvm/Support/InitLLVM.h"
25061da546Spatrick #include "llvm/Support/Path.h"
26061da546Spatrick #include "llvm/Support/Signals.h"
27061da546Spatrick #include "llvm/Support/WithColor.h"
28061da546Spatrick #include "llvm/Support/raw_ostream.h"
29061da546Spatrick 
30061da546Spatrick #include <algorithm>
31061da546Spatrick #include <atomic>
32061da546Spatrick #include <bitset>
33a0747c9fSpatrick #include <clocale>
34061da546Spatrick #include <csignal>
35061da546Spatrick #include <string>
36061da546Spatrick #include <thread>
37061da546Spatrick #include <utility>
38061da546Spatrick 
39a0747c9fSpatrick #include <climits>
40a0747c9fSpatrick #include <cstdio>
41a0747c9fSpatrick #include <cstdlib>
42a0747c9fSpatrick #include <cstring>
43061da546Spatrick #include <fcntl.h>
44061da546Spatrick 
45061da546Spatrick #if !defined(__APPLE__)
46061da546Spatrick #include "llvm/Support/DataTypes.h"
47061da546Spatrick #endif
48061da546Spatrick 
49061da546Spatrick using namespace lldb;
50061da546Spatrick using namespace llvm;
51061da546Spatrick 
52061da546Spatrick namespace {
53061da546Spatrick enum ID {
54061da546Spatrick   OPT_INVALID = 0, // This is not an option ID.
55061da546Spatrick #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
56061da546Spatrick                HELPTEXT, METAVAR, VALUES)                                      \
57061da546Spatrick   OPT_##ID,
58061da546Spatrick #include "Options.inc"
59061da546Spatrick #undef OPTION
60061da546Spatrick };
61061da546Spatrick 
62*101d251dSrobert #define PREFIX(NAME, VALUE)                                                    \
63*101d251dSrobert   static constexpr StringLiteral NAME##_init[] = VALUE;                        \
64*101d251dSrobert   static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
65*101d251dSrobert                                                 std::size(NAME##_init) - 1);
66061da546Spatrick #include "Options.inc"
67061da546Spatrick #undef PREFIX
68061da546Spatrick 
69*101d251dSrobert static constexpr opt::OptTable::Info InfoTable[] = {
70061da546Spatrick #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
71061da546Spatrick                HELPTEXT, METAVAR, VALUES)                                      \
72061da546Spatrick   {                                                                            \
73061da546Spatrick       PREFIX,      NAME,      HELPTEXT,                                        \
74061da546Spatrick       METAVAR,     OPT_##ID,  opt::Option::KIND##Class,                        \
75061da546Spatrick       PARAM,       FLAGS,     OPT_##GROUP,                                     \
76061da546Spatrick       OPT_##ALIAS, ALIASARGS, VALUES},
77061da546Spatrick #include "Options.inc"
78061da546Spatrick #undef OPTION
79061da546Spatrick };
80061da546Spatrick 
81*101d251dSrobert class LLDBOptTable : public opt::GenericOptTable {
82061da546Spatrick public:
LLDBOptTable()83*101d251dSrobert   LLDBOptTable() : opt::GenericOptTable(InfoTable) {}
84061da546Spatrick };
85061da546Spatrick } // namespace
86061da546Spatrick 
87061da546Spatrick static void reset_stdin_termios();
88061da546Spatrick static bool g_old_stdin_termios_is_valid = false;
89061da546Spatrick static struct termios g_old_stdin_termios;
90061da546Spatrick 
disable_color(const raw_ostream & OS)91*101d251dSrobert static bool disable_color(const raw_ostream &OS) { return false; }
92*101d251dSrobert 
93061da546Spatrick static Driver *g_driver = nullptr;
94061da546Spatrick 
95061da546Spatrick // In the Driver::MainLoop, we change the terminal settings.  This function is
96061da546Spatrick // added as an atexit handler to make sure we clean them up.
reset_stdin_termios()97061da546Spatrick static void reset_stdin_termios() {
98061da546Spatrick   if (g_old_stdin_termios_is_valid) {
99061da546Spatrick     g_old_stdin_termios_is_valid = false;
100061da546Spatrick     ::tcsetattr(STDIN_FILENO, TCSANOW, &g_old_stdin_termios);
101061da546Spatrick   }
102061da546Spatrick }
103061da546Spatrick 
Driver()104061da546Spatrick Driver::Driver()
105061da546Spatrick     : SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(false)) {
106061da546Spatrick   // We want to be able to handle CTRL+D in the terminal to have it terminate
107061da546Spatrick   // certain input
108061da546Spatrick   m_debugger.SetCloseInputOnEOF(false);
109061da546Spatrick   g_driver = this;
110061da546Spatrick }
111061da546Spatrick 
~Driver()112a0747c9fSpatrick Driver::~Driver() {
113a0747c9fSpatrick   SBDebugger::Destroy(m_debugger);
114a0747c9fSpatrick   g_driver = nullptr;
115a0747c9fSpatrick }
116061da546Spatrick 
AddInitialCommand(std::string command,CommandPlacement placement,bool is_file,SBError & error)117061da546Spatrick void Driver::OptionData::AddInitialCommand(std::string command,
118061da546Spatrick                                            CommandPlacement placement,
119061da546Spatrick                                            bool is_file, SBError &error) {
120061da546Spatrick   std::vector<InitialCmdEntry> *command_set;
121061da546Spatrick   switch (placement) {
122061da546Spatrick   case eCommandPlacementBeforeFile:
123061da546Spatrick     command_set = &(m_initial_commands);
124061da546Spatrick     break;
125061da546Spatrick   case eCommandPlacementAfterFile:
126061da546Spatrick     command_set = &(m_after_file_commands);
127061da546Spatrick     break;
128061da546Spatrick   case eCommandPlacementAfterCrash:
129061da546Spatrick     command_set = &(m_after_crash_commands);
130061da546Spatrick     break;
131061da546Spatrick   }
132061da546Spatrick 
133061da546Spatrick   if (is_file) {
134061da546Spatrick     SBFileSpec file(command.c_str());
135061da546Spatrick     if (file.Exists())
136061da546Spatrick       command_set->push_back(InitialCmdEntry(command, is_file));
137061da546Spatrick     else if (file.ResolveExecutableLocation()) {
138061da546Spatrick       char final_path[PATH_MAX];
139061da546Spatrick       file.GetPath(final_path, sizeof(final_path));
140061da546Spatrick       command_set->push_back(InitialCmdEntry(final_path, is_file));
141061da546Spatrick     } else
142061da546Spatrick       error.SetErrorStringWithFormat(
143061da546Spatrick           "file specified in --source (-s) option doesn't exist: '%s'",
144061da546Spatrick           command.c_str());
145061da546Spatrick   } else
146061da546Spatrick     command_set->push_back(InitialCmdEntry(command, is_file));
147061da546Spatrick }
148061da546Spatrick 
WriteCommandsForSourcing(CommandPlacement placement,SBStream & strm)149061da546Spatrick void Driver::WriteCommandsForSourcing(CommandPlacement placement,
150061da546Spatrick                                       SBStream &strm) {
151061da546Spatrick   std::vector<OptionData::InitialCmdEntry> *command_set;
152061da546Spatrick   switch (placement) {
153061da546Spatrick   case eCommandPlacementBeforeFile:
154061da546Spatrick     command_set = &m_option_data.m_initial_commands;
155061da546Spatrick     break;
156061da546Spatrick   case eCommandPlacementAfterFile:
157061da546Spatrick     command_set = &m_option_data.m_after_file_commands;
158061da546Spatrick     break;
159061da546Spatrick   case eCommandPlacementAfterCrash:
160061da546Spatrick     command_set = &m_option_data.m_after_crash_commands;
161061da546Spatrick     break;
162061da546Spatrick   }
163061da546Spatrick 
164061da546Spatrick   for (const auto &command_entry : *command_set) {
165061da546Spatrick     const char *command = command_entry.contents.c_str();
166061da546Spatrick     if (command_entry.is_file) {
167061da546Spatrick       bool source_quietly =
168061da546Spatrick           m_option_data.m_source_quietly || command_entry.source_quietly;
169061da546Spatrick       strm.Printf("command source -s %i '%s'\n",
170061da546Spatrick                   static_cast<int>(source_quietly), command);
171061da546Spatrick     } else
172061da546Spatrick       strm.Printf("%s\n", command);
173061da546Spatrick   }
174061da546Spatrick }
175061da546Spatrick 
176061da546Spatrick // Check the arguments that were passed to this program to make sure they are
177061da546Spatrick // valid and to get their argument values (if any).  Return a boolean value
178061da546Spatrick // indicating whether or not to start up the full debugger (i.e. the Command
179061da546Spatrick // Interpreter) or not.  Return FALSE if the arguments were invalid OR if the
180061da546Spatrick // user only wanted help or version information.
ProcessArgs(const opt::InputArgList & args,bool & exiting)181061da546Spatrick SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) {
182061da546Spatrick   SBError error;
183061da546Spatrick 
184061da546Spatrick   // This is kind of a pain, but since we make the debugger in the Driver's
185061da546Spatrick   // constructor, we can't know at that point whether we should read in init
186061da546Spatrick   // files yet.  So we don't read them in in the Driver constructor, then set
187061da546Spatrick   // the flags back to "read them in" here, and then if we see the "-n" flag,
188061da546Spatrick   // we'll turn it off again.  Finally we have to read them in by hand later in
189061da546Spatrick   // the main loop.
190061da546Spatrick   m_debugger.SkipLLDBInitFiles(false);
191061da546Spatrick   m_debugger.SkipAppInitFiles(false);
192061da546Spatrick 
193*101d251dSrobert   if (args.hasArg(OPT_no_use_colors)) {
194*101d251dSrobert     m_debugger.SetUseColor(false);
195*101d251dSrobert     WithColor::setAutoDetectFunction(disable_color);
196*101d251dSrobert     m_option_data.m_debug_mode = true;
197*101d251dSrobert   }
198*101d251dSrobert 
199061da546Spatrick   if (args.hasArg(OPT_version)) {
200061da546Spatrick     m_option_data.m_print_version = true;
201061da546Spatrick   }
202061da546Spatrick 
203061da546Spatrick   if (args.hasArg(OPT_python_path)) {
204061da546Spatrick     m_option_data.m_print_python_path = true;
205061da546Spatrick   }
206*101d251dSrobert   if (args.hasArg(OPT_print_script_interpreter_info)) {
207*101d251dSrobert     m_option_data.m_print_script_interpreter_info = true;
208*101d251dSrobert   }
209061da546Spatrick 
210061da546Spatrick   if (args.hasArg(OPT_batch)) {
211061da546Spatrick     m_option_data.m_batch = true;
212061da546Spatrick   }
213061da546Spatrick 
214061da546Spatrick   if (auto *arg = args.getLastArg(OPT_core)) {
215061da546Spatrick     auto arg_value = arg->getValue();
216061da546Spatrick     SBFileSpec file(arg_value);
217061da546Spatrick     if (!file.Exists()) {
218061da546Spatrick       error.SetErrorStringWithFormat(
219061da546Spatrick           "file specified in --core (-c) option doesn't exist: '%s'",
220061da546Spatrick           arg_value);
221061da546Spatrick       return error;
222061da546Spatrick     }
223061da546Spatrick     m_option_data.m_core_file = arg_value;
224061da546Spatrick   }
225061da546Spatrick 
226061da546Spatrick   if (args.hasArg(OPT_editor)) {
227061da546Spatrick     m_option_data.m_use_external_editor = true;
228061da546Spatrick   }
229061da546Spatrick 
230061da546Spatrick   if (args.hasArg(OPT_no_lldbinit)) {
231061da546Spatrick     m_debugger.SkipLLDBInitFiles(true);
232061da546Spatrick     m_debugger.SkipAppInitFiles(true);
233061da546Spatrick   }
234061da546Spatrick 
235061da546Spatrick   if (args.hasArg(OPT_local_lldbinit)) {
236061da546Spatrick     lldb::SBDebugger::SetInternalVariable("target.load-cwd-lldbinit", "true",
237061da546Spatrick                                           m_debugger.GetInstanceName());
238061da546Spatrick   }
239061da546Spatrick 
240061da546Spatrick   if (auto *arg = args.getLastArg(OPT_file)) {
241061da546Spatrick     auto arg_value = arg->getValue();
242061da546Spatrick     SBFileSpec file(arg_value);
243061da546Spatrick     if (file.Exists()) {
244061da546Spatrick       m_option_data.m_args.emplace_back(arg_value);
245061da546Spatrick     } else if (file.ResolveExecutableLocation()) {
246061da546Spatrick       char path[PATH_MAX];
247061da546Spatrick       file.GetPath(path, sizeof(path));
248061da546Spatrick       m_option_data.m_args.emplace_back(path);
249061da546Spatrick     } else {
250061da546Spatrick       error.SetErrorStringWithFormat(
251061da546Spatrick           "file specified in --file (-f) option doesn't exist: '%s'",
252061da546Spatrick           arg_value);
253061da546Spatrick       return error;
254061da546Spatrick     }
255061da546Spatrick   }
256061da546Spatrick 
257061da546Spatrick   if (auto *arg = args.getLastArg(OPT_arch)) {
258061da546Spatrick     auto arg_value = arg->getValue();
259061da546Spatrick     if (!lldb::SBDebugger::SetDefaultArchitecture(arg_value)) {
260061da546Spatrick       error.SetErrorStringWithFormat(
261061da546Spatrick           "invalid architecture in the -a or --arch option: '%s'", arg_value);
262061da546Spatrick       return error;
263061da546Spatrick     }
264061da546Spatrick   }
265061da546Spatrick 
266061da546Spatrick   if (auto *arg = args.getLastArg(OPT_script_language)) {
267061da546Spatrick     auto arg_value = arg->getValue();
268061da546Spatrick     m_debugger.SetScriptLanguage(m_debugger.GetScriptingLanguage(arg_value));
269061da546Spatrick   }
270061da546Spatrick 
271061da546Spatrick   if (args.hasArg(OPT_source_quietly)) {
272061da546Spatrick     m_option_data.m_source_quietly = true;
273061da546Spatrick   }
274061da546Spatrick 
275061da546Spatrick   if (auto *arg = args.getLastArg(OPT_attach_name)) {
276061da546Spatrick     auto arg_value = arg->getValue();
277061da546Spatrick     m_option_data.m_process_name = arg_value;
278061da546Spatrick   }
279061da546Spatrick 
280061da546Spatrick   if (args.hasArg(OPT_wait_for)) {
281061da546Spatrick     m_option_data.m_wait_for = true;
282061da546Spatrick   }
283061da546Spatrick 
284061da546Spatrick   if (auto *arg = args.getLastArg(OPT_attach_pid)) {
285061da546Spatrick     auto arg_value = arg->getValue();
286061da546Spatrick     char *remainder;
287061da546Spatrick     m_option_data.m_process_pid = strtol(arg_value, &remainder, 0);
288061da546Spatrick     if (remainder == arg_value || *remainder != '\0') {
289061da546Spatrick       error.SetErrorStringWithFormat(
290061da546Spatrick           "Could not convert process PID: \"%s\" into a pid.", arg_value);
291061da546Spatrick       return error;
292061da546Spatrick     }
293061da546Spatrick   }
294061da546Spatrick 
295061da546Spatrick   if (auto *arg = args.getLastArg(OPT_repl_language)) {
296061da546Spatrick     auto arg_value = arg->getValue();
297061da546Spatrick     m_option_data.m_repl_lang =
298061da546Spatrick         SBLanguageRuntime::GetLanguageTypeFromString(arg_value);
299061da546Spatrick     if (m_option_data.m_repl_lang == eLanguageTypeUnknown) {
300061da546Spatrick       error.SetErrorStringWithFormat("Unrecognized language name: \"%s\"",
301061da546Spatrick                                      arg_value);
302061da546Spatrick       return error;
303061da546Spatrick     }
304*101d251dSrobert     m_debugger.SetREPLLanguage(m_option_data.m_repl_lang);
305061da546Spatrick   }
306061da546Spatrick 
307061da546Spatrick   if (args.hasArg(OPT_repl)) {
308061da546Spatrick     m_option_data.m_repl = true;
309061da546Spatrick   }
310061da546Spatrick 
311061da546Spatrick   if (auto *arg = args.getLastArg(OPT_repl_)) {
312061da546Spatrick     m_option_data.m_repl = true;
313061da546Spatrick     if (auto arg_value = arg->getValue())
314061da546Spatrick       m_option_data.m_repl_options = arg_value;
315061da546Spatrick   }
316061da546Spatrick 
317061da546Spatrick   // We need to process the options below together as their relative order
318061da546Spatrick   // matters.
319061da546Spatrick   for (auto *arg : args.filtered(OPT_source_on_crash, OPT_one_line_on_crash,
320061da546Spatrick                                  OPT_source, OPT_source_before_file,
321061da546Spatrick                                  OPT_one_line, OPT_one_line_before_file)) {
322061da546Spatrick     auto arg_value = arg->getValue();
323061da546Spatrick     if (arg->getOption().matches(OPT_source_on_crash)) {
324061da546Spatrick       m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash,
325061da546Spatrick                                       true, error);
326061da546Spatrick       if (error.Fail())
327061da546Spatrick         return error;
328061da546Spatrick     }
329061da546Spatrick 
330061da546Spatrick     if (arg->getOption().matches(OPT_one_line_on_crash)) {
331061da546Spatrick       m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash,
332061da546Spatrick                                       false, error);
333061da546Spatrick       if (error.Fail())
334061da546Spatrick         return error;
335061da546Spatrick     }
336061da546Spatrick 
337061da546Spatrick     if (arg->getOption().matches(OPT_source)) {
338061da546Spatrick       m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile,
339061da546Spatrick                                       true, error);
340061da546Spatrick       if (error.Fail())
341061da546Spatrick         return error;
342061da546Spatrick     }
343061da546Spatrick 
344061da546Spatrick     if (arg->getOption().matches(OPT_source_before_file)) {
345061da546Spatrick       m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile,
346061da546Spatrick                                       true, error);
347061da546Spatrick       if (error.Fail())
348061da546Spatrick         return error;
349061da546Spatrick     }
350061da546Spatrick 
351061da546Spatrick     if (arg->getOption().matches(OPT_one_line)) {
352061da546Spatrick       m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile,
353061da546Spatrick                                       false, error);
354061da546Spatrick       if (error.Fail())
355061da546Spatrick         return error;
356061da546Spatrick     }
357061da546Spatrick 
358061da546Spatrick     if (arg->getOption().matches(OPT_one_line_before_file)) {
359061da546Spatrick       m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile,
360061da546Spatrick                                       false, error);
361061da546Spatrick       if (error.Fail())
362061da546Spatrick         return error;
363061da546Spatrick     }
364061da546Spatrick   }
365061da546Spatrick 
366061da546Spatrick   if (m_option_data.m_process_name.empty() &&
367061da546Spatrick       m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID) {
368061da546Spatrick 
369dda28197Spatrick     for (auto *arg : args.filtered(OPT_INPUT))
370061da546Spatrick       m_option_data.m_args.push_back(arg->getAsString((args)));
371061da546Spatrick 
372061da546Spatrick     // Any argument following -- is an argument for the inferior.
373061da546Spatrick     if (auto *arg = args.getLastArgNoClaim(OPT_REM)) {
374061da546Spatrick       for (auto value : arg->getValues())
375061da546Spatrick         m_option_data.m_args.emplace_back(value);
376061da546Spatrick     }
377061da546Spatrick   } else if (args.getLastArgNoClaim() != nullptr) {
378061da546Spatrick     WithColor::warning() << "program arguments are ignored when attaching.\n";
379061da546Spatrick   }
380061da546Spatrick 
381061da546Spatrick   if (m_option_data.m_print_version) {
382061da546Spatrick     llvm::outs() << lldb::SBDebugger::GetVersionString() << '\n';
383061da546Spatrick     exiting = true;
384061da546Spatrick     return error;
385061da546Spatrick   }
386061da546Spatrick 
387061da546Spatrick   if (m_option_data.m_print_python_path) {
388061da546Spatrick     SBFileSpec python_file_spec = SBHostOS::GetLLDBPythonPath();
389061da546Spatrick     if (python_file_spec.IsValid()) {
390061da546Spatrick       char python_path[PATH_MAX];
391061da546Spatrick       size_t num_chars = python_file_spec.GetPath(python_path, PATH_MAX);
392061da546Spatrick       if (num_chars < PATH_MAX) {
393061da546Spatrick         llvm::outs() << python_path << '\n';
394061da546Spatrick       } else
395061da546Spatrick         llvm::outs() << "<PATH TOO LONG>\n";
396061da546Spatrick     } else
397061da546Spatrick       llvm::outs() << "<COULD NOT FIND PATH>\n";
398061da546Spatrick     exiting = true;
399061da546Spatrick     return error;
400061da546Spatrick   }
401061da546Spatrick 
402*101d251dSrobert   if (m_option_data.m_print_script_interpreter_info) {
403*101d251dSrobert     SBStructuredData info =
404*101d251dSrobert         m_debugger.GetScriptInterpreterInfo(m_debugger.GetScriptLanguage());
405*101d251dSrobert     if (!info) {
406*101d251dSrobert       error.SetErrorString("no script interpreter.");
407*101d251dSrobert     } else {
408*101d251dSrobert       SBStream stream;
409*101d251dSrobert       error = info.GetAsJSON(stream);
410*101d251dSrobert       if (error.Success()) {
411*101d251dSrobert         llvm::outs() << stream.GetData() << '\n';
412*101d251dSrobert       }
413*101d251dSrobert     }
414*101d251dSrobert     exiting = true;
415061da546Spatrick     return error;
416061da546Spatrick   }
417061da546Spatrick 
418*101d251dSrobert   return error;
419061da546Spatrick }
420061da546Spatrick 
EscapeString(std::string arg)421061da546Spatrick std::string EscapeString(std::string arg) {
422061da546Spatrick   std::string::size_type pos = 0;
423061da546Spatrick   while ((pos = arg.find_first_of("\"\\", pos)) != std::string::npos) {
424061da546Spatrick     arg.insert(pos, 1, '\\');
425061da546Spatrick     pos += 2;
426061da546Spatrick   }
427061da546Spatrick   return '"' + arg + '"';
428061da546Spatrick }
429061da546Spatrick 
MainLoop()430061da546Spatrick int Driver::MainLoop() {
431061da546Spatrick   if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0) {
432061da546Spatrick     g_old_stdin_termios_is_valid = true;
433061da546Spatrick     atexit(reset_stdin_termios);
434061da546Spatrick   }
435061da546Spatrick 
436061da546Spatrick #ifndef _MSC_VER
437061da546Spatrick   // Disabling stdin buffering with MSVC's 2015 CRT exposes a bug in fgets
438061da546Spatrick   // which causes it to miss newlines depending on whether there have been an
439061da546Spatrick   // odd or even number of characters.  Bug has been reported to MS via Connect.
440061da546Spatrick   ::setbuf(stdin, nullptr);
441061da546Spatrick #endif
442061da546Spatrick   ::setbuf(stdout, nullptr);
443061da546Spatrick 
444061da546Spatrick   m_debugger.SetErrorFileHandle(stderr, false);
445061da546Spatrick   m_debugger.SetOutputFileHandle(stdout, false);
446061da546Spatrick   // Don't take ownership of STDIN yet...
447061da546Spatrick   m_debugger.SetInputFileHandle(stdin, false);
448061da546Spatrick 
449061da546Spatrick   m_debugger.SetUseExternalEditor(m_option_data.m_use_external_editor);
450061da546Spatrick 
451061da546Spatrick   struct winsize window_size;
452061da546Spatrick   if ((isatty(STDIN_FILENO) != 0) &&
453061da546Spatrick       ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {
454061da546Spatrick     if (window_size.ws_col > 0)
455061da546Spatrick       m_debugger.SetTerminalWidth(window_size.ws_col);
456061da546Spatrick   }
457061da546Spatrick 
458061da546Spatrick   SBCommandInterpreter sb_interpreter = m_debugger.GetCommandInterpreter();
459061da546Spatrick 
460*101d251dSrobert   // Process lldbinit files before handling any options from the command line.
461061da546Spatrick   SBCommandReturnObject result;
462*101d251dSrobert   sb_interpreter.SourceInitFileInGlobalDirectory(result);
463*101d251dSrobert   if (m_option_data.m_debug_mode) {
464*101d251dSrobert     result.PutError(m_debugger.GetErrorFile());
465*101d251dSrobert     result.PutOutput(m_debugger.GetOutputFile());
466*101d251dSrobert   }
467*101d251dSrobert 
468a0747c9fSpatrick   sb_interpreter.SourceInitFileInHomeDirectory(result, m_option_data.m_repl);
469061da546Spatrick   if (m_option_data.m_debug_mode) {
470061da546Spatrick     result.PutError(m_debugger.GetErrorFile());
471061da546Spatrick     result.PutOutput(m_debugger.GetOutputFile());
472061da546Spatrick   }
473061da546Spatrick 
474061da546Spatrick   // Source the local .lldbinit file if it exists and we're allowed to source.
475061da546Spatrick   // Here we want to always print the return object because it contains the
476061da546Spatrick   // warning and instructions to load local lldbinit files.
477061da546Spatrick   sb_interpreter.SourceInitFileInCurrentWorkingDirectory(result);
478061da546Spatrick   result.PutError(m_debugger.GetErrorFile());
479061da546Spatrick   result.PutOutput(m_debugger.GetOutputFile());
480061da546Spatrick 
481061da546Spatrick   // We allow the user to specify an exit code when calling quit which we will
482061da546Spatrick   // return when exiting.
483061da546Spatrick   m_debugger.GetCommandInterpreter().AllowExitCodeOnQuit(true);
484061da546Spatrick 
485061da546Spatrick   // Now we handle options we got from the command line
486061da546Spatrick   SBStream commands_stream;
487061da546Spatrick 
488061da546Spatrick   // First source in the commands specified to be run before the file arguments
489061da546Spatrick   // are processed.
490061da546Spatrick   WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream);
491061da546Spatrick 
492061da546Spatrick   // If we're not in --repl mode, add the commands to process the file
493061da546Spatrick   // arguments, and the commands specified to run afterwards.
494061da546Spatrick   if (!m_option_data.m_repl) {
495061da546Spatrick     const size_t num_args = m_option_data.m_args.size();
496061da546Spatrick     if (num_args > 0) {
497061da546Spatrick       char arch_name[64];
498061da546Spatrick       if (lldb::SBDebugger::GetDefaultArchitecture(arch_name,
499061da546Spatrick                                                    sizeof(arch_name)))
500061da546Spatrick         commands_stream.Printf("target create --arch=%s %s", arch_name,
501061da546Spatrick                                EscapeString(m_option_data.m_args[0]).c_str());
502061da546Spatrick       else
503061da546Spatrick         commands_stream.Printf("target create %s",
504061da546Spatrick                                EscapeString(m_option_data.m_args[0]).c_str());
505061da546Spatrick 
506061da546Spatrick       if (!m_option_data.m_core_file.empty()) {
507061da546Spatrick         commands_stream.Printf(" --core %s",
508061da546Spatrick                                EscapeString(m_option_data.m_core_file).c_str());
509061da546Spatrick       }
510061da546Spatrick       commands_stream.Printf("\n");
511061da546Spatrick 
512061da546Spatrick       if (num_args > 1) {
513061da546Spatrick         commands_stream.Printf("settings set -- target.run-args ");
514061da546Spatrick         for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx)
515061da546Spatrick           commands_stream.Printf(
516061da546Spatrick               " %s", EscapeString(m_option_data.m_args[arg_idx]).c_str());
517061da546Spatrick         commands_stream.Printf("\n");
518061da546Spatrick       }
519061da546Spatrick     } else if (!m_option_data.m_core_file.empty()) {
520061da546Spatrick       commands_stream.Printf("target create --core %s\n",
521061da546Spatrick                              EscapeString(m_option_data.m_core_file).c_str());
522061da546Spatrick     } else if (!m_option_data.m_process_name.empty()) {
523061da546Spatrick       commands_stream.Printf(
524061da546Spatrick           "process attach --name %s",
525061da546Spatrick           EscapeString(m_option_data.m_process_name).c_str());
526061da546Spatrick 
527061da546Spatrick       if (m_option_data.m_wait_for)
528061da546Spatrick         commands_stream.Printf(" --waitfor");
529061da546Spatrick 
530061da546Spatrick       commands_stream.Printf("\n");
531061da546Spatrick 
532061da546Spatrick     } else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid) {
533061da546Spatrick       commands_stream.Printf("process attach --pid %" PRIu64 "\n",
534061da546Spatrick                              m_option_data.m_process_pid);
535061da546Spatrick     }
536061da546Spatrick 
537061da546Spatrick     WriteCommandsForSourcing(eCommandPlacementAfterFile, commands_stream);
538061da546Spatrick   } else if (!m_option_data.m_after_file_commands.empty()) {
539061da546Spatrick     // We're in repl mode and after-file-load commands were specified.
540061da546Spatrick     WithColor::warning() << "commands specified to run after file load (via -o "
541061da546Spatrick                             "or -s) are ignored in REPL mode.\n";
542061da546Spatrick   }
543061da546Spatrick 
544061da546Spatrick   if (m_option_data.m_debug_mode) {
545061da546Spatrick     result.PutError(m_debugger.GetErrorFile());
546061da546Spatrick     result.PutOutput(m_debugger.GetOutputFile());
547061da546Spatrick   }
548061da546Spatrick 
549061da546Spatrick   const bool handle_events = true;
550061da546Spatrick   const bool spawn_thread = false;
551061da546Spatrick 
552061da546Spatrick   // Check if we have any data in the commands stream, and if so, save it to a
553061da546Spatrick   // temp file
554061da546Spatrick   // so we can then run the command interpreter using the file contents.
555dda28197Spatrick   bool go_interactive = true;
556*101d251dSrobert   if ((commands_stream.GetData() != nullptr) &&
557*101d251dSrobert       (commands_stream.GetSize() != 0u)) {
558*101d251dSrobert     SBError error = m_debugger.SetInputString(commands_stream.GetData());
559*101d251dSrobert     if (error.Fail()) {
560*101d251dSrobert       WithColor::error() << error.GetCString() << '\n';
561a0747c9fSpatrick       return 1;
562dda28197Spatrick     }
563dda28197Spatrick 
564dda28197Spatrick     // Set the debugger into Sync mode when running the command file. Otherwise
565dda28197Spatrick     // command files that run the target won't run in a sensible way.
566061da546Spatrick     bool old_async = m_debugger.GetAsync();
567061da546Spatrick     m_debugger.SetAsync(false);
568061da546Spatrick 
569061da546Spatrick     SBCommandInterpreterRunOptions options;
570dda28197Spatrick     options.SetAutoHandleEvents(true);
571dda28197Spatrick     options.SetSpawnThread(false);
572061da546Spatrick     options.SetStopOnError(true);
573dda28197Spatrick     options.SetStopOnCrash(m_option_data.m_batch);
574*101d251dSrobert     options.SetEchoCommands(!m_option_data.m_source_quietly);
575061da546Spatrick 
576dda28197Spatrick     SBCommandInterpreterRunResult results =
577dda28197Spatrick         m_debugger.RunCommandInterpreter(options);
578dda28197Spatrick     if (results.GetResult() == lldb::eCommandInterpreterResultQuitRequested)
579dda28197Spatrick       go_interactive = false;
580dda28197Spatrick     if (m_option_data.m_batch &&
581dda28197Spatrick         results.GetResult() != lldb::eCommandInterpreterResultInferiorCrash)
582dda28197Spatrick       go_interactive = false;
583061da546Spatrick 
584dda28197Spatrick     // When running in batch mode and stopped because of an error, exit with a
585dda28197Spatrick     // non-zero exit status.
586dda28197Spatrick     if (m_option_data.m_batch &&
587dda28197Spatrick         results.GetResult() == lldb::eCommandInterpreterResultCommandError)
588a0747c9fSpatrick       return 1;
589dda28197Spatrick 
590dda28197Spatrick     if (m_option_data.m_batch &&
591dda28197Spatrick         results.GetResult() == lldb::eCommandInterpreterResultInferiorCrash &&
592061da546Spatrick         !m_option_data.m_after_crash_commands.empty()) {
593061da546Spatrick       SBStream crash_commands_stream;
594061da546Spatrick       WriteCommandsForSourcing(eCommandPlacementAfterCrash,
595061da546Spatrick                                crash_commands_stream);
596*101d251dSrobert       SBError error =
597*101d251dSrobert           m_debugger.SetInputString(crash_commands_stream.GetData());
598*101d251dSrobert       if (error.Success()) {
599dda28197Spatrick         SBCommandInterpreterRunResult local_results =
600dda28197Spatrick             m_debugger.RunCommandInterpreter(options);
601dda28197Spatrick         if (local_results.GetResult() ==
602dda28197Spatrick             lldb::eCommandInterpreterResultQuitRequested)
603dda28197Spatrick           go_interactive = false;
604061da546Spatrick 
605dda28197Spatrick         // When running in batch mode and an error occurred while sourcing
606dda28197Spatrick         // the crash commands, exit with a non-zero exit status.
607dda28197Spatrick         if (m_option_data.m_batch &&
608dda28197Spatrick             local_results.GetResult() ==
609dda28197Spatrick                 lldb::eCommandInterpreterResultCommandError)
610a0747c9fSpatrick           return 1;
611061da546Spatrick       }
612061da546Spatrick     }
613dda28197Spatrick     m_debugger.SetAsync(old_async);
614dda28197Spatrick   }
615061da546Spatrick 
616dda28197Spatrick   // Now set the input file handle to STDIN and run the command interpreter
617dda28197Spatrick   // again in interactive mode or repl mode and let the debugger take ownership
618dda28197Spatrick   // of stdin.
619061da546Spatrick   if (go_interactive) {
620061da546Spatrick     m_debugger.SetInputFileHandle(stdin, true);
621061da546Spatrick 
622061da546Spatrick     if (m_option_data.m_repl) {
623061da546Spatrick       const char *repl_options = nullptr;
624061da546Spatrick       if (!m_option_data.m_repl_options.empty())
625061da546Spatrick         repl_options = m_option_data.m_repl_options.c_str();
626061da546Spatrick       SBError error(
627061da546Spatrick           m_debugger.RunREPL(m_option_data.m_repl_lang, repl_options));
628061da546Spatrick       if (error.Fail()) {
629061da546Spatrick         const char *error_cstr = error.GetCString();
630061da546Spatrick         if ((error_cstr != nullptr) && (error_cstr[0] != 0))
631061da546Spatrick           WithColor::error() << error_cstr << '\n';
632061da546Spatrick         else
633061da546Spatrick           WithColor::error() << error.GetError() << '\n';
634061da546Spatrick       }
635061da546Spatrick     } else {
636061da546Spatrick       m_debugger.RunCommandInterpreter(handle_events, spawn_thread);
637061da546Spatrick     }
638061da546Spatrick   }
639061da546Spatrick 
640061da546Spatrick   reset_stdin_termios();
641061da546Spatrick   fclose(stdin);
642061da546Spatrick 
643a0747c9fSpatrick   return sb_interpreter.GetQuitStatus();
644061da546Spatrick }
645061da546Spatrick 
ResizeWindow(unsigned short col)646061da546Spatrick void Driver::ResizeWindow(unsigned short col) {
647061da546Spatrick   GetDebugger().SetTerminalWidth(col);
648061da546Spatrick }
649061da546Spatrick 
sigwinch_handler(int signo)650061da546Spatrick void sigwinch_handler(int signo) {
651061da546Spatrick   struct winsize window_size;
652061da546Spatrick   if ((isatty(STDIN_FILENO) != 0) &&
653061da546Spatrick       ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {
654061da546Spatrick     if ((window_size.ws_col > 0) && g_driver != nullptr) {
655061da546Spatrick       g_driver->ResizeWindow(window_size.ws_col);
656061da546Spatrick     }
657061da546Spatrick   }
658061da546Spatrick }
659061da546Spatrick 
sigint_handler(int signo)660061da546Spatrick void sigint_handler(int signo) {
661061da546Spatrick #ifdef _WIN32 // Restore handler as it is not persistent on Windows
662061da546Spatrick   signal(SIGINT, sigint_handler);
663061da546Spatrick #endif
664061da546Spatrick   static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT;
665061da546Spatrick   if (g_driver != nullptr) {
666061da546Spatrick     if (!g_interrupt_sent.test_and_set()) {
667061da546Spatrick       g_driver->GetDebugger().DispatchInputInterrupt();
668061da546Spatrick       g_interrupt_sent.clear();
669061da546Spatrick       return;
670061da546Spatrick     }
671061da546Spatrick   }
672061da546Spatrick 
673061da546Spatrick   _exit(signo);
674061da546Spatrick }
675061da546Spatrick 
676*101d251dSrobert #ifndef _WIN32
sigtstp_handler(int signo)677*101d251dSrobert static void sigtstp_handler(int signo) {
678061da546Spatrick   if (g_driver != nullptr)
679061da546Spatrick     g_driver->GetDebugger().SaveInputTerminalState();
680061da546Spatrick 
681*101d251dSrobert   // Unblock the signal and remove our handler.
682*101d251dSrobert   sigset_t set;
683*101d251dSrobert   sigemptyset(&set);
684*101d251dSrobert   sigaddset(&set, signo);
685*101d251dSrobert   pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
686061da546Spatrick   signal(signo, SIG_DFL);
687061da546Spatrick 
688*101d251dSrobert   // Now re-raise the signal. We will immediately suspend...
689*101d251dSrobert   raise(signo);
690*101d251dSrobert   // ... and resume after a SIGCONT.
691*101d251dSrobert 
692*101d251dSrobert   // Now undo the modifications.
693*101d251dSrobert   pthread_sigmask(SIG_BLOCK, &set, nullptr);
694*101d251dSrobert   signal(signo, sigtstp_handler);
695*101d251dSrobert 
696061da546Spatrick   if (g_driver != nullptr)
697061da546Spatrick     g_driver->GetDebugger().RestoreInputTerminalState();
698061da546Spatrick }
699*101d251dSrobert #endif
700061da546Spatrick 
printHelp(LLDBOptTable & table,llvm::StringRef tool_name)701061da546Spatrick static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) {
702061da546Spatrick   std::string usage_str = tool_name.str() + " [options]";
703a0747c9fSpatrick   table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB", false);
704061da546Spatrick 
705061da546Spatrick   std::string examples = R"___(
706061da546Spatrick EXAMPLES:
707061da546Spatrick   The debugger can be started in several modes.
708061da546Spatrick 
709061da546Spatrick   Passing an executable as a positional argument prepares lldb to debug the
710dda28197Spatrick   given executable. To disambiguate between arguments passed to lldb and
711dda28197Spatrick   arguments passed to the debugged executable, arguments starting with a - must
712dda28197Spatrick   be passed after --.
713061da546Spatrick 
714b3094f69Sjsg     lldb --arch x86_64 /path/to/program program argument -- --arch armv7
715dda28197Spatrick 
716dda28197Spatrick   For convenience, passing the executable after -- is also supported.
717dda28197Spatrick 
718b3094f69Sjsg     lldb --arch x86_64 -- /path/to/program program argument --arch armv7
719061da546Spatrick 
720061da546Spatrick   Passing one of the attach options causes lldb to immediately attach to the
721061da546Spatrick   given process.
722061da546Spatrick 
723061da546Spatrick     lldb -p <pid>
724061da546Spatrick     lldb -n <process-name>
725061da546Spatrick 
726061da546Spatrick   Passing --repl starts lldb in REPL mode.
727061da546Spatrick 
728061da546Spatrick     lldb -r
729061da546Spatrick 
730061da546Spatrick   Passing --core causes lldb to debug the core file.
731061da546Spatrick 
732061da546Spatrick     lldb -c /path/to/core
733061da546Spatrick 
734061da546Spatrick   Command options can be combined with these modes and cause lldb to run the
735061da546Spatrick   specified commands before or after events, like loading the file or crashing,
736061da546Spatrick   in the order provided on the command line.
737061da546Spatrick 
738061da546Spatrick     lldb -O 'settings set stop-disassembly-count 20' -o 'run' -o 'bt'
739061da546Spatrick     lldb -S /source/before/file -s /source/after/file
740061da546Spatrick     lldb -K /source/before/crash -k /source/after/crash
741061da546Spatrick 
742061da546Spatrick   Note: In REPL mode no file is loaded, so commands specified to run after
743061da546Spatrick   loading the file (via -o or -s) will be ignored.)___";
744061da546Spatrick   llvm::outs() << examples << '\n';
745061da546Spatrick }
746061da546Spatrick 
main(int argc,char const * argv[])747061da546Spatrick int main(int argc, char const *argv[]) {
748a0747c9fSpatrick   // Editline uses for example iswprint which is dependent on LC_CTYPE.
749a0747c9fSpatrick   std::setlocale(LC_ALL, "");
750a0747c9fSpatrick   std::setlocale(LC_CTYPE, "");
751a0747c9fSpatrick 
752061da546Spatrick   // Setup LLVM signal handlers and make sure we call llvm_shutdown() on
753061da546Spatrick   // destruction.
754061da546Spatrick   llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
755061da546Spatrick 
756061da546Spatrick   // Parse arguments.
757061da546Spatrick   LLDBOptTable T;
758a0747c9fSpatrick   unsigned MissingArgIndex;
759a0747c9fSpatrick   unsigned MissingArgCount;
760*101d251dSrobert   ArrayRef<const char *> arg_arr = ArrayRef(argv + 1, argc - 1);
761a0747c9fSpatrick   opt::InputArgList input_args =
762a0747c9fSpatrick       T.ParseArgs(arg_arr, MissingArgIndex, MissingArgCount);
763dda28197Spatrick   llvm::StringRef argv0 = llvm::sys::path::filename(argv[0]);
764061da546Spatrick 
765061da546Spatrick   if (input_args.hasArg(OPT_help)) {
766dda28197Spatrick     printHelp(T, argv0);
767061da546Spatrick     return 0;
768061da546Spatrick   }
769061da546Spatrick 
770a0747c9fSpatrick   // Check for missing argument error.
771a0747c9fSpatrick   if (MissingArgCount) {
772a0747c9fSpatrick     WithColor::error() << "argument to '"
773a0747c9fSpatrick                        << input_args.getArgString(MissingArgIndex)
774a0747c9fSpatrick                        << "' is missing\n";
775a0747c9fSpatrick   }
776dda28197Spatrick   // Error out on unknown options.
777dda28197Spatrick   if (input_args.hasArg(OPT_UNKNOWN)) {
778061da546Spatrick     for (auto *arg : input_args.filtered(OPT_UNKNOWN)) {
779dda28197Spatrick       WithColor::error() << "unknown option: " << arg->getSpelling() << '\n';
780dda28197Spatrick     }
781a0747c9fSpatrick   }
782a0747c9fSpatrick   if (MissingArgCount || input_args.hasArg(OPT_UNKNOWN)) {
783dda28197Spatrick     llvm::errs() << "Use '" << argv0
784dda28197Spatrick                  << " --help' for a complete list of options.\n";
785dda28197Spatrick     return 1;
786061da546Spatrick   }
787061da546Spatrick 
788061da546Spatrick   SBError error = SBDebugger::InitializeWithErrorHandling();
789061da546Spatrick   if (error.Fail()) {
790061da546Spatrick     WithColor::error() << "initialization failed: " << error.GetCString()
791061da546Spatrick                        << '\n';
792061da546Spatrick     return 1;
793061da546Spatrick   }
794*101d251dSrobert 
795*101d251dSrobert   // Setup LLDB signal handlers once the debugger has been initialized.
796*101d251dSrobert   SBDebugger::PrintDiagnosticsOnError();
797*101d251dSrobert 
798061da546Spatrick   SBHostOS::ThreadCreated("<lldb.driver.main-thread>");
799061da546Spatrick 
800061da546Spatrick   signal(SIGINT, sigint_handler);
801*101d251dSrobert #if !defined(_WIN32)
802061da546Spatrick   signal(SIGPIPE, SIG_IGN);
803061da546Spatrick   signal(SIGWINCH, sigwinch_handler);
804061da546Spatrick   signal(SIGTSTP, sigtstp_handler);
805061da546Spatrick #endif
806061da546Spatrick 
807061da546Spatrick   int exit_code = 0;
808061da546Spatrick   // Create a scope for driver so that the driver object will destroy itself
809061da546Spatrick   // before SBDebugger::Terminate() is called.
810061da546Spatrick   {
811061da546Spatrick     Driver driver;
812061da546Spatrick 
813061da546Spatrick     bool exiting = false;
814061da546Spatrick     SBError error(driver.ProcessArgs(input_args, exiting));
815061da546Spatrick     if (error.Fail()) {
816061da546Spatrick       exit_code = 1;
817061da546Spatrick       if (const char *error_cstr = error.GetCString())
818061da546Spatrick         WithColor::error() << error_cstr << '\n';
819061da546Spatrick     } else if (!exiting) {
820061da546Spatrick       exit_code = driver.MainLoop();
821061da546Spatrick     }
822061da546Spatrick   }
823061da546Spatrick 
824061da546Spatrick   SBDebugger::Terminate();
825061da546Spatrick   return exit_code;
826061da546Spatrick }
827