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