Coverage Report

Created: 2023-09-30 09:22

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