xref: /freebsd/contrib/llvm-project/lldb/tools/driver/Driver.cpp (revision 924226fba12cc9a228c73b956e1b7fa24c60b055)
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     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   // Before we handle any options from the command line, we parse the
456   // REPL init file or the default file in the user's home directory.
457   SBCommandReturnObject result;
458   sb_interpreter.SourceInitFileInHomeDirectory(result, m_option_data.m_repl);
459   if (m_option_data.m_debug_mode) {
460     result.PutError(m_debugger.GetErrorFile());
461     result.PutOutput(m_debugger.GetOutputFile());
462   }
463 
464   // Source the local .lldbinit file if it exists and we're allowed to source.
465   // Here we want to always print the return object because it contains the
466   // warning and instructions to load local lldbinit files.
467   sb_interpreter.SourceInitFileInCurrentWorkingDirectory(result);
468   result.PutError(m_debugger.GetErrorFile());
469   result.PutOutput(m_debugger.GetOutputFile());
470 
471   // We allow the user to specify an exit code when calling quit which we will
472   // return when exiting.
473   m_debugger.GetCommandInterpreter().AllowExitCodeOnQuit(true);
474 
475   // Now we handle options we got from the command line
476   SBStream commands_stream;
477 
478   // First source in the commands specified to be run before the file arguments
479   // are processed.
480   WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream);
481 
482   // If we're not in --repl mode, add the commands to process the file
483   // arguments, and the commands specified to run afterwards.
484   if (!m_option_data.m_repl) {
485     const size_t num_args = m_option_data.m_args.size();
486     if (num_args > 0) {
487       char arch_name[64];
488       if (lldb::SBDebugger::GetDefaultArchitecture(arch_name,
489                                                    sizeof(arch_name)))
490         commands_stream.Printf("target create --arch=%s %s", arch_name,
491                                EscapeString(m_option_data.m_args[0]).c_str());
492       else
493         commands_stream.Printf("target create %s",
494                                EscapeString(m_option_data.m_args[0]).c_str());
495 
496       if (!m_option_data.m_core_file.empty()) {
497         commands_stream.Printf(" --core %s",
498                                EscapeString(m_option_data.m_core_file).c_str());
499       }
500       commands_stream.Printf("\n");
501 
502       if (num_args > 1) {
503         commands_stream.Printf("settings set -- target.run-args ");
504         for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx)
505           commands_stream.Printf(
506               " %s", EscapeString(m_option_data.m_args[arg_idx]).c_str());
507         commands_stream.Printf("\n");
508       }
509     } else if (!m_option_data.m_core_file.empty()) {
510       commands_stream.Printf("target create --core %s\n",
511                              EscapeString(m_option_data.m_core_file).c_str());
512     } else if (!m_option_data.m_process_name.empty()) {
513       commands_stream.Printf(
514           "process attach --name %s",
515           EscapeString(m_option_data.m_process_name).c_str());
516 
517       if (m_option_data.m_wait_for)
518         commands_stream.Printf(" --waitfor");
519 
520       commands_stream.Printf("\n");
521 
522     } else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid) {
523       commands_stream.Printf("process attach --pid %" PRIu64 "\n",
524                              m_option_data.m_process_pid);
525     }
526 
527     WriteCommandsForSourcing(eCommandPlacementAfterFile, commands_stream);
528   } else if (!m_option_data.m_after_file_commands.empty()) {
529     // We're in repl mode and after-file-load commands were specified.
530     WithColor::warning() << "commands specified to run after file load (via -o "
531                             "or -s) are ignored in REPL mode.\n";
532   }
533 
534   if (m_option_data.m_debug_mode) {
535     result.PutError(m_debugger.GetErrorFile());
536     result.PutOutput(m_debugger.GetOutputFile());
537   }
538 
539   const bool handle_events = true;
540   const bool spawn_thread = false;
541 
542   // Check if we have any data in the commands stream, and if so, save it to a
543   // temp file
544   // so we can then run the command interpreter using the file contents.
545   bool go_interactive = true;
546   if ((commands_stream.GetData() != nullptr) &&
547       (commands_stream.GetSize() != 0u)) {
548     SBError error = m_debugger.SetInputString(commands_stream.GetData());
549     if (error.Fail()) {
550       WithColor::error() << error.GetCString() << '\n';
551       return 1;
552     }
553 
554     // Set the debugger into Sync mode when running the command file. Otherwise
555     // command files that run the target won't run in a sensible way.
556     bool old_async = m_debugger.GetAsync();
557     m_debugger.SetAsync(false);
558 
559     SBCommandInterpreterRunOptions options;
560     options.SetAutoHandleEvents(true);
561     options.SetSpawnThread(false);
562     options.SetStopOnError(true);
563     options.SetStopOnCrash(m_option_data.m_batch);
564     options.SetEchoCommands(!m_option_data.m_source_quietly);
565 
566     SBCommandInterpreterRunResult results =
567         m_debugger.RunCommandInterpreter(options);
568     if (results.GetResult() == lldb::eCommandInterpreterResultQuitRequested)
569       go_interactive = false;
570     if (m_option_data.m_batch &&
571         results.GetResult() != lldb::eCommandInterpreterResultInferiorCrash)
572       go_interactive = false;
573 
574     // When running in batch mode and stopped because of an error, exit with a
575     // non-zero exit status.
576     if (m_option_data.m_batch &&
577         results.GetResult() == lldb::eCommandInterpreterResultCommandError)
578       return 1;
579 
580     if (m_option_data.m_batch &&
581         results.GetResult() == lldb::eCommandInterpreterResultInferiorCrash &&
582         !m_option_data.m_after_crash_commands.empty()) {
583       SBStream crash_commands_stream;
584       WriteCommandsForSourcing(eCommandPlacementAfterCrash,
585                                crash_commands_stream);
586       SBError error =
587           m_debugger.SetInputString(crash_commands_stream.GetData());
588       if (error.Success()) {
589         SBCommandInterpreterRunResult local_results =
590             m_debugger.RunCommandInterpreter(options);
591         if (local_results.GetResult() ==
592             lldb::eCommandInterpreterResultQuitRequested)
593           go_interactive = false;
594 
595         // When running in batch mode and an error occurred while sourcing
596         // the crash commands, exit with a non-zero exit status.
597         if (m_option_data.m_batch &&
598             local_results.GetResult() ==
599                 lldb::eCommandInterpreterResultCommandError)
600           return 1;
601       }
602     }
603     m_debugger.SetAsync(old_async);
604   }
605 
606   // Now set the input file handle to STDIN and run the command interpreter
607   // again in interactive mode or repl mode and let the debugger take ownership
608   // of stdin.
609   if (go_interactive) {
610     m_debugger.SetInputFileHandle(stdin, true);
611 
612     if (m_option_data.m_repl) {
613       const char *repl_options = nullptr;
614       if (!m_option_data.m_repl_options.empty())
615         repl_options = m_option_data.m_repl_options.c_str();
616       SBError error(
617           m_debugger.RunREPL(m_option_data.m_repl_lang, repl_options));
618       if (error.Fail()) {
619         const char *error_cstr = error.GetCString();
620         if ((error_cstr != nullptr) && (error_cstr[0] != 0))
621           WithColor::error() << error_cstr << '\n';
622         else
623           WithColor::error() << error.GetError() << '\n';
624       }
625     } else {
626       m_debugger.RunCommandInterpreter(handle_events, spawn_thread);
627     }
628   }
629 
630   reset_stdin_termios();
631   fclose(stdin);
632 
633   return sb_interpreter.GetQuitStatus();
634 }
635 
636 void Driver::ResizeWindow(unsigned short col) {
637   GetDebugger().SetTerminalWidth(col);
638 }
639 
640 void sigwinch_handler(int signo) {
641   struct winsize window_size;
642   if ((isatty(STDIN_FILENO) != 0) &&
643       ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {
644     if ((window_size.ws_col > 0) && g_driver != nullptr) {
645       g_driver->ResizeWindow(window_size.ws_col);
646     }
647   }
648 }
649 
650 void sigint_handler(int signo) {
651 #ifdef _WIN32 // Restore handler as it is not persistent on Windows
652   signal(SIGINT, sigint_handler);
653 #endif
654   static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT;
655   if (g_driver != nullptr) {
656     if (!g_interrupt_sent.test_and_set()) {
657       g_driver->GetDebugger().DispatchInputInterrupt();
658       g_interrupt_sent.clear();
659       return;
660     }
661   }
662 
663   _exit(signo);
664 }
665 
666 void sigtstp_handler(int signo) {
667   if (g_driver != nullptr)
668     g_driver->GetDebugger().SaveInputTerminalState();
669 
670   signal(signo, SIG_DFL);
671   kill(getpid(), signo);
672   signal(signo, sigtstp_handler);
673 }
674 
675 void sigcont_handler(int signo) {
676   if (g_driver != nullptr)
677     g_driver->GetDebugger().RestoreInputTerminalState();
678 
679   signal(signo, SIG_DFL);
680   kill(getpid(), signo);
681   signal(signo, sigcont_handler);
682 }
683 
684 void reproducer_handler(void *finalize_cmd) {
685   if (SBReproducer::Generate()) {
686     int result = std::system(static_cast<const char *>(finalize_cmd));
687     (void)result;
688     fflush(stdout);
689   }
690 }
691 
692 static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) {
693   std::string usage_str = tool_name.str() + " [options]";
694   table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB", false);
695 
696   std::string examples = R"___(
697 EXAMPLES:
698   The debugger can be started in several modes.
699 
700   Passing an executable as a positional argument prepares lldb to debug the
701   given executable. To disambiguate between arguments passed to lldb and
702   arguments passed to the debugged executable, arguments starting with a - must
703   be passed after --.
704 
705     lldb --arch x86_64 /path/to/program program argument -- --arch armv7
706 
707   For convenience, passing the executable after -- is also supported.
708 
709     lldb --arch x86_64 -- /path/to/program program argument --arch armv7
710 
711   Passing one of the attach options causes lldb to immediately attach to the
712   given process.
713 
714     lldb -p <pid>
715     lldb -n <process-name>
716 
717   Passing --repl starts lldb in REPL mode.
718 
719     lldb -r
720 
721   Passing --core causes lldb to debug the core file.
722 
723     lldb -c /path/to/core
724 
725   Command options can be combined with these modes and cause lldb to run the
726   specified commands before or after events, like loading the file or crashing,
727   in the order provided on the command line.
728 
729     lldb -O 'settings set stop-disassembly-count 20' -o 'run' -o 'bt'
730     lldb -S /source/before/file -s /source/after/file
731     lldb -K /source/before/crash -k /source/after/crash
732 
733   Note: In REPL mode no file is loaded, so commands specified to run after
734   loading the file (via -o or -s) will be ignored.)___";
735   llvm::outs() << examples << '\n';
736 }
737 
738 static llvm::Optional<int> InitializeReproducer(llvm::StringRef argv0,
739                                                 opt::InputArgList &input_args) {
740   bool capture = input_args.hasArg(OPT_capture);
741   bool generate_on_exit = input_args.hasArg(OPT_generate_on_exit);
742   auto *capture_path = input_args.getLastArg(OPT_capture_path);
743 
744   if (generate_on_exit && !capture) {
745     WithColor::warning()
746         << "-reproducer-generate-on-exit specified without -capture\n";
747   }
748 
749   if (capture || capture_path) {
750     if (capture_path) {
751       if (!capture)
752         WithColor::warning() << "-capture-path specified without -capture\n";
753       if (const char *error = SBReproducer::Capture(capture_path->getValue())) {
754         WithColor::error() << "reproducer capture failed: " << error << '\n';
755         return 1;
756       }
757     } else {
758       const char *error = SBReproducer::Capture();
759       if (error) {
760         WithColor::error() << "reproducer capture failed: " << error << '\n';
761         return 1;
762       }
763     }
764     if (generate_on_exit)
765       SBReproducer::SetAutoGenerate(true);
766   }
767 
768   return llvm::None;
769 }
770 
771 int main(int argc, char const *argv[]) {
772   // Editline uses for example iswprint which is dependent on LC_CTYPE.
773   std::setlocale(LC_ALL, "");
774   std::setlocale(LC_CTYPE, "");
775 
776   // Setup LLVM signal handlers and make sure we call llvm_shutdown() on
777   // destruction.
778   llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
779 
780   // Parse arguments.
781   LLDBOptTable T;
782   unsigned MissingArgIndex;
783   unsigned MissingArgCount;
784   ArrayRef<const char *> arg_arr = makeArrayRef(argv + 1, argc - 1);
785   opt::InputArgList input_args =
786       T.ParseArgs(arg_arr, MissingArgIndex, MissingArgCount);
787   llvm::StringRef argv0 = llvm::sys::path::filename(argv[0]);
788 
789   if (input_args.hasArg(OPT_help)) {
790     printHelp(T, argv0);
791     return 0;
792   }
793 
794   // Check for missing argument error.
795   if (MissingArgCount) {
796     WithColor::error() << "argument to '"
797                        << input_args.getArgString(MissingArgIndex)
798                        << "' is missing\n";
799   }
800   // Error out on unknown options.
801   if (input_args.hasArg(OPT_UNKNOWN)) {
802     for (auto *arg : input_args.filtered(OPT_UNKNOWN)) {
803       WithColor::error() << "unknown option: " << arg->getSpelling() << '\n';
804     }
805   }
806   if (MissingArgCount || input_args.hasArg(OPT_UNKNOWN)) {
807     llvm::errs() << "Use '" << argv0
808                  << " --help' for a complete list of options.\n";
809     return 1;
810   }
811 
812   if (auto exit_code = InitializeReproducer(argv[0], input_args)) {
813     return *exit_code;
814   }
815 
816   SBError error = SBDebugger::InitializeWithErrorHandling();
817   if (error.Fail()) {
818     WithColor::error() << "initialization failed: " << error.GetCString()
819                        << '\n';
820     return 1;
821   }
822   SBHostOS::ThreadCreated("<lldb.driver.main-thread>");
823 
824   signal(SIGINT, sigint_handler);
825 #if !defined(_MSC_VER)
826   signal(SIGPIPE, SIG_IGN);
827   signal(SIGWINCH, sigwinch_handler);
828   signal(SIGTSTP, sigtstp_handler);
829   signal(SIGCONT, sigcont_handler);
830 #endif
831 
832   int exit_code = 0;
833   // Create a scope for driver so that the driver object will destroy itself
834   // before SBDebugger::Terminate() is called.
835   {
836     Driver driver;
837 
838     bool exiting = false;
839     SBError error(driver.ProcessArgs(input_args, exiting));
840     if (error.Fail()) {
841       exit_code = 1;
842       if (const char *error_cstr = error.GetCString())
843         WithColor::error() << error_cstr << '\n';
844     } else if (!exiting) {
845       exit_code = driver.MainLoop();
846     }
847   }
848 
849   SBDebugger::Terminate();
850   return exit_code;
851 }
852