/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 | } |