xref: /freebsd/contrib/llvm-project/lldb/tools/driver/Driver.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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 <future>
37 #include <string>
38 #include <thread>
39 #include <utility>
40 
41 #include <climits>
42 #include <cstdio>
43 #include <cstdlib>
44 #include <cstring>
45 #include <fcntl.h>
46 
47 #if !defined(__APPLE__)
48 #include "llvm/Support/DataTypes.h"
49 #endif
50 
51 using namespace lldb;
52 using namespace llvm;
53 
54 namespace {
55 using namespace llvm::opt;
56 
57 enum ID {
58   OPT_INVALID = 0, // This is not an option ID.
59 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
60 #include "Options.inc"
61 #undef OPTION
62 };
63 
64 #define PREFIX(NAME, VALUE)                                                    \
65   static constexpr StringLiteral NAME##_init[] = VALUE;                        \
66   static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
67                                                 std::size(NAME##_init) - 1);
68 #include "Options.inc"
69 #undef PREFIX
70 
71 static constexpr opt::OptTable::Info InfoTable[] = {
72 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
73 #include "Options.inc"
74 #undef OPTION
75 };
76 
77 class LLDBOptTable : public opt::GenericOptTable {
78 public:
LLDBOptTable()79   LLDBOptTable() : opt::GenericOptTable(InfoTable) {}
80 };
81 } // namespace
82 
83 static void reset_stdin_termios();
84 static bool g_old_stdin_termios_is_valid = false;
85 static struct termios g_old_stdin_termios;
86 
disable_color(const raw_ostream & OS)87 static bool disable_color(const raw_ostream &OS) { return false; }
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.
reset_stdin_termios()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 
Driver()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 
~Driver()108 Driver::~Driver() {
109   SBDebugger::Destroy(m_debugger);
110   g_driver = nullptr;
111 }
112 
AddInitialCommand(std::string command,CommandPlacement placement,bool is_file,SBError & error)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 
WriteCommandsForSourcing(CommandPlacement placement,SBStream & strm)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.
ProcessArgs(const opt::InputArgList & args,bool & exiting)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_no_use_colors)) {
190     m_debugger.SetUseColor(false);
191     WithColor::setAutoDetectFunction(disable_color);
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 
EscapeString(std::string arg)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 
MainLoop()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   sb_interpreter.SourceInitFileInHomeDirectory(result, m_option_data.m_repl);
459 
460   // Source the local .lldbinit file if it exists and we're allowed to source.
461   // Here we want to always print the return object because it contains the
462   // warning and instructions to load local lldbinit files.
463   sb_interpreter.SourceInitFileInCurrentWorkingDirectory(result);
464   result.PutError(m_debugger.GetErrorFile());
465   result.PutOutput(m_debugger.GetOutputFile());
466 
467   // We allow the user to specify an exit code when calling quit which we will
468   // return when exiting.
469   m_debugger.GetCommandInterpreter().AllowExitCodeOnQuit(true);
470 
471   // Now we handle options we got from the command line
472   SBStream commands_stream;
473 
474   // First source in the commands specified to be run before the file arguments
475   // are processed.
476   WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream);
477 
478   // If we're not in --repl mode, add the commands to process the file
479   // arguments, and the commands specified to run afterwards.
480   if (!m_option_data.m_repl) {
481     const size_t num_args = m_option_data.m_args.size();
482     if (num_args > 0) {
483       char arch_name[64];
484       if (lldb::SBDebugger::GetDefaultArchitecture(arch_name,
485                                                    sizeof(arch_name)))
486         commands_stream.Printf("target create --arch=%s %s", arch_name,
487                                EscapeString(m_option_data.m_args[0]).c_str());
488       else
489         commands_stream.Printf("target create %s",
490                                EscapeString(m_option_data.m_args[0]).c_str());
491 
492       if (!m_option_data.m_core_file.empty()) {
493         commands_stream.Printf(" --core %s",
494                                EscapeString(m_option_data.m_core_file).c_str());
495       }
496       commands_stream.Printf("\n");
497 
498       if (num_args > 1) {
499         commands_stream.Printf("settings set -- target.run-args ");
500         for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx)
501           commands_stream.Printf(
502               " %s", EscapeString(m_option_data.m_args[arg_idx]).c_str());
503         commands_stream.Printf("\n");
504       }
505     } else if (!m_option_data.m_core_file.empty()) {
506       commands_stream.Printf("target create --core %s\n",
507                              EscapeString(m_option_data.m_core_file).c_str());
508     } else if (!m_option_data.m_process_name.empty()) {
509       commands_stream.Printf(
510           "process attach --name %s",
511           EscapeString(m_option_data.m_process_name).c_str());
512 
513       if (m_option_data.m_wait_for)
514         commands_stream.Printf(" --waitfor");
515 
516       commands_stream.Printf("\n");
517 
518     } else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid) {
519       commands_stream.Printf("process attach --pid %" PRIu64 "\n",
520                              m_option_data.m_process_pid);
521     }
522 
523     WriteCommandsForSourcing(eCommandPlacementAfterFile, commands_stream);
524   } else if (!m_option_data.m_after_file_commands.empty()) {
525     // We're in repl mode and after-file-load commands were specified.
526     WithColor::warning() << "commands specified to run after file load (via -o "
527                             "or -s) are ignored in REPL mode.\n";
528   }
529 
530   const bool handle_events = true;
531   const bool spawn_thread = false;
532 
533   // Check if we have any data in the commands stream, and if so, save it to a
534   // temp file
535   // so we can then run the command interpreter using the file contents.
536   bool go_interactive = true;
537   if ((commands_stream.GetData() != nullptr) &&
538       (commands_stream.GetSize() != 0u)) {
539     SBError error = m_debugger.SetInputString(commands_stream.GetData());
540     if (error.Fail()) {
541       WithColor::error() << error.GetCString() << '\n';
542       return 1;
543     }
544 
545     // Set the debugger into Sync mode when running the command file. Otherwise
546     // command files that run the target won't run in a sensible way.
547     bool old_async = m_debugger.GetAsync();
548     m_debugger.SetAsync(false);
549 
550     SBCommandInterpreterRunOptions options;
551     options.SetAutoHandleEvents(true);
552     options.SetSpawnThread(false);
553     options.SetStopOnError(true);
554     options.SetStopOnCrash(m_option_data.m_batch);
555     options.SetEchoCommands(!m_option_data.m_source_quietly);
556 
557     SBCommandInterpreterRunResult results =
558         m_debugger.RunCommandInterpreter(options);
559     if (results.GetResult() == lldb::eCommandInterpreterResultQuitRequested)
560       go_interactive = false;
561     if (m_option_data.m_batch &&
562         results.GetResult() != lldb::eCommandInterpreterResultInferiorCrash)
563       go_interactive = false;
564 
565     // When running in batch mode and stopped because of an error, exit with a
566     // non-zero exit status.
567     if (m_option_data.m_batch &&
568         results.GetResult() == lldb::eCommandInterpreterResultCommandError)
569       return 1;
570 
571     if (m_option_data.m_batch &&
572         results.GetResult() == lldb::eCommandInterpreterResultInferiorCrash &&
573         !m_option_data.m_after_crash_commands.empty()) {
574       SBStream crash_commands_stream;
575       WriteCommandsForSourcing(eCommandPlacementAfterCrash,
576                                crash_commands_stream);
577       SBError error =
578           m_debugger.SetInputString(crash_commands_stream.GetData());
579       if (error.Success()) {
580         SBCommandInterpreterRunResult local_results =
581             m_debugger.RunCommandInterpreter(options);
582         if (local_results.GetResult() ==
583             lldb::eCommandInterpreterResultQuitRequested)
584           go_interactive = false;
585 
586         // When running in batch mode and an error occurred while sourcing
587         // the crash commands, exit with a non-zero exit status.
588         if (m_option_data.m_batch &&
589             local_results.GetResult() ==
590                 lldb::eCommandInterpreterResultCommandError)
591           return 1;
592       }
593     }
594     m_debugger.SetAsync(old_async);
595   }
596 
597   // Now set the input file handle to STDIN and run the command interpreter
598   // again in interactive mode or repl mode and let the debugger take ownership
599   // of stdin.
600   if (go_interactive) {
601     m_debugger.SetInputFileHandle(stdin, true);
602 
603     if (m_option_data.m_repl) {
604       const char *repl_options = nullptr;
605       if (!m_option_data.m_repl_options.empty())
606         repl_options = m_option_data.m_repl_options.c_str();
607       SBError error(
608           m_debugger.RunREPL(m_option_data.m_repl_lang, repl_options));
609       if (error.Fail()) {
610         const char *error_cstr = error.GetCString();
611         if ((error_cstr != nullptr) && (error_cstr[0] != 0))
612           WithColor::error() << error_cstr << '\n';
613         else
614           WithColor::error() << error.GetError() << '\n';
615       }
616     } else {
617       m_debugger.RunCommandInterpreter(handle_events, spawn_thread);
618     }
619   }
620 
621   reset_stdin_termios();
622   fclose(stdin);
623 
624   return sb_interpreter.GetQuitStatus();
625 }
626 
ResizeWindow(unsigned short col)627 void Driver::ResizeWindow(unsigned short col) {
628   GetDebugger().SetTerminalWidth(col);
629 }
630 
sigwinch_handler(int signo)631 void sigwinch_handler(int signo) {
632   struct winsize window_size;
633   if ((isatty(STDIN_FILENO) != 0) &&
634       ::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {
635     if ((window_size.ws_col > 0) && g_driver != nullptr) {
636       g_driver->ResizeWindow(window_size.ws_col);
637     }
638   }
639 }
640 
sigint_handler(int signo)641 void sigint_handler(int signo) {
642 #ifdef _WIN32 // Restore handler as it is not persistent on Windows
643   signal(SIGINT, sigint_handler);
644 #endif
645   static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT;
646   if (g_driver != nullptr) {
647     if (!g_interrupt_sent.test_and_set()) {
648       g_driver->GetDebugger().DispatchInputInterrupt();
649       g_interrupt_sent.clear();
650       return;
651     }
652   }
653 
654   _exit(signo);
655 }
656 
657 #ifndef _WIN32
sigtstp_handler(int signo)658 static void sigtstp_handler(int signo) {
659   if (g_driver != nullptr)
660     g_driver->GetDebugger().SaveInputTerminalState();
661 
662   // Unblock the signal and remove our handler.
663   sigset_t set;
664   sigemptyset(&set);
665   sigaddset(&set, signo);
666   pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
667   signal(signo, SIG_DFL);
668 
669   // Now re-raise the signal. We will immediately suspend...
670   raise(signo);
671   // ... and resume after a SIGCONT.
672 
673   // Now undo the modifications.
674   pthread_sigmask(SIG_BLOCK, &set, nullptr);
675   signal(signo, sigtstp_handler);
676 
677   if (g_driver != nullptr)
678     g_driver->GetDebugger().RestoreInputTerminalState();
679 }
680 #endif
681 
printHelp(LLDBOptTable & table,llvm::StringRef tool_name)682 static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) {
683   std::string usage_str = tool_name.str() + " [options]";
684   table.printHelp(llvm::outs(), usage_str.c_str(), "LLDB", false);
685 
686   std::string examples = R"___(
687 EXAMPLES:
688   The debugger can be started in several modes.
689 
690   Passing an executable as a positional argument prepares lldb to debug the
691   given executable. To disambiguate between arguments passed to lldb and
692   arguments passed to the debugged executable, arguments starting with a - must
693   be passed after --.
694 
695     lldb --arch x86_64 /path/to/program program argument -- --arch armv7
696 
697   For convenience, passing the executable after -- is also supported.
698 
699     lldb --arch x86_64 -- /path/to/program program argument --arch armv7
700 
701   Passing one of the attach options causes lldb to immediately attach to the
702   given process.
703 
704     lldb -p <pid>
705     lldb -n <process-name>
706 
707   Passing --repl starts lldb in REPL mode.
708 
709     lldb -r
710 
711   Passing --core causes lldb to debug the core file.
712 
713     lldb -c /path/to/core
714 
715   Command options can be combined with these modes and cause lldb to run the
716   specified commands before or after events, like loading the file or crashing,
717   in the order provided on the command line.
718 
719     lldb -O 'settings set stop-disassembly-count 20' -o 'run' -o 'bt'
720     lldb -S /source/before/file -s /source/after/file
721     lldb -K /source/before/crash -k /source/after/crash
722 
723   Note: In REPL mode no file is loaded, so commands specified to run after
724   loading the file (via -o or -s) will be ignored.)___";
725   llvm::outs() << examples << '\n';
726 }
727 
main(int argc,char const * argv[])728 int main(int argc, char const *argv[]) {
729   // Editline uses for example iswprint which is dependent on LC_CTYPE.
730   std::setlocale(LC_ALL, "");
731   std::setlocale(LC_CTYPE, "");
732 
733   // Setup LLVM signal handlers and make sure we call llvm_shutdown() on
734   // destruction.
735   llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
736 #if !defined(__APPLE__)
737   llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL
738                         " and include the crash backtrace.\n");
739 #else
740   llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL
741                         " and include the crash report from "
742                         "~/Library/Logs/DiagnosticReports/.\n");
743 #endif
744 
745   // Parse arguments.
746   LLDBOptTable T;
747   unsigned MissingArgIndex;
748   unsigned MissingArgCount;
749   ArrayRef<const char *> arg_arr = ArrayRef(argv + 1, argc - 1);
750   opt::InputArgList input_args =
751       T.ParseArgs(arg_arr, MissingArgIndex, MissingArgCount);
752   llvm::StringRef argv0 = llvm::sys::path::filename(argv[0]);
753 
754   if (input_args.hasArg(OPT_help)) {
755     printHelp(T, argv0);
756     return 0;
757   }
758 
759   // Check for missing argument error.
760   if (MissingArgCount) {
761     WithColor::error() << "argument to '"
762                        << input_args.getArgString(MissingArgIndex)
763                        << "' is missing\n";
764   }
765   // Error out on unknown options.
766   if (input_args.hasArg(OPT_UNKNOWN)) {
767     for (auto *arg : input_args.filtered(OPT_UNKNOWN)) {
768       WithColor::error() << "unknown option: " << arg->getSpelling() << '\n';
769     }
770   }
771   if (MissingArgCount || input_args.hasArg(OPT_UNKNOWN)) {
772     llvm::errs() << "Use '" << argv0
773                  << " --help' for a complete list of options.\n";
774     return 1;
775   }
776 
777   SBError error = SBDebugger::InitializeWithErrorHandling();
778   if (error.Fail()) {
779     WithColor::error() << "initialization failed: " << error.GetCString()
780                        << '\n';
781     return 1;
782   }
783 
784   // Setup LLDB signal handlers once the debugger has been initialized.
785   SBDebugger::PrintDiagnosticsOnError();
786 
787   signal(SIGINT, sigint_handler);
788 #if !defined(_WIN32)
789   signal(SIGPIPE, SIG_IGN);
790   signal(SIGWINCH, sigwinch_handler);
791   signal(SIGTSTP, sigtstp_handler);
792 #endif
793 
794   int exit_code = 0;
795   // Create a scope for driver so that the driver object will destroy itself
796   // before SBDebugger::Terminate() is called.
797   {
798     Driver driver;
799 
800     bool exiting = false;
801     SBError error(driver.ProcessArgs(input_args, exiting));
802     if (error.Fail()) {
803       exit_code = 1;
804       if (const char *error_cstr = error.GetCString())
805         WithColor::error() << error_cstr << '\n';
806     } else if (!exiting) {
807       exit_code = driver.MainLoop();
808     }
809   }
810 
811   // When terminating the debugger we have to wait on all the background tasks
812   // to complete, which can take a while. Print a message when this takes longer
813   // than 1 second.
814   {
815     std::future<void> future =
816         std::async(std::launch::async, []() { SBDebugger::Terminate(); });
817 
818     if (future.wait_for(std::chrono::seconds(1)) == std::future_status::timeout)
819       fprintf(stderr, "Waiting for background tasks to complete...\n");
820 
821     future.wait();
822   }
823 
824   return exit_code;
825 }
826