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