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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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