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
51using namespace lldb;
52using namespace llvm;
53
54namespace {
55using namespace llvm::opt;
56
57enum 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
71static constexpr opt::OptTable::Info InfoTable[] = {
72#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
73#include "Options.inc"
74#undef OPTION
75};
76
77class LLDBOptTable : public opt::GenericOptTable {
78public:
79 LLDBOptTable() : opt::GenericOptTable(InfoTable) {}
80};
81} // namespace
82
83static void reset_stdin_termios();
84static bool g_old_stdin_termios_is_valid = false;
85static struct termios g_old_stdin_termios;
86
87static bool disable_color(const raw_ostream &OS) { return false; }
88
89static 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.
93static 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, termios_p: &g_old_stdin_termios);
97 }
98}
99
100Driver::Driver()
101 : SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(source_init_files: 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
108Driver::~Driver() {
109 SBDebugger::Destroy(debugger&: m_debugger);
110 g_driver = nullptr;
111}
112
113void 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(x: InitialCmdEntry(command, is_file));
133 else if (file.ResolveExecutableLocation()) {
134 char final_path[PATH_MAX];
135 file.GetPath(dst_path: final_path, dst_len: sizeof(final_path));
136 command_set->push_back(x: 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(x: InitialCmdEntry(command, is_file));
143}
144
145void 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(format: "command source -s %i '%s'\n",
166 static_cast<int>(source_quietly), command);
167 } else
168 strm.Printf(format: "%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.
177SBError 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(b: false);
187 m_debugger.SkipAppInitFiles(b: 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(b: true);
227 m_debugger.SkipAppInitFiles(b: true);
228 }
229
230 if (args.hasArg(OPT_local_lldbinit)) {
231 lldb::SBDebugger::SetInternalVariable(var_name: "target.load-cwd-lldbinit", value: "true",
232 debugger_instance_name: 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(dst_path: path, dst_len: sizeof(path));
243 m_option_data.m_args.emplace_back(args&: 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(script_language_name: 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(string: 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(dst_path: 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
416std::string EscapeString(std::string arg) {
417 std::string::size_type pos = 0;
418 while ((pos = arg.find_first_of(s: "\"\\", pos: pos)) != std::string::npos) {
419 arg.insert(pos: pos, n: 1, c: '\\');
420 pos += 2;
421 }
422 return '"' + arg + '"';
423}
424
425int Driver::MainLoop() {
426 if (::tcgetattr(STDIN_FILENO, termios_p: &g_old_stdin_termios) == 0) {
427 g_old_stdin_termios_is_valid = true;
428 atexit(func: 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, buf: nullptr);
436#endif
437 ::setbuf(stdout, buf: nullptr);
438
439 m_debugger.SetErrorFileHandle(stderr, transfer_ownership: false);
440 m_debugger.SetOutputFileHandle(stdout, transfer_ownership: false);
441 // Don't take ownership of STDIN yet...
442 m_debugger.SetInputFileHandle(stdin, transfer_ownership: 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, is_repl: 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(file: m_debugger.GetErrorFile());
465 result.PutOutput(file: 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(allow: 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(placement: eCommandPlacementBeforeFile, strm&: 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 arch_name_len: sizeof(arch_name)))
486 commands_stream.Printf(format: "target create --arch=%s %s", arch_name,
487 EscapeString(arg: m_option_data.m_args[0]).c_str());
488 else
489 commands_stream.Printf(format: "target create %s",
490 EscapeString(arg: m_option_data.m_args[0]).c_str());
491
492 if (!m_option_data.m_core_file.empty()) {
493 commands_stream.Printf(format: " --core %s",
494 EscapeString(arg: m_option_data.m_core_file).c_str());
495 }
496 commands_stream.Printf(format: "\n");
497
498 if (num_args > 1) {
499 commands_stream.Printf(format: "settings set -- target.run-args ");
500 for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx)
501 commands_stream.Printf(
502 format: " %s", EscapeString(arg: m_option_data.m_args[arg_idx]).c_str());
503 commands_stream.Printf(format: "\n");
504 }
505 } else if (!m_option_data.m_core_file.empty()) {
506 commands_stream.Printf(format: "target create --core %s\n",
507 EscapeString(arg: m_option_data.m_core_file).c_str());
508 } else if (!m_option_data.m_process_name.empty()) {
509 commands_stream.Printf(
510 format: "process attach --name %s",
511 EscapeString(arg: m_option_data.m_process_name).c_str());
512
513 if (m_option_data.m_wait_for)
514 commands_stream.Printf(format: " --waitfor");
515
516 commands_stream.Printf(format: "\n");
517
518 } else if (LLDB_INVALID_PROCESS_ID != m_option_data.m_process_pid) {
519 commands_stream.Printf(format: "process attach --pid %" PRIu64 "\n",
520 m_option_data.m_process_pid);
521 }
522
523 WriteCommandsForSourcing(placement: eCommandPlacementAfterFile, strm&: 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(placement: eCommandPlacementAfterCrash,
576 strm&: 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, transfer_ownership: 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(language: 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(auto_handle_events: handle_events, spawn_thread);
618 }
619 }
620
621 reset_stdin_termios();
622 fclose(stdin);
623
624 return sb_interpreter.GetQuitStatus();
625}
626
627void Driver::ResizeWindow(unsigned short col) {
628 GetDebugger().SetTerminalWidth(col);
629}
630
631void 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(col: window_size.ws_col);
637 }
638 }
639}
640
641void 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(status: signo);
655}
656
657#ifndef _WIN32
658static 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: &set);
665 sigaddset(set: &set, signo: signo);
666 pthread_sigmask(SIG_UNBLOCK, newmask: &set, oldmask: nullptr);
667 signal(sig: signo, SIG_DFL);
668
669 // Now re-raise the signal. We will immediately suspend...
670 raise(sig: signo);
671 // ... and resume after a SIGCONT.
672
673 // Now undo the modifications.
674 pthread_sigmask(SIG_BLOCK, newmask: &set, oldmask: nullptr);
675 signal(sig: signo, handler: sigtstp_handler);
676
677 if (g_driver != nullptr)
678 g_driver->GetDebugger().RestoreInputTerminalState();
679}
680#endif
681
682static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) {
683 std::string usage_str = tool_name.str() + " [options]";
684 table.printHelp(OS&: llvm::outs(), Usage: usage_str.c_str(), Title: "LLDB", ShowHidden: false);
685
686 std::string examples = R"___(
687EXAMPLES:
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
728int main(int argc, char const *argv[]) {
729 // Editline uses for example iswprint which is dependent on LC_CTYPE.
730 std::setlocale(LC_ALL, locale: "");
731 std::setlocale(LC_CTYPE, locale: "");
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 llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL
737 " and include the crash backtrace.\n");
738
739 // Parse arguments.
740 LLDBOptTable T;
741 unsigned MissingArgIndex;
742 unsigned MissingArgCount;
743 ArrayRef<const char *> arg_arr = ArrayRef(argv + 1, argc - 1);
744 opt::InputArgList input_args =
745 T.ParseArgs(Args: arg_arr, MissingArgIndex, MissingArgCount);
746 llvm::StringRef argv0 = llvm::sys::path::filename(path: argv[0]);
747
748 if (input_args.hasArg(OPT_help)) {
749 printHelp(table&: T, tool_name: argv0);
750 return 0;
751 }
752
753 // Check for missing argument error.
754 if (MissingArgCount) {
755 WithColor::error() << "argument to '"
756 << input_args.getArgString(Index: MissingArgIndex)
757 << "' is missing\n";
758 }
759 // Error out on unknown options.
760 if (input_args.hasArg(OPT_UNKNOWN)) {
761 for (auto *arg : input_args.filtered(OPT_UNKNOWN)) {
762 WithColor::error() << "unknown option: " << arg->getSpelling() << '\n';
763 }
764 }
765 if (MissingArgCount || input_args.hasArg(OPT_UNKNOWN)) {
766 llvm::errs() << "Use '" << argv0
767 << " --help' for a complete list of options.\n";
768 return 1;
769 }
770
771 SBError error = SBDebugger::InitializeWithErrorHandling();
772 if (error.Fail()) {
773 WithColor::error() << "initialization failed: " << error.GetCString()
774 << '\n';
775 return 1;
776 }
777
778 // Setup LLDB signal handlers once the debugger has been initialized.
779 SBDebugger::PrintDiagnosticsOnError();
780
781 signal(SIGINT, handler: sigint_handler);
782#if !defined(_WIN32)
783 signal(SIGPIPE, SIG_IGN);
784 signal(SIGWINCH, handler: sigwinch_handler);
785 signal(SIGTSTP, handler: sigtstp_handler);
786#endif
787
788 int exit_code = 0;
789 // Create a scope for driver so that the driver object will destroy itself
790 // before SBDebugger::Terminate() is called.
791 {
792 Driver driver;
793
794 bool exiting = false;
795 SBError error(driver.ProcessArgs(args: input_args, exiting));
796 if (error.Fail()) {
797 exit_code = 1;
798 if (const char *error_cstr = error.GetCString())
799 WithColor::error() << error_cstr << '\n';
800 } else if (!exiting) {
801 exit_code = driver.MainLoop();
802 }
803 }
804
805 // When terminating the debugger we have to wait on all the background tasks
806 // to complete, which can take a while. Print a message when this takes longer
807 // than 1 second.
808 {
809 std::future<void> future =
810 std::async(policy: std::launch::async, fn: []() { SBDebugger::Terminate(); });
811
812 if (future.wait_for(rel: std::chrono::seconds(1)) == std::future_status::timeout)
813 fprintf(stderr, format: "Waiting for background tasks to complete...\n");
814
815 future.wait();
816 }
817
818 return exit_code;
819}
820

source code of lldb/tools/driver/Driver.cpp