xref: /freebsd/contrib/llvm-project/lldb/tools/driver/Driver.cpp (revision cb14a3fe5122c879eae1fb480ed7ce82a699ddb6)
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 
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/Support/Format.h"
24 #include "llvm/Support/InitLLVM.h"
25 #include "llvm/Support/Path.h"
26 #include "llvm/Support/Signals.h"
27 #include "llvm/Support/WithColor.h"
28 #include "llvm/Support/raw_ostream.h"
29 
30 #include <algorithm>
31 #include <atomic>
32 #include <bitset>
33 #include <clocale>
34 #include <csignal>
35 #include <string>
36 #include <thread>
37 #include <utility>
38 
39 #include <climits>
40 #include <cstdio>
41 #include <cstdlib>
42 #include <cstring>
43 #include <fcntl.h>
44 
45 #if !defined(__APPLE__)
46 #include "llvm/Support/DataTypes.h"
47 #endif
48 
49 using namespace lldb;
50 using namespace llvm;
51 
52 namespace {
53 using namespace llvm::opt;
54 
55 enum ID {
56   OPT_INVALID = 0, // This is not an option ID.
57 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
58 #include "Options.inc"
59 #undef OPTION
60 };
61 
62 #define PREFIX(NAME, VALUE)                                                    \
63   static constexpr StringLiteral NAME##_init[] = VALUE;                        \
64   static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
65                                                 std::size(NAME##_init) - 1);
66 #include "Options.inc"
67 #undef PREFIX
68 
69 static constexpr opt::OptTable::Info InfoTable[] = {
70 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
71 #include "Options.inc"
72 #undef OPTION
73 };
74 
75 class LLDBOptTable : public opt::GenericOptTable {
76 public:
77   LLDBOptTable() : opt::GenericOptTable(InfoTable) {}
78 };
79 } // namespace
80 
81 static void reset_stdin_termios();
82 static bool g_old_stdin_termios_is_valid = false;
83 static struct termios g_old_stdin_termios;
84 
85 static bool disable_color(const raw_ostream &OS) { return false; }
86 
87 static Driver *g_driver = nullptr;
88 
89 // In the Driver::MainLoop, we change the terminal settings.  This function is
90 // added as an atexit handler to make sure we clean them up.
91 static void reset_stdin_termios() {
92   if (g_old_stdin_termios_is_valid) {
93     g_old_stdin_termios_is_valid = false;
94     ::tcsetattr(STDIN_FILENO, TCSANOW, &g_old_stdin_termios);
95   }
96 }
97 
98 Driver::Driver()
99     : SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(false)) {
100   // We want to be able to handle CTRL+D in the terminal to have it terminate
101   // certain input
102   m_debugger.SetCloseInputOnEOF(false);
103   g_driver = this;
104 }
105 
106 Driver::~Driver() {
107   SBDebugger::Destroy(m_debugger);
108   g_driver = nullptr;
109 }
110 
111 void Driver::OptionData::AddInitialCommand(std::string command,
112                                            CommandPlacement placement,
113                                            bool is_file, SBError &error) {
114   std::vector<InitialCmdEntry> *command_set;
115   switch (placement) {
116   case eCommandPlacementBeforeFile:
117     command_set = &(m_initial_commands);
118     break;
119   case eCommandPlacementAfterFile:
120     command_set = &(m_after_file_commands);
121     break;
122   case eCommandPlacementAfterCrash:
123     command_set = &(m_after_crash_commands);
124     break;
125   }
126 
127   if (is_file) {
128     SBFileSpec file(command.c_str());
129     if (file.Exists())
130       command_set->push_back(InitialCmdEntry(command, is_file));
131     else if (file.ResolveExecutableLocation()) {
132       char final_path[PATH_MAX];
133       file.GetPath(final_path, sizeof(final_path));
134       command_set->push_back(InitialCmdEntry(final_path, is_file));
135     } else
136       error.SetErrorStringWithFormat(
137           "file specified in --source (-s) option doesn't exist: '%s'",
138           command.c_str());
139   } else
140     command_set->push_back(InitialCmdEntry(command, is_file));
141 }
142 
143 void Driver::WriteCommandsForSourcing(CommandPlacement placement,
144                                       SBStream &strm) {
145   std::vector<OptionData::InitialCmdEntry> *command_set;
146   switch (placement) {
147   case eCommandPlacementBeforeFile:
148     command_set = &m_option_data.m_initial_commands;
149     break;
150   case eCommandPlacementAfterFile:
151     command_set = &m_option_data.m_after_file_commands;
152     break;
153   case eCommandPlacementAfterCrash:
154     command_set = &m_option_data.m_after_crash_commands;
155     break;
156   }
157 
158   for (const auto &command_entry : *command_set) {
159     const char *command = command_entry.contents.c_str();
160     if (command_entry.is_file) {
161       bool source_quietly =
162           m_option_data.m_source_quietly || command_entry.source_quietly;
163       strm.Printf("command source -s %i '%s'\n",
164                   static_cast<int>(source_quietly), command);
165     } else
166       strm.Printf("%s\n", command);
167   }
168 }
169 
170 // Check the arguments that were passed to this program to make sure they are
171 // valid and to get their argument values (if any).  Return a boolean value
172 // indicating whether or not to start up the full debugger (i.e. the Command
173 // Interpreter) or not.  Return FALSE if the arguments were invalid OR if the
174 // user only wanted help or version information.
175 SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) {
176   SBError error;
177 
178   // This is kind of a pain, but since we make the debugger in the Driver's
179   // constructor, we can't know at that point whether we should read in init
180   // files yet.  So we don't read them in in the Driver constructor, then set
181   // the flags back to "read them in" here, and then if we see the "-n" flag,
182   // we'll turn it off again.  Finally we have to read them in by hand later in
183   // the main loop.
184   m_debugger.SkipLLDBInitFiles(false);
185   m_debugger.SkipAppInitFiles(false);
186 
187   if (args.hasArg(OPT_no_use_colors)) {
188     m_debugger.SetUseColor(false);
189     WithColor::setAutoDetectFunction(disable_color);
190     m_option_data.m_debug_mode = true;
191   }
192 
193   if (args.hasArg(OPT_version)) {
194     m_option_data.m_print_version = true;
195   }
196 
197   if (args.hasArg(OPT_python_path)) {
198     m_option_data.m_print_python_path = true;
199   }
200   if (args.hasArg(OPT_print_script_interpreter_info)) {
201     m_option_data.m_print_script_interpreter_info = true;
202   }
203 
204   if (args.hasArg(OPT_batch)) {
205     m_option_data.m_batch = true;
206   }
207 
208   if (auto *arg = args.getLastArg(OPT_core)) {
209     auto *arg_value = arg->getValue();
210     SBFileSpec file(arg_value);
211     if (!file.Exists()) {
212       error.SetErrorStringWithFormat(
213           "file specified in --core (-c) option doesn't exist: '%s'",
214           arg_value);
215       return error;
216     }
217     m_option_data.m_core_file = arg_value;
218   }
219 
220   if (args.hasArg(OPT_editor)) {
221     m_option_data.m_use_external_editor = true;
222   }
223 
224   if (args.hasArg(OPT_no_lldbinit)) {
225     m_debugger.SkipLLDBInitFiles(true);
226     m_debugger.SkipAppInitFiles(true);
227   }
228 
229   if (args.hasArg(OPT_local_lldbinit)) {
230     lldb::SBDebugger::SetInternalVariable("target.load-cwd-lldbinit", "true",
231                                           m_debugger.GetInstanceName());
232   }
233 
234   if (auto *arg = args.getLastArg(OPT_file)) {
235     auto *arg_value = arg->getValue();
236     SBFileSpec file(arg_value);
237     if (file.Exists()) {
238       m_option_data.m_args.emplace_back(arg_value);
239     } else if (file.ResolveExecutableLocation()) {
240       char path[PATH_MAX];
241       file.GetPath(path, sizeof(path));
242       m_option_data.m_args.emplace_back(path);
243     } else {
244       error.SetErrorStringWithFormat(
245           "file specified in --file (-f) option doesn't exist: '%s'",
246           arg_value);
247       return error;
248     }
249   }
250 
251   if (auto *arg = args.getLastArg(OPT_arch)) {
252     auto *arg_value = arg->getValue();
253     if (!lldb::SBDebugger::SetDefaultArchitecture(arg_value)) {
254       error.SetErrorStringWithFormat(
255           "invalid architecture in the -a or --arch option: '%s'", arg_value);
256       return error;
257     }
258   }
259 
260   if (auto *arg = args.getLastArg(OPT_script_language)) {
261     auto *arg_value = arg->getValue();
262     m_debugger.SetScriptLanguage(m_debugger.GetScriptingLanguage(arg_value));
263   }
264 
265   if (args.hasArg(OPT_source_quietly)) {
266     m_option_data.m_source_quietly = true;
267   }
268 
269   if (auto *arg = args.getLastArg(OPT_attach_name)) {
270     auto *arg_value = arg->getValue();
271     m_option_data.m_process_name = arg_value;
272   }
273 
274   if (args.hasArg(OPT_wait_for)) {
275     m_option_data.m_wait_for = true;
276   }
277 
278   if (auto *arg = args.getLastArg(OPT_attach_pid)) {
279     auto *arg_value = arg->getValue();
280     char *remainder;
281     m_option_data.m_process_pid = strtol(arg_value, &remainder, 0);
282     if (remainder == arg_value || *remainder != '\0') {
283       error.SetErrorStringWithFormat(
284           "Could not convert process PID: \"%s\" into a pid.", arg_value);
285       return error;
286     }
287   }
288 
289   if (auto *arg = args.getLastArg(OPT_repl_language)) {
290     auto *arg_value = arg->getValue();
291     m_option_data.m_repl_lang =
292         SBLanguageRuntime::GetLanguageTypeFromString(arg_value);
293     if (m_option_data.m_repl_lang == eLanguageTypeUnknown) {
294       error.SetErrorStringWithFormat("Unrecognized language name: \"%s\"",
295                                      arg_value);
296       return error;
297     }
298     m_debugger.SetREPLLanguage(m_option_data.m_repl_lang);
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   // Process lldbinit files before handling any options from the command line.
455   SBCommandReturnObject result;
456   sb_interpreter.SourceInitFileInGlobalDirectory(result);
457   if (m_option_data.m_debug_mode) {
458     result.PutError(m_debugger.GetErrorFile());
459     result.PutOutput(m_debugger.GetOutputFile());
460   }
461 
462   sb_interpreter.SourceInitFileInHomeDirectory(result, m_option_data.m_repl);
463   if (m_option_data.m_debug_mode) {
464     result.PutError(m_debugger.GetErrorFile());
465     result.PutOutput(m_debugger.GetOutputFile());
466   }
467 
468   // Source the local .lldbinit file if it exists and we're allowed to source.
469   // Here we want to always print the return object because it contains the
470   // warning and instructions to load local lldbinit files.
471   sb_interpreter.SourceInitFileInCurrentWorkingDirectory(result);
472   result.PutError(m_debugger.GetErrorFile());
473   result.PutOutput(m_debugger.GetOutputFile());
474 
475   // We allow the user to specify an exit code when calling quit which we will
476   // return when exiting.
477   m_debugger.GetCommandInterpreter().AllowExitCodeOnQuit(true);
478 
479   // Now we handle options we got from the command line
480   SBStream commands_stream;
481 
482   // First source in the commands specified to be run before the file arguments
483   // are processed.
484   WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream);
485 
486   // If we're not in --repl mode, add the commands to process the file
487   // arguments, and the commands specified to run afterwards.
488   if (!m_option_data.m_repl) {
489     const size_t num_args = m_option_data.m_args.size();
490     if (num_args > 0) {
491       char arch_name[64];
492       if (lldb::SBDebugger::GetDefaultArchitecture(arch_name,
493                                                    sizeof(arch_name)))
494         commands_stream.Printf("target create --arch=%s %s", arch_name,
495                                EscapeString(m_option_data.m_args[0]).c_str());
496       else
497         commands_stream.Printf("target create %s",
498                                EscapeString(m_option_data.m_args[0]).c_str());
499 
500       if (!m_option_data.m_core_file.empty()) {
501         commands_stream.Printf(" --core %s",
502                                EscapeString(m_option_data.m_core_file).c_str());
503       }
504       commands_stream.Printf("\n");
505 
506       if (num_args > 1) {
507         commands_stream.Printf("settings set -- target.run-args ");
508         for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx)
509           commands_stream.Printf(
510               " %s", EscapeString(m_option_data.m_args[arg_idx]).c_str());
511         commands_stream.Printf("\n");
512       }
513     } else if (!m_option_data.m_core_file.empty()) {
514       commands_stream.Printf("target create --core %s\n",
515                              EscapeString(m_option_data.m_core_file).c_str());
516     } else if (!m_option_data.m_process_name.empty()) {
517       commands_stream.Printf(
518           "process attach --name %s",
519           EscapeString(m_option_data.m_process_name).c_str());
520 
521       if (m_option_data.m_wait_for)
522         commands_stream.Printf(" --waitfor");
523 
524       commands_stream.Printf("\n");
525 
526     } else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid) {
527       commands_stream.Printf("process attach --pid %" PRIu64 "\n",
528                              m_option_data.m_process_pid);
529     }
530 
531     WriteCommandsForSourcing(eCommandPlacementAfterFile, commands_stream);
532   } else if (!m_option_data.m_after_file_commands.empty()) {
533     // We're in repl mode and after-file-load commands were specified.
534     WithColor::warning() << "commands specified to run after file load (via -o "
535                             "or -s) are ignored in REPL mode.\n";
536   }
537 
538   if (m_option_data.m_debug_mode) {
539     result.PutError(m_debugger.GetErrorFile());
540     result.PutOutput(m_debugger.GetOutputFile());
541   }
542 
543   const bool handle_events = true;
544   const bool spawn_thread = false;
545 
546   // Check if we have any data in the commands stream, and if so, save it to a
547   // temp file
548   // so we can then run the command interpreter using the file contents.
549   bool go_interactive = true;
550   if ((commands_stream.GetData() != nullptr) &&
551       (commands_stream.GetSize() != 0u)) {
552     SBError error = m_debugger.SetInputString(commands_stream.GetData());
553     if (error.Fail()) {
554       WithColor::error() << error.GetCString() << '\n';
555       return 1;
556     }
557 
558     // Set the debugger into Sync mode when running the command file. Otherwise
559     // command files that run the target won't run in a sensible way.
560     bool old_async = m_debugger.GetAsync();
561     m_debugger.SetAsync(false);
562 
563     SBCommandInterpreterRunOptions options;
564     options.SetAutoHandleEvents(true);
565     options.SetSpawnThread(false);
566     options.SetStopOnError(true);
567     options.SetStopOnCrash(m_option_data.m_batch);
568     options.SetEchoCommands(!m_option_data.m_source_quietly);
569 
570     SBCommandInterpreterRunResult results =
571         m_debugger.RunCommandInterpreter(options);
572     if (results.GetResult() == lldb::eCommandInterpreterResultQuitRequested)
573       go_interactive = false;
574     if (m_option_data.m_batch &&
575         results.GetResult() != lldb::eCommandInterpreterResultInferiorCrash)
576       go_interactive = false;
577 
578     // When running in batch mode and stopped because of an error, exit with a
579     // non-zero exit status.
580     if (m_option_data.m_batch &&
581         results.GetResult() == lldb::eCommandInterpreterResultCommandError)
582       return 1;
583 
584     if (m_option_data.m_batch &&
585         results.GetResult() == lldb::eCommandInterpreterResultInferiorCrash &&
586         !m_option_data.m_after_crash_commands.empty()) {
587       SBStream crash_commands_stream;
588       WriteCommandsForSourcing(eCommandPlacementAfterCrash,
589                                crash_commands_stream);
590       SBError error =
591           m_debugger.SetInputString(crash_commands_stream.GetData());
592       if (error.Success()) {
593         SBCommandInterpreterRunResult local_results =
594             m_debugger.RunCommandInterpreter(options);
595         if (local_results.GetResult() ==
596             lldb::eCommandInterpreterResultQuitRequested)
597           go_interactive = false;
598 
599         // When running in batch mode and an error occurred while sourcing
600         // the crash commands, exit with a non-zero exit status.
601         if (m_option_data.m_batch &&
602             local_results.GetResult() ==
603                 lldb::eCommandInterpreterResultCommandError)
604           return 1;
605       }
606     }
607     m_debugger.SetAsync(old_async);
608   }
609 
610   // Now set the input file handle to STDIN and run the command interpreter
611   // again in interactive mode or repl mode and let the debugger take ownership
612   // of stdin.
613   if (go_interactive) {
614     m_debugger.SetInputFileHandle(stdin, true);
615 
616     if (m_option_data.m_repl) {
617       const char *repl_options = nullptr;
618       if (!m_option_data.m_repl_options.empty())
619         repl_options = m_option_data.m_repl_options.c_str();
620       SBError error(
621           m_debugger.RunREPL(m_option_data.m_repl_lang, repl_options));
622       if (error.Fail()) {
623         const char *error_cstr = error.GetCString();
624         if ((error_cstr != nullptr) && (error_cstr[0] != 0))
625           WithColor::error() << error_cstr << '\n';
626         else
627           WithColor::error() << error.GetError() << '\n';
628       }
629     } else {
630       m_debugger.RunCommandInterpreter(handle_events, spawn_thread);
631     }
632   }
633 
634   reset_stdin_termios();
635   fclose(stdin);
636 
637   return sb_interpreter.GetQuitStatus();
638 }
639 
640 void Driver::ResizeWindow(unsigned short col) {
641   GetDebugger().SetTerminalWidth(col);
642 }
643 
644 void sigwinch_handler(int signo) {
645   struct winsize window_size;
646   if ((isatty(STDIN_FILENO) != 0) &&
647       ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {
648     if ((window_size.ws_col > 0) && g_driver != nullptr) {
649       g_driver->ResizeWindow(window_size.ws_col);
650     }
651   }
652 }
653 
654 void sigint_handler(int signo) {
655 #ifdef _WIN32 // Restore handler as it is not persistent on Windows
656   signal(SIGINT, sigint_handler);
657 #endif
658   static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT;
659   if (g_driver != nullptr) {
660     if (!g_interrupt_sent.test_and_set()) {
661       g_driver->GetDebugger().DispatchInputInterrupt();
662       g_interrupt_sent.clear();
663       return;
664     }
665   }
666 
667   _exit(signo);
668 }
669 
670 #ifndef _WIN32
671 static void sigtstp_handler(int signo) {
672   if (g_driver != nullptr)
673     g_driver->GetDebugger().SaveInputTerminalState();
674 
675   // Unblock the signal and remove our handler.
676   sigset_t set;
677   sigemptyset(&set);
678   sigaddset(&set, signo);
679   pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
680   signal(signo, SIG_DFL);
681 
682   // Now re-raise the signal. We will immediately suspend...
683   raise(signo);
684   // ... and resume after a SIGCONT.
685 
686   // Now undo the modifications.
687   pthread_sigmask(SIG_BLOCK, &set, nullptr);
688   signal(signo, sigtstp_handler);
689 
690   if (g_driver != nullptr)
691     g_driver->GetDebugger().RestoreInputTerminalState();
692 }
693 #endif
694 
695 static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) {
696   std::string usage_str = tool_name.str() + " [options]";
697   table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB", false);
698 
699   std::string examples = R"___(
700 EXAMPLES:
701   The debugger can be started in several modes.
702 
703   Passing an executable as a positional argument prepares lldb to debug the
704   given executable. To disambiguate between arguments passed to lldb and
705   arguments passed to the debugged executable, arguments starting with a - must
706   be passed after --.
707 
708     lldb --arch x86_64 /path/to/program program argument -- --arch armv7
709 
710   For convenience, passing the executable after -- is also supported.
711 
712     lldb --arch x86_64 -- /path/to/program program argument --arch armv7
713 
714   Passing one of the attach options causes lldb to immediately attach to the
715   given process.
716 
717     lldb -p <pid>
718     lldb -n <process-name>
719 
720   Passing --repl starts lldb in REPL mode.
721 
722     lldb -r
723 
724   Passing --core causes lldb to debug the core file.
725 
726     lldb -c /path/to/core
727 
728   Command options can be combined with these modes and cause lldb to run the
729   specified commands before or after events, like loading the file or crashing,
730   in the order provided on the command line.
731 
732     lldb -O 'settings set stop-disassembly-count 20' -o 'run' -o 'bt'
733     lldb -S /source/before/file -s /source/after/file
734     lldb -K /source/before/crash -k /source/after/crash
735 
736   Note: In REPL mode no file is loaded, so commands specified to run after
737   loading the file (via -o or -s) will be ignored.)___";
738   llvm::outs() << examples << '\n';
739 }
740 
741 int main(int argc, char const *argv[]) {
742   // Editline uses for example iswprint which is dependent on LC_CTYPE.
743   std::setlocale(LC_ALL, "");
744   std::setlocale(LC_CTYPE, "");
745 
746   // Setup LLVM signal handlers and make sure we call llvm_shutdown() on
747   // destruction.
748   llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
749 
750   // Parse arguments.
751   LLDBOptTable T;
752   unsigned MissingArgIndex;
753   unsigned MissingArgCount;
754   ArrayRef<const char *> arg_arr = ArrayRef(argv + 1, argc - 1);
755   opt::InputArgList input_args =
756       T.ParseArgs(arg_arr, MissingArgIndex, MissingArgCount);
757   llvm::StringRef argv0 = llvm::sys::path::filename(argv[0]);
758 
759   if (input_args.hasArg(OPT_help)) {
760     printHelp(T, argv0);
761     return 0;
762   }
763 
764   // Check for missing argument error.
765   if (MissingArgCount) {
766     WithColor::error() << "argument to '"
767                        << input_args.getArgString(MissingArgIndex)
768                        << "' is missing\n";
769   }
770   // Error out on unknown options.
771   if (input_args.hasArg(OPT_UNKNOWN)) {
772     for (auto *arg : input_args.filtered(OPT_UNKNOWN)) {
773       WithColor::error() << "unknown option: " << arg->getSpelling() << '\n';
774     }
775   }
776   if (MissingArgCount || input_args.hasArg(OPT_UNKNOWN)) {
777     llvm::errs() << "Use '" << argv0
778                  << " --help' for a complete list of options.\n";
779     return 1;
780   }
781 
782   SBError error = SBDebugger::InitializeWithErrorHandling();
783   if (error.Fail()) {
784     WithColor::error() << "initialization failed: " << error.GetCString()
785                        << '\n';
786     return 1;
787   }
788 
789   // Setup LLDB signal handlers once the debugger has been initialized.
790   SBDebugger::PrintDiagnosticsOnError();
791 
792   signal(SIGINT, sigint_handler);
793 #if !defined(_WIN32)
794   signal(SIGPIPE, SIG_IGN);
795   signal(SIGWINCH, sigwinch_handler);
796   signal(SIGTSTP, sigtstp_handler);
797 #endif
798 
799   int exit_code = 0;
800   // Create a scope for driver so that the driver object will destroy itself
801   // before SBDebugger::Terminate() is called.
802   {
803     Driver driver;
804 
805     bool exiting = false;
806     SBError error(driver.ProcessArgs(input_args, exiting));
807     if (error.Fail()) {
808       exit_code = 1;
809       if (const char *error_cstr = error.GetCString())
810         WithColor::error() << error_cstr << '\n';
811     } else if (!exiting) {
812       exit_code = driver.MainLoop();
813     }
814   }
815 
816   SBDebugger::Terminate();
817   return exit_code;
818 }
819