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