xref: /freebsd/contrib/llvm-project/lldb/tools/driver/Driver.cpp (revision 5abaf0866445a61c11665fffc148ecd13a7bb9ac)
1 //===-- Driver.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 "Driver.h"
10 
11 #include "lldb/API/SBCommandInterpreter.h"
12 #include "lldb/API/SBCommandInterpreterRunOptions.h"
13 #include "lldb/API/SBCommandReturnObject.h"
14 #include "lldb/API/SBDebugger.h"
15 #include "lldb/API/SBFile.h"
16 #include "lldb/API/SBHostOS.h"
17 #include "lldb/API/SBLanguageRuntime.h"
18 #include "lldb/API/SBReproducer.h"
19 #include "lldb/API/SBStream.h"
20 #include "lldb/API/SBStringList.h"
21 #include "lldb/API/SBStructuredData.h"
22 
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/Support/Format.h"
25 #include "llvm/Support/InitLLVM.h"
26 #include "llvm/Support/Path.h"
27 #include "llvm/Support/Signals.h"
28 #include "llvm/Support/WithColor.h"
29 #include "llvm/Support/raw_ostream.h"
30 
31 #include <algorithm>
32 #include <atomic>
33 #include <bitset>
34 #include <clocale>
35 #include <csignal>
36 #include <string>
37 #include <thread>
38 #include <utility>
39 
40 #include <climits>
41 #include <cstdio>
42 #include <cstdlib>
43 #include <cstring>
44 #include <fcntl.h>
45 
46 #if !defined(__APPLE__)
47 #include "llvm/Support/DataTypes.h"
48 #endif
49 
50 using namespace lldb;
51 using namespace llvm;
52 
53 namespace {
54 enum ID {
55   OPT_INVALID = 0, // This is not an option ID.
56 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
57                HELPTEXT, METAVAR, VALUES)                                      \
58   OPT_##ID,
59 #include "Options.inc"
60 #undef OPTION
61 };
62 
63 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
64 #include "Options.inc"
65 #undef PREFIX
66 
67 const opt::OptTable::Info InfoTable[] = {
68 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
69                HELPTEXT, METAVAR, VALUES)                                      \
70   {                                                                            \
71       PREFIX,      NAME,      HELPTEXT,                                        \
72       METAVAR,     OPT_##ID,  opt::Option::KIND##Class,                        \
73       PARAM,       FLAGS,     OPT_##GROUP,                                     \
74       OPT_##ALIAS, ALIASARGS, VALUES},
75 #include "Options.inc"
76 #undef OPTION
77 };
78 
79 class LLDBOptTable : public opt::OptTable {
80 public:
81   LLDBOptTable() : OptTable(InfoTable) {}
82 };
83 } // namespace
84 
85 static void reset_stdin_termios();
86 static bool g_old_stdin_termios_is_valid = false;
87 static struct termios g_old_stdin_termios;
88 
89 static bool disable_color(const raw_ostream &OS) { return false; }
90 
91 static Driver *g_driver = nullptr;
92 
93 // In the Driver::MainLoop, we change the terminal settings.  This function is
94 // added as an atexit handler to make sure we clean them up.
95 static void reset_stdin_termios() {
96   if (g_old_stdin_termios_is_valid) {
97     g_old_stdin_termios_is_valid = false;
98     ::tcsetattr(STDIN_FILENO, TCSANOW, &g_old_stdin_termios);
99   }
100 }
101 
102 Driver::Driver()
103     : SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(false)) {
104   // We want to be able to handle CTRL+D in the terminal to have it terminate
105   // certain input
106   m_debugger.SetCloseInputOnEOF(false);
107   g_driver = this;
108 }
109 
110 Driver::~Driver() {
111   SBDebugger::Destroy(m_debugger);
112   g_driver = nullptr;
113 }
114 
115 void Driver::OptionData::AddInitialCommand(std::string command,
116                                            CommandPlacement placement,
117                                            bool is_file, SBError &error) {
118   std::vector<InitialCmdEntry> *command_set;
119   switch (placement) {
120   case eCommandPlacementBeforeFile:
121     command_set = &(m_initial_commands);
122     break;
123   case eCommandPlacementAfterFile:
124     command_set = &(m_after_file_commands);
125     break;
126   case eCommandPlacementAfterCrash:
127     command_set = &(m_after_crash_commands);
128     break;
129   }
130 
131   if (is_file) {
132     SBFileSpec file(command.c_str());
133     if (file.Exists())
134       command_set->push_back(InitialCmdEntry(command, is_file));
135     else if (file.ResolveExecutableLocation()) {
136       char final_path[PATH_MAX];
137       file.GetPath(final_path, sizeof(final_path));
138       command_set->push_back(InitialCmdEntry(final_path, is_file));
139     } else
140       error.SetErrorStringWithFormat(
141           "file specified in --source (-s) option doesn't exist: '%s'",
142           command.c_str());
143   } else
144     command_set->push_back(InitialCmdEntry(command, is_file));
145 }
146 
147 void Driver::WriteCommandsForSourcing(CommandPlacement placement,
148                                       SBStream &strm) {
149   std::vector<OptionData::InitialCmdEntry> *command_set;
150   switch (placement) {
151   case eCommandPlacementBeforeFile:
152     command_set = &m_option_data.m_initial_commands;
153     break;
154   case eCommandPlacementAfterFile:
155     command_set = &m_option_data.m_after_file_commands;
156     break;
157   case eCommandPlacementAfterCrash:
158     command_set = &m_option_data.m_after_crash_commands;
159     break;
160   }
161 
162   for (const auto &command_entry : *command_set) {
163     const char *command = command_entry.contents.c_str();
164     if (command_entry.is_file) {
165       bool source_quietly =
166           m_option_data.m_source_quietly || command_entry.source_quietly;
167       strm.Printf("command source -s %i '%s'\n",
168                   static_cast<int>(source_quietly), command);
169     } else
170       strm.Printf("%s\n", command);
171   }
172 }
173 
174 // Check the arguments that were passed to this program to make sure they are
175 // valid and to get their argument values (if any).  Return a boolean value
176 // indicating whether or not to start up the full debugger (i.e. the Command
177 // Interpreter) or not.  Return FALSE if the arguments were invalid OR if the
178 // user only wanted help or version information.
179 SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) {
180   SBError error;
181 
182   // This is kind of a pain, but since we make the debugger in the Driver's
183   // constructor, we can't know at that point whether we should read in init
184   // files yet.  So we don't read them in in the Driver constructor, then set
185   // the flags back to "read them in" here, and then if we see the "-n" flag,
186   // we'll turn it off again.  Finally we have to read them in by hand later in
187   // the main loop.
188   m_debugger.SkipLLDBInitFiles(false);
189   m_debugger.SkipAppInitFiles(false);
190 
191   if (args.hasArg(OPT_no_use_colors)) {
192     m_debugger.SetUseColor(false);
193     WithColor::setAutoDetectFunction(disable_color);
194     m_option_data.m_debug_mode = true;
195   }
196 
197   if (args.hasArg(OPT_version)) {
198     m_option_data.m_print_version = true;
199   }
200 
201   if (args.hasArg(OPT_python_path)) {
202     m_option_data.m_print_python_path = true;
203   }
204   if (args.hasArg(OPT_print_script_interpreter_info)) {
205     m_option_data.m_print_script_interpreter_info = true;
206   }
207 
208   if (args.hasArg(OPT_batch)) {
209     m_option_data.m_batch = true;
210   }
211 
212   if (auto *arg = args.getLastArg(OPT_core)) {
213     auto arg_value = arg->getValue();
214     SBFileSpec file(arg_value);
215     if (!file.Exists()) {
216       error.SetErrorStringWithFormat(
217           "file specified in --core (-c) option doesn't exist: '%s'",
218           arg_value);
219       return error;
220     }
221     m_option_data.m_core_file = arg_value;
222   }
223 
224   if (args.hasArg(OPT_editor)) {
225     m_option_data.m_use_external_editor = true;
226   }
227 
228   if (args.hasArg(OPT_no_lldbinit)) {
229     m_debugger.SkipLLDBInitFiles(true);
230     m_debugger.SkipAppInitFiles(true);
231   }
232 
233   if (args.hasArg(OPT_local_lldbinit)) {
234     lldb::SBDebugger::SetInternalVariable("target.load-cwd-lldbinit", "true",
235                                           m_debugger.GetInstanceName());
236   }
237 
238   if (auto *arg = args.getLastArg(OPT_file)) {
239     auto arg_value = arg->getValue();
240     SBFileSpec file(arg_value);
241     if (file.Exists()) {
242       m_option_data.m_args.emplace_back(arg_value);
243     } else if (file.ResolveExecutableLocation()) {
244       char path[PATH_MAX];
245       file.GetPath(path, sizeof(path));
246       m_option_data.m_args.emplace_back(path);
247     } else {
248       error.SetErrorStringWithFormat(
249           "file specified in --file (-f) option doesn't exist: '%s'",
250           arg_value);
251       return error;
252     }
253   }
254 
255   if (auto *arg = args.getLastArg(OPT_arch)) {
256     auto arg_value = arg->getValue();
257     if (!lldb::SBDebugger::SetDefaultArchitecture(arg_value)) {
258       error.SetErrorStringWithFormat(
259           "invalid architecture in the -a or --arch option: '%s'", arg_value);
260       return error;
261     }
262   }
263 
264   if (auto *arg = args.getLastArg(OPT_script_language)) {
265     auto arg_value = arg->getValue();
266     m_debugger.SetScriptLanguage(m_debugger.GetScriptingLanguage(arg_value));
267   }
268 
269   if (args.hasArg(OPT_source_quietly)) {
270     m_option_data.m_source_quietly = true;
271   }
272 
273   if (auto *arg = args.getLastArg(OPT_attach_name)) {
274     auto arg_value = arg->getValue();
275     m_option_data.m_process_name = arg_value;
276   }
277 
278   if (args.hasArg(OPT_wait_for)) {
279     m_option_data.m_wait_for = true;
280   }
281 
282   if (auto *arg = args.getLastArg(OPT_attach_pid)) {
283     auto arg_value = arg->getValue();
284     char *remainder;
285     m_option_data.m_process_pid = strtol(arg_value, &remainder, 0);
286     if (remainder == arg_value || *remainder != '\0') {
287       error.SetErrorStringWithFormat(
288           "Could not convert process PID: \"%s\" into a pid.", arg_value);
289       return error;
290     }
291   }
292 
293   if (auto *arg = args.getLastArg(OPT_repl_language)) {
294     auto arg_value = arg->getValue();
295     m_option_data.m_repl_lang =
296         SBLanguageRuntime::GetLanguageTypeFromString(arg_value);
297     if (m_option_data.m_repl_lang == eLanguageTypeUnknown) {
298       error.SetErrorStringWithFormat("Unrecognized language name: \"%s\"",
299                                      arg_value);
300       return error;
301     }
302     m_debugger.SetREPLLanguage(m_option_data.m_repl_lang);
303   }
304 
305   if (args.hasArg(OPT_repl)) {
306     m_option_data.m_repl = true;
307   }
308 
309   if (auto *arg = args.getLastArg(OPT_repl_)) {
310     m_option_data.m_repl = true;
311     if (auto arg_value = arg->getValue())
312       m_option_data.m_repl_options = arg_value;
313   }
314 
315   // We need to process the options below together as their relative order
316   // matters.
317   for (auto *arg : args.filtered(OPT_source_on_crash, OPT_one_line_on_crash,
318                                  OPT_source, OPT_source_before_file,
319                                  OPT_one_line, OPT_one_line_before_file)) {
320     auto arg_value = arg->getValue();
321     if (arg->getOption().matches(OPT_source_on_crash)) {
322       m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash,
323                                       true, error);
324       if (error.Fail())
325         return error;
326     }
327 
328     if (arg->getOption().matches(OPT_one_line_on_crash)) {
329       m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterCrash,
330                                       false, error);
331       if (error.Fail())
332         return error;
333     }
334 
335     if (arg->getOption().matches(OPT_source)) {
336       m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile,
337                                       true, error);
338       if (error.Fail())
339         return error;
340     }
341 
342     if (arg->getOption().matches(OPT_source_before_file)) {
343       m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile,
344                                       true, error);
345       if (error.Fail())
346         return error;
347     }
348 
349     if (arg->getOption().matches(OPT_one_line)) {
350       m_option_data.AddInitialCommand(arg_value, eCommandPlacementAfterFile,
351                                       false, error);
352       if (error.Fail())
353         return error;
354     }
355 
356     if (arg->getOption().matches(OPT_one_line_before_file)) {
357       m_option_data.AddInitialCommand(arg_value, eCommandPlacementBeforeFile,
358                                       false, error);
359       if (error.Fail())
360         return error;
361     }
362   }
363 
364   if (m_option_data.m_process_name.empty() &&
365       m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID) {
366 
367     for (auto *arg : args.filtered(OPT_INPUT))
368       m_option_data.m_args.push_back(arg->getAsString((args)));
369 
370     // Any argument following -- is an argument for the inferior.
371     if (auto *arg = args.getLastArgNoClaim(OPT_REM)) {
372       for (auto value : arg->getValues())
373         m_option_data.m_args.emplace_back(value);
374     }
375   } else if (args.getLastArgNoClaim() != nullptr) {
376     WithColor::warning() << "program arguments are ignored when attaching.\n";
377   }
378 
379   if (m_option_data.m_print_version) {
380     llvm::outs() << lldb::SBDebugger::GetVersionString() << '\n';
381     exiting = true;
382     return error;
383   }
384 
385   if (m_option_data.m_print_python_path) {
386     SBFileSpec python_file_spec = SBHostOS::GetLLDBPythonPath();
387     if (python_file_spec.IsValid()) {
388       char python_path[PATH_MAX];
389       size_t num_chars = python_file_spec.GetPath(python_path, PATH_MAX);
390       if (num_chars < PATH_MAX) {
391         llvm::outs() << python_path << '\n';
392       } else
393         llvm::outs() << "<PATH TOO LONG>\n";
394     } else
395       llvm::outs() << "<COULD NOT FIND PATH>\n";
396     exiting = true;
397     return error;
398   }
399 
400   if (m_option_data.m_print_script_interpreter_info) {
401     SBStructuredData info =
402         m_debugger.GetScriptInterpreterInfo(m_debugger.GetScriptLanguage());
403     if (!info) {
404       error.SetErrorString("no script interpreter.");
405     } else {
406       SBStream stream;
407       error = info.GetAsJSON(stream);
408       if (error.Success()) {
409         llvm::outs() << stream.GetData() << '\n';
410       }
411     }
412     exiting = true;
413     return error;
414   }
415 
416   return error;
417 }
418 
419 std::string EscapeString(std::string arg) {
420   std::string::size_type pos = 0;
421   while ((pos = arg.find_first_of("\"\\", pos)) != std::string::npos) {
422     arg.insert(pos, 1, '\\');
423     pos += 2;
424   }
425   return '"' + arg + '"';
426 }
427 
428 int Driver::MainLoop() {
429   if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0) {
430     g_old_stdin_termios_is_valid = true;
431     atexit(reset_stdin_termios);
432   }
433 
434 #ifndef _MSC_VER
435   // Disabling stdin buffering with MSVC's 2015 CRT exposes a bug in fgets
436   // which causes it to miss newlines depending on whether there have been an
437   // odd or even number of characters.  Bug has been reported to MS via Connect.
438   ::setbuf(stdin, nullptr);
439 #endif
440   ::setbuf(stdout, nullptr);
441 
442   m_debugger.SetErrorFileHandle(stderr, false);
443   m_debugger.SetOutputFileHandle(stdout, false);
444   // Don't take ownership of STDIN yet...
445   m_debugger.SetInputFileHandle(stdin, false);
446 
447   m_debugger.SetUseExternalEditor(m_option_data.m_use_external_editor);
448 
449   struct winsize window_size;
450   if ((isatty(STDIN_FILENO) != 0) &&
451       ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {
452     if (window_size.ws_col > 0)
453       m_debugger.SetTerminalWidth(window_size.ws_col);
454   }
455 
456   SBCommandInterpreter sb_interpreter = m_debugger.GetCommandInterpreter();
457 
458   // Process lldbinit files before handling any options from the command line.
459   SBCommandReturnObject result;
460   sb_interpreter.SourceInitFileInGlobalDirectory(result);
461   if (m_option_data.m_debug_mode) {
462     result.PutError(m_debugger.GetErrorFile());
463     result.PutOutput(m_debugger.GetOutputFile());
464   }
465 
466   sb_interpreter.SourceInitFileInHomeDirectory(result, m_option_data.m_repl);
467   if (m_option_data.m_debug_mode) {
468     result.PutError(m_debugger.GetErrorFile());
469     result.PutOutput(m_debugger.GetOutputFile());
470   }
471 
472   // Source the local .lldbinit file if it exists and we're allowed to source.
473   // Here we want to always print the return object because it contains the
474   // warning and instructions to load local lldbinit files.
475   sb_interpreter.SourceInitFileInCurrentWorkingDirectory(result);
476   result.PutError(m_debugger.GetErrorFile());
477   result.PutOutput(m_debugger.GetOutputFile());
478 
479   // We allow the user to specify an exit code when calling quit which we will
480   // return when exiting.
481   m_debugger.GetCommandInterpreter().AllowExitCodeOnQuit(true);
482 
483   // Now we handle options we got from the command line
484   SBStream commands_stream;
485 
486   // First source in the commands specified to be run before the file arguments
487   // are processed.
488   WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream);
489 
490   // If we're not in --repl mode, add the commands to process the file
491   // arguments, and the commands specified to run afterwards.
492   if (!m_option_data.m_repl) {
493     const size_t num_args = m_option_data.m_args.size();
494     if (num_args > 0) {
495       char arch_name[64];
496       if (lldb::SBDebugger::GetDefaultArchitecture(arch_name,
497                                                    sizeof(arch_name)))
498         commands_stream.Printf("target create --arch=%s %s", arch_name,
499                                EscapeString(m_option_data.m_args[0]).c_str());
500       else
501         commands_stream.Printf("target create %s",
502                                EscapeString(m_option_data.m_args[0]).c_str());
503 
504       if (!m_option_data.m_core_file.empty()) {
505         commands_stream.Printf(" --core %s",
506                                EscapeString(m_option_data.m_core_file).c_str());
507       }
508       commands_stream.Printf("\n");
509 
510       if (num_args > 1) {
511         commands_stream.Printf("settings set -- target.run-args ");
512         for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx)
513           commands_stream.Printf(
514               " %s", EscapeString(m_option_data.m_args[arg_idx]).c_str());
515         commands_stream.Printf("\n");
516       }
517     } else if (!m_option_data.m_core_file.empty()) {
518       commands_stream.Printf("target create --core %s\n",
519                              EscapeString(m_option_data.m_core_file).c_str());
520     } else if (!m_option_data.m_process_name.empty()) {
521       commands_stream.Printf(
522           "process attach --name %s",
523           EscapeString(m_option_data.m_process_name).c_str());
524 
525       if (m_option_data.m_wait_for)
526         commands_stream.Printf(" --waitfor");
527 
528       commands_stream.Printf("\n");
529 
530     } else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid) {
531       commands_stream.Printf("process attach --pid %" PRIu64 "\n",
532                              m_option_data.m_process_pid);
533     }
534 
535     WriteCommandsForSourcing(eCommandPlacementAfterFile, commands_stream);
536   } else if (!m_option_data.m_after_file_commands.empty()) {
537     // We're in repl mode and after-file-load commands were specified.
538     WithColor::warning() << "commands specified to run after file load (via -o "
539                             "or -s) are ignored in REPL mode.\n";
540   }
541 
542   if (m_option_data.m_debug_mode) {
543     result.PutError(m_debugger.GetErrorFile());
544     result.PutOutput(m_debugger.GetOutputFile());
545   }
546 
547   const bool handle_events = true;
548   const bool spawn_thread = false;
549 
550   // Check if we have any data in the commands stream, and if so, save it to a
551   // temp file
552   // so we can then run the command interpreter using the file contents.
553   bool go_interactive = true;
554   if ((commands_stream.GetData() != nullptr) &&
555       (commands_stream.GetSize() != 0u)) {
556     SBError error = m_debugger.SetInputString(commands_stream.GetData());
557     if (error.Fail()) {
558       WithColor::error() << error.GetCString() << '\n';
559       return 1;
560     }
561 
562     // Set the debugger into Sync mode when running the command file. Otherwise
563     // command files that run the target won't run in a sensible way.
564     bool old_async = m_debugger.GetAsync();
565     m_debugger.SetAsync(false);
566 
567     SBCommandInterpreterRunOptions options;
568     options.SetAutoHandleEvents(true);
569     options.SetSpawnThread(false);
570     options.SetStopOnError(true);
571     options.SetStopOnCrash(m_option_data.m_batch);
572     options.SetEchoCommands(!m_option_data.m_source_quietly);
573 
574     SBCommandInterpreterRunResult results =
575         m_debugger.RunCommandInterpreter(options);
576     if (results.GetResult() == lldb::eCommandInterpreterResultQuitRequested)
577       go_interactive = false;
578     if (m_option_data.m_batch &&
579         results.GetResult() != lldb::eCommandInterpreterResultInferiorCrash)
580       go_interactive = false;
581 
582     // When running in batch mode and stopped because of an error, exit with a
583     // non-zero exit status.
584     if (m_option_data.m_batch &&
585         results.GetResult() == lldb::eCommandInterpreterResultCommandError)
586       return 1;
587 
588     if (m_option_data.m_batch &&
589         results.GetResult() == lldb::eCommandInterpreterResultInferiorCrash &&
590         !m_option_data.m_after_crash_commands.empty()) {
591       SBStream crash_commands_stream;
592       WriteCommandsForSourcing(eCommandPlacementAfterCrash,
593                                crash_commands_stream);
594       SBError error =
595           m_debugger.SetInputString(crash_commands_stream.GetData());
596       if (error.Success()) {
597         SBCommandInterpreterRunResult local_results =
598             m_debugger.RunCommandInterpreter(options);
599         if (local_results.GetResult() ==
600             lldb::eCommandInterpreterResultQuitRequested)
601           go_interactive = false;
602 
603         // When running in batch mode and an error occurred while sourcing
604         // the crash commands, exit with a non-zero exit status.
605         if (m_option_data.m_batch &&
606             local_results.GetResult() ==
607                 lldb::eCommandInterpreterResultCommandError)
608           return 1;
609       }
610     }
611     m_debugger.SetAsync(old_async);
612   }
613 
614   // Now set the input file handle to STDIN and run the command interpreter
615   // again in interactive mode or repl mode and let the debugger take ownership
616   // of stdin.
617   if (go_interactive) {
618     m_debugger.SetInputFileHandle(stdin, true);
619 
620     if (m_option_data.m_repl) {
621       const char *repl_options = nullptr;
622       if (!m_option_data.m_repl_options.empty())
623         repl_options = m_option_data.m_repl_options.c_str();
624       SBError error(
625           m_debugger.RunREPL(m_option_data.m_repl_lang, repl_options));
626       if (error.Fail()) {
627         const char *error_cstr = error.GetCString();
628         if ((error_cstr != nullptr) && (error_cstr[0] != 0))
629           WithColor::error() << error_cstr << '\n';
630         else
631           WithColor::error() << error.GetError() << '\n';
632       }
633     } else {
634       m_debugger.RunCommandInterpreter(handle_events, spawn_thread);
635     }
636   }
637 
638   reset_stdin_termios();
639   fclose(stdin);
640 
641   return sb_interpreter.GetQuitStatus();
642 }
643 
644 void Driver::ResizeWindow(unsigned short col) {
645   GetDebugger().SetTerminalWidth(col);
646 }
647 
648 void sigwinch_handler(int signo) {
649   struct winsize window_size;
650   if ((isatty(STDIN_FILENO) != 0) &&
651       ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {
652     if ((window_size.ws_col > 0) && g_driver != nullptr) {
653       g_driver->ResizeWindow(window_size.ws_col);
654     }
655   }
656 }
657 
658 void sigint_handler(int signo) {
659 #ifdef _WIN32 // Restore handler as it is not persistent on Windows
660   signal(SIGINT, sigint_handler);
661 #endif
662   static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT;
663   if (g_driver != nullptr) {
664     if (!g_interrupt_sent.test_and_set()) {
665       g_driver->GetDebugger().DispatchInputInterrupt();
666       g_interrupt_sent.clear();
667       return;
668     }
669   }
670 
671   _exit(signo);
672 }
673 
674 #ifndef _WIN32
675 static void sigtstp_handler(int signo) {
676   if (g_driver != nullptr)
677     g_driver->GetDebugger().SaveInputTerminalState();
678 
679   // Unblock the signal and remove our handler.
680   sigset_t set;
681   sigemptyset(&set);
682   sigaddset(&set, signo);
683   pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
684   signal(signo, SIG_DFL);
685 
686   // Now re-raise the signal. We will immediately suspend...
687   raise(signo);
688   // ... and resume after a SIGCONT.
689 
690   // Now undo the modifications.
691   pthread_sigmask(SIG_BLOCK, &set, nullptr);
692   signal(signo, sigtstp_handler);
693 
694   if (g_driver != nullptr)
695     g_driver->GetDebugger().RestoreInputTerminalState();
696 }
697 #endif
698 
699 static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) {
700   std::string usage_str = tool_name.str() + " [options]";
701   table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB", false);
702 
703   std::string examples = R"___(
704 EXAMPLES:
705   The debugger can be started in several modes.
706 
707   Passing an executable as a positional argument prepares lldb to debug the
708   given executable. To disambiguate between arguments passed to lldb and
709   arguments passed to the debugged executable, arguments starting with a - must
710   be passed after --.
711 
712     lldb --arch x86_64 /path/to/program program argument -- --arch armv7
713 
714   For convenience, passing the executable after -- is also supported.
715 
716     lldb --arch x86_64 -- /path/to/program program argument --arch armv7
717 
718   Passing one of the attach options causes lldb to immediately attach to the
719   given process.
720 
721     lldb -p <pid>
722     lldb -n <process-name>
723 
724   Passing --repl starts lldb in REPL mode.
725 
726     lldb -r
727 
728   Passing --core causes lldb to debug the core file.
729 
730     lldb -c /path/to/core
731 
732   Command options can be combined with these modes and cause lldb to run the
733   specified commands before or after events, like loading the file or crashing,
734   in the order provided on the command line.
735 
736     lldb -O 'settings set stop-disassembly-count 20' -o 'run' -o 'bt'
737     lldb -S /source/before/file -s /source/after/file
738     lldb -K /source/before/crash -k /source/after/crash
739 
740   Note: In REPL mode no file is loaded, so commands specified to run after
741   loading the file (via -o or -s) will be ignored.)___";
742   llvm::outs() << examples << '\n';
743 }
744 
745 static llvm::Optional<int> InitializeReproducer(llvm::StringRef argv0,
746                                                 opt::InputArgList &input_args) {
747   bool capture = input_args.hasArg(OPT_capture);
748   bool generate_on_exit = input_args.hasArg(OPT_generate_on_exit);
749   auto *capture_path = input_args.getLastArg(OPT_capture_path);
750 
751   if (generate_on_exit && !capture) {
752     WithColor::warning()
753         << "-reproducer-generate-on-exit specified without -capture\n";
754   }
755 
756   if (capture || capture_path) {
757     if (capture_path) {
758       if (!capture)
759         WithColor::warning() << "-capture-path specified without -capture\n";
760       if (const char *error = SBReproducer::Capture(capture_path->getValue())) {
761         WithColor::error() << "reproducer capture failed: " << error << '\n';
762         return 1;
763       }
764     } else {
765       const char *error = SBReproducer::Capture();
766       if (error) {
767         WithColor::error() << "reproducer capture failed: " << error << '\n';
768         return 1;
769       }
770     }
771     if (generate_on_exit)
772       SBReproducer::SetAutoGenerate(true);
773   }
774 
775   return llvm::None;
776 }
777 
778 int main(int argc, char const *argv[]) {
779   // Editline uses for example iswprint which is dependent on LC_CTYPE.
780   std::setlocale(LC_ALL, "");
781   std::setlocale(LC_CTYPE, "");
782 
783   // Setup LLVM signal handlers and make sure we call llvm_shutdown() on
784   // destruction.
785   llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
786 
787   // Parse arguments.
788   LLDBOptTable T;
789   unsigned MissingArgIndex;
790   unsigned MissingArgCount;
791   ArrayRef<const char *> arg_arr = makeArrayRef(argv + 1, argc - 1);
792   opt::InputArgList input_args =
793       T.ParseArgs(arg_arr, MissingArgIndex, MissingArgCount);
794   llvm::StringRef argv0 = llvm::sys::path::filename(argv[0]);
795 
796   if (input_args.hasArg(OPT_help)) {
797     printHelp(T, argv0);
798     return 0;
799   }
800 
801   // Check for missing argument error.
802   if (MissingArgCount) {
803     WithColor::error() << "argument to '"
804                        << input_args.getArgString(MissingArgIndex)
805                        << "' is missing\n";
806   }
807   // Error out on unknown options.
808   if (input_args.hasArg(OPT_UNKNOWN)) {
809     for (auto *arg : input_args.filtered(OPT_UNKNOWN)) {
810       WithColor::error() << "unknown option: " << arg->getSpelling() << '\n';
811     }
812   }
813   if (MissingArgCount || input_args.hasArg(OPT_UNKNOWN)) {
814     llvm::errs() << "Use '" << argv0
815                  << " --help' for a complete list of options.\n";
816     return 1;
817   }
818 
819   if (auto exit_code = InitializeReproducer(argv[0], input_args)) {
820     return *exit_code;
821   }
822 
823   SBError error = SBDebugger::InitializeWithErrorHandling();
824   if (error.Fail()) {
825     WithColor::error() << "initialization failed: " << error.GetCString()
826                        << '\n';
827     return 1;
828   }
829   SBHostOS::ThreadCreated("<lldb.driver.main-thread>");
830 
831   signal(SIGINT, sigint_handler);
832 #if !defined(_WIN32)
833   signal(SIGPIPE, SIG_IGN);
834   signal(SIGWINCH, sigwinch_handler);
835   signal(SIGTSTP, sigtstp_handler);
836 #endif
837 
838   int exit_code = 0;
839   // Create a scope for driver so that the driver object will destroy itself
840   // before SBDebugger::Terminate() is called.
841   {
842     Driver driver;
843 
844     bool exiting = false;
845     SBError error(driver.ProcessArgs(input_args, exiting));
846     if (error.Fail()) {
847       exit_code = 1;
848       if (const char *error_cstr = error.GetCString())
849         WithColor::error() << error_cstr << '\n';
850     } else if (!exiting) {
851       exit_code = driver.MainLoop();
852     }
853   }
854 
855   SBDebugger::Terminate();
856   return exit_code;
857 }
858