1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | #include "Driver.h" |
11 | |
12 | #include <stdio.h> |
13 | #include <string.h> |
14 | #include <stdlib.h> |
15 | #include <limits.h> |
16 | #include <fcntl.h> |
17 | |
18 | |
19 | #if defined(_WIN32) |
20 | #include <io.h> |
21 | #include <fcntl.h> |
22 | #elif defined(__ANDROID_NDK__) |
23 | #include <errno(*__errno_location ()).h> |
24 | #else |
25 | #include <unistd.h> |
26 | #endif |
27 | |
28 | #include <string> |
29 | |
30 | #include <thread> |
31 | #include "lldb/API/SBBreakpoint.h" |
32 | #include "lldb/API/SBCommandInterpreter.h" |
33 | #include "lldb/API/SBCommandReturnObject.h" |
34 | #include "lldb/API/SBCommunication.h" |
35 | #include "lldb/API/SBDebugger.h" |
36 | #include "lldb/API/SBEvent.h" |
37 | #include "lldb/API/SBHostOS.h" |
38 | #include "lldb/API/SBListener.h" |
39 | #include "lldb/API/SBStream.h" |
40 | #include "lldb/API/SBTarget.h" |
41 | #include "lldb/API/SBThread.h" |
42 | #include "lldb/API/SBProcess.h" |
43 | |
44 | #if !defined(__APPLE__) |
45 | #include "llvm/Support/DataTypes.h" |
46 | #endif |
47 | |
48 | using namespace lldb; |
49 | |
50 | static void reset_stdin_termios (); |
51 | static bool g_old_stdin_termios_is_valid = false; |
52 | static struct termios g_old_stdin_termios; |
53 | |
54 | static Driver *g_driver = NULL__null; |
55 | |
56 | |
57 | |
58 | static void |
59 | reset_stdin_termios () |
60 | { |
61 | if (g_old_stdin_termios_is_valid) |
62 | { |
63 | g_old_stdin_termios_is_valid = false; |
64 | ::tcsetattr (STDIN_FILENO0, TCSANOW0, &g_old_stdin_termios); |
65 | } |
66 | } |
67 | |
68 | typedef struct |
69 | { |
70 | uint32_t usage_mask; |
71 | |
72 | bool required; |
73 | const char * long_option; |
74 | int short_option; |
75 | int option_has_arg; |
76 | uint32_t completion_type; |
77 | lldb::CommandArgumentType argument_type; |
78 | const char * usage_text; |
79 | |
80 | } OptionDefinition; |
81 | |
82 | #define LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4) LLDB_OPT_SET_3(1U << 2)|LLDB_OPT_SET_4(1U << 3)|LLDB_OPT_SET_5(1U << 4) |
83 | #define LLDB_4_TO_5(1U << 3)|(1U << 4) LLDB_OPT_SET_4(1U << 3)|LLDB_OPT_SET_5(1U << 4) |
84 | |
85 | static OptionDefinition g_options[] = |
86 | { |
87 | { LLDB_OPT_SET_1(1U << 0), true , "help" , 'h', no_argument0 , 0, eArgTypeNone, |
88 | "Prints out the usage information for the LLDB debugger." }, |
89 | { LLDB_OPT_SET_2(1U << 1), true , "version" , 'v', no_argument0 , 0, eArgTypeNone, |
90 | "Prints out the current version number of the LLDB debugger." }, |
91 | { LLDB_OPT_SET_3(1U << 2), true , "arch" , 'a', required_argument1, 0, eArgTypeArchitecture, |
92 | "Tells the debugger to use the specified architecture when starting and running the program. <architecture> must " |
93 | "be one of the architectures for which the program was compiled." }, |
94 | { LLDB_OPT_SET_3(1U << 2), true , "file" , 'f', required_argument1, 0, eArgTypeFilename, |
95 | "Tells the debugger to use the file <filename> as the program to be debugged." }, |
96 | { LLDB_OPT_SET_3(1U << 2), false, "core" , 'c', required_argument1, 0, eArgTypeFilename, |
97 | "Tells the debugger to use the fullpath to <path> as the core file." }, |
98 | { LLDB_OPT_SET_5(1U << 4), true , "attach-pid" , 'p', required_argument1, 0, eArgTypePid, |
99 | "Tells the debugger to attach to a process with the given pid." }, |
100 | { LLDB_OPT_SET_4(1U << 3), true , "attach-name" , 'n', required_argument1, 0, eArgTypeProcessName, |
101 | "Tells the debugger to attach to a process with the given name." }, |
102 | { LLDB_OPT_SET_4(1U << 3), true , "wait-for" , 'w', no_argument0 , 0, eArgTypeNone, |
103 | "Tells the debugger to wait for a process with the given pid or name to launch before attaching." }, |
104 | { LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4), false, "source" , 's', required_argument1, 0, eArgTypeFilename, |
105 | "Tells the debugger to read in and execute the lldb commands in the given file, after any file provided on the command line has been loaded." }, |
106 | { LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4), false, "one-line" , 'o', required_argument1, 0, eArgTypeNone, |
107 | "Tells the debugger to execute this one-line lldb command after any file provided on the command line has been loaded." }, |
108 | { LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4), false, "source-before-file" , 'S', required_argument1, 0, eArgTypeFilename, |
109 | "Tells the debugger to read in and execute the lldb commands in the given file, before any file provided on the command line has been loaded." }, |
110 | { LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4), false, "one-line-before-file" , 'O', required_argument1, 0, eArgTypeNone, |
111 | "Tells the debugger to execute this one-line lldb command before any file provided on the command line has been loaded." }, |
112 | { LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4), false, "one-line-on-crash" , 'k', required_argument1, 0, eArgTypeNone, |
113 | "When in batch mode, tells the debugger to execute this one-line lldb command if the target crashes." }, |
114 | { LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4), false, "source-on-crash" , 'K', required_argument1, 0, eArgTypeFilename, |
115 | "When in batch mode, tells the debugger to source this file of lldb commands if the target crashes." }, |
116 | { LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4), false, "source-quietly" , 'Q', no_argument0 , 0, eArgTypeNone, |
117 | "Tells the debugger to execute this one-line lldb command before any file provided on the command line has been loaded." }, |
118 | { LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4), false, "batch" , 'b', no_argument0 , 0, eArgTypeNone, |
119 | "Tells the debugger to running the commands from -s, -S, -o & -O, and then quit. However if any run command stopped due to a signal or crash, " |
120 | "the debugger will return to the interactive prompt at the place of the crash." }, |
121 | { LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4), false, "editor" , 'e', no_argument0 , 0, eArgTypeNone, |
122 | "Tells the debugger to open source files using the host's \"external editor\" mechanism." }, |
123 | { LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4), false, "no-lldbinit" , 'x', no_argument0 , 0, eArgTypeNone, |
124 | "Do not automatically parse any '.lldbinit' files." }, |
125 | { LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4), false, "no-use-colors" , 'X', no_argument0 , 0, eArgTypeNone, |
126 | "Do not use colors." }, |
127 | { LLDB_OPT_SET_6(1U << 5), true , "python-path" , 'P', no_argument0 , 0, eArgTypeNone, |
128 | "Prints out the path to the lldb.py file for this version of lldb." }, |
129 | { LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4), false, "script-language", 'l', required_argument1, 0, eArgTypeScriptLang, |
130 | "Tells the debugger to use the specified scripting language for user-defined scripts, rather than the default. " |
131 | "Valid scripting languages that can be specified include Python, Perl, Ruby and Tcl. Currently only the Python " |
132 | "extensions have been implemented." }, |
133 | { LLDB_3_TO_5(1U << 2)|(1U << 3)|(1U << 4), false, "debug" , 'd', no_argument0 , 0, eArgTypeNone, |
134 | "Tells the debugger to print out extra information for debugging itself." }, |
135 | { 0, false, NULL__null , 0 , 0 , 0, eArgTypeNone, NULL__null } |
136 | }; |
137 | |
138 | static const uint32_t last_option_set_with_args = 2; |
139 | |
140 | Driver::Driver () : |
141 | SBBroadcaster ("Driver"), |
142 | m_debugger (SBDebugger::Create(false)), |
143 | m_option_data () |
144 | { |
145 | |
146 | |
147 | m_debugger.SetCloseInputOnEOF (false); |
148 | g_driver = this; |
149 | } |
150 | |
151 | Driver::~Driver () |
152 | { |
153 | g_driver = NULL__null; |
154 | } |
155 | |
156 | |
157 | |
158 | |
159 | |
160 | |
161 | |
162 | |
163 | |
164 | void |
165 | OutputFormattedUsageText (FILE *out, int indent, const char *text, int output_max_columns) |
166 | { |
167 | int len = strlen (text); |
168 | std::string text_string (text); |
169 | |
170 | |
171 | if (indent >= output_max_columns) |
172 | indent = 0; |
173 | |
174 | |
175 | |
176 | if (len + indent < output_max_columns) |
177 | |
178 | fprintf (out, "%*s%s\n", indent, "", text); |
179 | else |
180 | { |
181 | |
182 | int text_width = output_max_columns - indent - 1; |
183 | int start = 0; |
184 | int end = start; |
185 | int final_end = len; |
186 | int sub_len; |
187 | |
188 | while (end < final_end) |
189 | { |
190 | |
191 | while ((start < final_end) && (text[start] == ' ')) |
192 | start++; |
193 | |
194 | end = start + text_width; |
195 | if (end > final_end) |
196 | end = final_end; |
197 | else |
198 | { |
199 | |
200 | while (end > start |
201 | && text[end] != ' ' && text[end] != '\t' && text[end] != '\n') |
202 | end--; |
203 | } |
204 | sub_len = end - start; |
205 | std::string substring = text_string.substr (start, sub_len); |
206 | fprintf (out, "%*s%s\n", indent, "", substring.c_str()); |
207 | start = end + 1; |
208 | } |
209 | } |
210 | } |
211 | |
212 | void |
213 | ShowUsage (FILE *out, OptionDefinition *option_table, Driver::OptionData data) |
214 | { |
215 | uint32_t screen_width = 80; |
216 | uint32_t indent_level = 0; |
217 | const char *name = "lldb"; |
218 | |
219 | fprintf (out, "\nUsage:\n\n"); |
220 | |
221 | indent_level += 2; |
222 | |
223 | |
224 | |
225 | |
226 | |
227 | |
228 | uint32_t num_options; |
229 | uint32_t num_option_sets = 0; |
230 | |
231 | for (num_options = 0; option_table[num_options].long_option != NULL__null; ++num_options) |
232 | { |
233 | uint32_t this_usage_mask = option_table[num_options].usage_mask; |
234 | if (this_usage_mask == LLDB_OPT_SET_ALL0xFFFFFFFFU) |
235 | { |
236 | if (num_option_sets == 0) |
237 | num_option_sets = 1; |
238 | } |
239 | else |
240 | { |
241 | for (uint32_t j = 0; j < LLDB_MAX_NUM_OPTION_SETS32; j++) |
242 | { |
243 | if (this_usage_mask & 1 << j) |
244 | { |
245 | if (num_option_sets <= j) |
246 | num_option_sets = j + 1; |
247 | } |
248 | } |
249 | } |
250 | } |
251 | |
252 | for (uint32_t opt_set = 0; opt_set < num_option_sets; opt_set++) |
253 | { |
254 | uint32_t opt_set_mask; |
255 | |
256 | opt_set_mask = 1 << opt_set; |
257 | |
258 | if (opt_set > 0) |
259 | fprintf (out, "\n"); |
260 | fprintf (out, "%*s%s", indent_level, "", name); |
261 | bool is_help_line = false; |
262 | |
263 | for (uint32_t i = 0; i < num_options; ++i) |
264 | { |
265 | if (option_table[i].usage_mask & opt_set_mask) |
266 | { |
267 | CommandArgumentType arg_type = option_table[i].argument_type; |
268 | const char *arg_name = SBCommandInterpreter::GetArgumentTypeAsCString (arg_type); |
269 | |
270 | |
271 | if (option_table[i].short_option == 'h') |
272 | is_help_line = true; |
273 | |
274 | if (option_table[i].required) |
275 | { |
276 | if (option_table[i].option_has_arg == required_argument1) |
277 | fprintf (out, " -%c <%s>", option_table[i].short_option, arg_name); |
278 | else if (option_table[i].option_has_arg == optional_argument2) |
279 | fprintf (out, " -%c [<%s>]", option_table[i].short_option, arg_name); |
280 | else |
281 | fprintf (out, " -%c", option_table[i].short_option); |
282 | } |
283 | else |
284 | { |
285 | if (option_table[i].option_has_arg == required_argument1) |
286 | fprintf (out, " [-%c <%s>]", option_table[i].short_option, arg_name); |
287 | else if (option_table[i].option_has_arg == optional_argument2) |
288 | fprintf (out, " [-%c [<%s>]]", option_table[i].short_option, arg_name); |
289 | else |
290 | fprintf (out, " [-%c]", option_table[i].short_option); |
291 | } |
292 | } |
293 | } |
294 | if (!is_help_line && (opt_set <= last_option_set_with_args)) |
295 | fprintf (out, " [[--] <PROGRAM-ARG-1> [<PROGRAM_ARG-2> ...]]"); |
296 | } |
297 | |
298 | fprintf (out, "\n\n"); |
299 | |
300 | |
301 | |
302 | |
303 | |
304 | |
305 | |
306 | |
307 | |
308 | Driver::OptionData::OptionSet options_seen; |
309 | Driver::OptionData::OptionSet::iterator pos; |
310 | |
311 | indent_level += 5; |
312 | |
313 | for (uint32_t i = 0; i < num_options; ++i) |
314 | { |
315 | |
316 | pos = options_seen.find (option_table[i].short_option); |
317 | if (pos == options_seen.end()) |
318 | { |
319 | CommandArgumentType arg_type = option_table[i].argument_type; |
320 | const char *arg_name = SBCommandInterpreter::GetArgumentTypeAsCString (arg_type); |
321 | |
322 | options_seen.insert (option_table[i].short_option); |
323 | fprintf (out, "%*s-%c ", indent_level, "", option_table[i].short_option); |
324 | if (arg_type != eArgTypeNone) |
325 | fprintf (out, "<%s>", arg_name); |
326 | fprintf (out, "\n"); |
327 | fprintf (out, "%*s--%s ", indent_level, "", option_table[i].long_option); |
328 | if (arg_type != eArgTypeNone) |
329 | fprintf (out, "<%s>", arg_name); |
330 | fprintf (out, "\n"); |
331 | indent_level += 5; |
332 | OutputFormattedUsageText (out, indent_level, option_table[i].usage_text, screen_width); |
333 | indent_level -= 5; |
334 | fprintf (out, "\n"); |
335 | } |
336 | } |
337 | |
338 | indent_level -= 5; |
339 | |
340 | fprintf (out, "\n%*sNotes:\n", |
341 | indent_level, ""); |
342 | indent_level += 5; |
343 | |
344 | fprintf (out, "\n%*sMultiple \"-s\" and \"-o\" options can be provided. They will be processed from left to right in order, " |
345 | "\n%*swith the source files and commands interleaved. The same is true of the \"-S\" and \"-O\" options." |
346 | "\n%*sThe before file and after file sets can intermixed freely, the command parser will sort them out." |
347 | "\n%*sThe order of the file specifiers (\"-c\", \"-f\", etc.) is not significant in this regard.\n\n", |
348 | indent_level, "", |
349 | indent_level, "", |
350 | indent_level, "", |
351 | indent_level, ""); |
352 | |
353 | fprintf (out, "\n%*sIf you don't provide -f then the first argument will be the file to be debugged" |
354 | "\n%*swhich means that '%s -- <filename> [<ARG1> [<ARG2>]]' also works." |
355 | "\n%*sBut remember to end the options with \"--\" if any of your arguments have a \"-\" in them.\n\n", |
356 | indent_level, "", |
357 | indent_level, "", |
358 | name, |
359 | indent_level, ""); |
360 | } |
361 | |
362 | void |
363 | BuildGetOptTable (OptionDefinition *expanded_option_table, std::vector<struct option> &getopt_table, |
364 | uint32_t num_options) |
365 | { |
366 | if (num_options == 0) |
367 | return; |
368 | |
369 | uint32_t i; |
370 | uint32_t j; |
371 | std::bitset<256> option_seen; |
372 | |
373 | getopt_table.resize (num_options + 1); |
374 | |
375 | for (i = 0, j = 0; i < num_options; ++i) |
376 | { |
377 | char short_opt = expanded_option_table[i].short_option; |
378 | |
379 | if (option_seen.test(short_opt) == false) |
380 | { |
381 | getopt_table[j].name = expanded_option_table[i].long_option; |
382 | getopt_table[j].has_arg = expanded_option_table[i].option_has_arg; |
383 | getopt_table[j].flag = NULL__null; |
384 | getopt_table[j].val = expanded_option_table[i].short_option; |
385 | option_seen.set(short_opt); |
386 | ++j; |
387 | } |
388 | } |
389 | |
390 | getopt_table[j].name = NULL__null; |
391 | getopt_table[j].has_arg = 0; |
392 | getopt_table[j].flag = NULL__null; |
393 | getopt_table[j].val = 0; |
394 | |
395 | } |
396 | |
397 | Driver::OptionData::OptionData () : |
398 | m_args(), |
399 | m_script_lang (lldb::eScriptLanguageDefault), |
400 | m_core_file (), |
401 | m_crash_log (), |
402 | m_initial_commands (), |
403 | m_after_file_commands (), |
404 | m_after_crash_commands(), |
405 | m_debug_mode (false), |
406 | m_source_quietly(false), |
407 | m_print_version (false), |
408 | m_print_python_path (false), |
409 | m_print_help (false), |
410 | m_wait_for(false), |
411 | m_process_name(), |
412 | m_process_pid(LLDB_INVALID_PROCESS_ID0), |
413 | m_use_external_editor(false), |
414 | m_batch(false), |
415 | m_seen_options() |
416 | { |
417 | } |
418 | |
419 | Driver::OptionData::~OptionData () |
420 | { |
421 | } |
422 | |
423 | void |
424 | Driver::OptionData::Clear () |
425 | { |
426 | m_args.clear (); |
427 | m_script_lang = lldb::eScriptLanguageDefault; |
428 | m_initial_commands.clear (); |
429 | m_after_file_commands.clear (); |
430 | |
431 | SBFileSpec local_lldbinit("./.lldbinit", true); |
432 | if (local_lldbinit.Exists()) |
433 | { |
434 | char path[2048]; |
435 | local_lldbinit.GetPath(path, 2047); |
436 | InitialCmdEntry entry(path, true, true); |
437 | m_after_file_commands.push_back (entry); |
438 | } |
439 | |
440 | m_debug_mode = false; |
441 | m_source_quietly = false; |
442 | m_print_help = false; |
443 | m_print_version = false; |
444 | m_print_python_path = false; |
445 | m_use_external_editor = false; |
446 | m_wait_for = false; |
447 | m_process_name.erase(); |
448 | m_batch = false; |
449 | m_after_crash_commands.clear(); |
450 | |
451 | m_process_pid = LLDB_INVALID_PROCESS_ID0; |
452 | } |
453 | |
454 | void |
455 | Driver::OptionData::AddInitialCommand (const char *command, CommandPlacement placement, bool is_file, bool silent, SBError &error) |
456 | { |
457 | std::vector<InitialCmdEntry> *command_set; |
458 | switch (placement) |
459 | { |
460 | case eCommandPlacementBeforeFile: |
461 | command_set = &(m_initial_commands); |
462 | break; |
463 | case eCommandPlacementAfterFile: |
464 | command_set = &(m_after_file_commands); |
465 | break; |
466 | case eCommandPlacementAfterCrash: |
467 | command_set = &(m_after_crash_commands); |
468 | break; |
469 | } |
470 | |
471 | if (is_file) |
472 | { |
473 | SBFileSpec file(command); |
474 | if (file.Exists()) |
475 | command_set->push_back (InitialCmdEntry(command, is_file, silent)); |
476 | else if (file.ResolveExecutableLocation()) |
477 | { |
478 | char final_path[PATH_MAX4096]; |
479 | file.GetPath (final_path, sizeof(final_path)); |
480 | command_set->push_back (InitialCmdEntry(final_path, is_file, silent)); |
481 | } |
482 | else |
483 | error.SetErrorStringWithFormat("file specified in --source (-s) option doesn't exist: '%s'", optarg); |
484 | } |
485 | else |
486 | command_set->push_back (InitialCmdEntry(command, is_file, silent)); |
487 | } |
488 | |
489 | void |
490 | Driver::ResetOptionValues () |
491 | { |
492 | m_option_data.Clear (); |
493 | } |
494 | |
495 | const char * |
496 | Driver::GetFilename() const |
497 | { |
498 | if (m_option_data.m_args.empty()) |
499 | return NULL__null; |
500 | return m_option_data.m_args.front().c_str(); |
501 | } |
502 | |
503 | const char * |
504 | Driver::GetCrashLogFilename() const |
505 | { |
506 | if (m_option_data.m_crash_log.empty()) |
507 | return NULL__null; |
508 | return m_option_data.m_crash_log.c_str(); |
509 | } |
510 | |
511 | lldb::ScriptLanguage |
512 | Driver::GetScriptLanguage() const |
513 | { |
514 | return m_option_data.m_script_lang; |
515 | } |
516 | |
517 | void |
518 | Driver::WriteCommandsForSourcing (CommandPlacement placement, SBStream &strm) |
519 | { |
520 | std::vector<OptionData::InitialCmdEntry> *command_set; |
521 | switch (placement) |
522 | { |
523 | case eCommandPlacementBeforeFile: |
524 | command_set = &m_option_data.m_initial_commands; |
525 | break; |
526 | case eCommandPlacementAfterFile: |
527 | command_set = &m_option_data.m_after_file_commands; |
528 | break; |
529 | case eCommandPlacementAfterCrash: |
530 | command_set = &m_option_data.m_after_crash_commands; |
531 | break; |
532 | } |
533 | |
534 | for (const auto &command_entry : *command_set) |
535 | { |
536 | const char *command = command_entry.contents.c_str(); |
537 | if (command_entry.is_file) |
538 | { |
539 | bool source_quietly = m_option_data.m_source_quietly || command_entry.source_quietly; |
540 | strm.Printf("command source -s %i '%s'\n", source_quietly, command); |
541 | } |
542 | else |
543 | strm.Printf("%s\n", command); |
544 | } |
545 | } |
546 | |
547 | bool |
548 | Driver::GetDebugMode() const |
549 | { |
550 | return m_option_data.m_debug_mode; |
551 | } |
552 | |
553 | |
554 | |
555 | |
556 | |
557 | |
558 | |
559 | SBError |
560 | Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exiting) |
561 | { |
562 | ResetOptionValues (); |
563 | |
564 | SBCommandReturnObject result; |
565 | |
566 | SBError error; |
567 | std::string option_string; |
568 | struct option *long_options = NULL__null; |
569 | std::vector<struct option> long_options_vector; |
570 | uint32_t num_options; |
571 | |
572 | for (num_options = 0; g_options[num_options].long_option != NULL__null; ++num_options) |
573 | ; |
574 | |
575 | if (num_options == 0) |
576 | { |
577 | if (argc > 1) |
578 | error.SetErrorStringWithFormat ("invalid number of options"); |
579 | return error; |
580 | } |
581 | |
582 | BuildGetOptTable (g_options, long_options_vector, num_options); |
583 | |
584 | if (long_options_vector.empty()) |
585 | long_options = NULL__null; |
586 | else |
587 | long_options = &long_options_vector.front(); |
588 | |
589 | if (long_options == NULL__null) |
590 | { |
591 | error.SetErrorStringWithFormat ("invalid long options"); |
592 | return error; |
593 | } |
594 | |
595 | |
596 | |
597 | for (int i = 0; long_options[i].name != NULL__null; ++i) |
598 | { |
599 | if (long_options[i].flag == NULL__null) |
600 | { |
601 | option_string.push_back ((char) long_options[i].val); |
602 | switch (long_options[i].has_arg) |
603 | { |
604 | default: |
605 | case no_argument0: |
606 | break; |
607 | case required_argument1: |
608 | option_string.push_back (':'); |
609 | break; |
610 | case optional_argument2: |
611 | option_string.append ("::"); |
612 | break; |
613 | } |
614 | } |
615 | } |
616 | |
617 | |
618 | |
619 | |
620 | |
621 | |
622 | |
623 | m_debugger.SkipLLDBInitFiles (false); |
624 | m_debugger.SkipAppInitFiles (false); |
625 | |
626 | |
627 | #if __GLIBC__2 |
628 | optind = 0; |
629 | #else |
630 | optreset = 1; |
631 | optind = 1; |
632 | #endif |
633 | int val; |
634 | while (1) |
635 | { |
636 | int long_options_index = -1; |
637 | val = ::getopt_long_only (argc, const_cast<char **>(argv), option_string.c_str(), long_options, &long_options_index); |
638 | |
639 | if (val == -1) |
640 | break; |
641 | else if (val == '?') |
642 | { |
643 | m_option_data.m_print_help = true; |
644 | error.SetErrorStringWithFormat ("unknown or ambiguous option"); |
645 | break; |
646 | } |
647 | else if (val == 0) |
648 | continue; |
649 | else |
650 | { |
651 | m_option_data.m_seen_options.insert ((char) val); |
652 | if (long_options_index == -1) |
653 | { |
654 | for (int i = 0; |
655 | long_options[i].name || long_options[i].has_arg || long_options[i].flag || long_options[i].val; |
656 | ++i) |
657 | { |
658 | if (long_options[i].val == val) |
659 | { |
660 | long_options_index = i; |
661 | break; |
662 | } |
663 | } |
664 | } |
665 | |
666 | if (long_options_index >= 0) |
667 | { |
668 | const int short_option = g_options[long_options_index].short_option; |
669 | |
670 | switch (short_option) |
671 | { |
672 | case 'h': |
673 | m_option_data.m_print_help = true; |
674 | break; |
675 | |
676 | case 'v': |
677 | m_option_data.m_print_version = true; |
678 | break; |
679 | |
680 | case 'P': |
681 | m_option_data.m_print_python_path = true; |
682 | break; |
683 | |
684 | case 'b': |
685 | m_option_data.m_batch = true; |
686 | break; |
687 | |
688 | case 'c': |
689 | { |
690 | SBFileSpec file(optarg); |
691 | if (file.Exists()) |
692 | { |
693 | m_option_data.m_core_file = optarg; |
694 | } |
695 | else |
696 | error.SetErrorStringWithFormat("file specified in --core (-c) option doesn't exist: '%s'", optarg); |
697 | } |
698 | break; |
699 | |
700 | case 'e': |
701 | m_option_data.m_use_external_editor = true; |
702 | break; |
703 | |
704 | case 'x': |
705 | m_debugger.SkipLLDBInitFiles (true); |
706 | m_debugger.SkipAppInitFiles (true); |
707 | break; |
708 | |
709 | case 'X': |
710 | m_debugger.SetUseColor (false); |
711 | break; |
712 | |
713 | case 'f': |
714 | { |
715 | SBFileSpec file(optarg); |
716 | if (file.Exists()) |
717 | { |
718 | m_option_data.m_args.push_back (optarg); |
719 | } |
720 | else if (file.ResolveExecutableLocation()) |
721 | { |
722 | char path[PATH_MAX4096]; |
723 | file.GetPath (path, sizeof(path)); |
724 | m_option_data.m_args.push_back (path); |
725 | } |
726 | else |
727 | error.SetErrorStringWithFormat("file specified in --file (-f) option doesn't exist: '%s'", optarg); |
728 | } |
729 | break; |
730 | |
731 | case 'a': |
732 | if (!m_debugger.SetDefaultArchitecture (optarg)) |
733 | error.SetErrorStringWithFormat("invalid architecture in the -a or --arch option: '%s'", optarg); |
734 | break; |
735 | |
736 | case 'l': |
737 | m_option_data.m_script_lang = m_debugger.GetScriptingLanguage (optarg); |
738 | break; |
739 | |
740 | case 'd': |
741 | m_option_data.m_debug_mode = true; |
742 | break; |
743 | |
744 | case 'Q': |
745 | m_option_data.m_source_quietly = true; |
746 | break; |
747 | |
748 | case 'K': |
749 | m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterCrash, true, true, error); |
750 | break; |
751 | case 'k': |
752 | m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterCrash, false, true, error); |
753 | break; |
754 | |
755 | case 'n': |
756 | m_option_data.m_process_name = optarg; |
757 | break; |
758 | |
759 | case 'w': |
760 | m_option_data.m_wait_for = true; |
761 | break; |
762 | |
763 | case 'p': |
764 | { |
765 | char *remainder; |
766 | m_option_data.m_process_pid = strtol (optarg, &remainder, 0); |
767 | if (remainder == optarg || *remainder != '\0') |
768 | error.SetErrorStringWithFormat ("Could not convert process PID: \"%s\" into a pid.", |
769 | optarg); |
770 | } |
771 | break; |
772 | case 's': |
773 | m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterFile, true, true, error); |
774 | break; |
775 | case 'o': |
776 | m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterFile, false, true, error); |
777 | break; |
778 | case 'S': |
779 | m_option_data.AddInitialCommand(optarg, eCommandPlacementBeforeFile, true, true, error); |
780 | break; |
781 | case 'O': |
782 | m_option_data.AddInitialCommand(optarg, eCommandPlacementBeforeFile, false, true, error); |
783 | break; |
784 | default: |
785 | m_option_data.m_print_help = true; |
786 | error.SetErrorStringWithFormat ("unrecognized option %c", short_option); |
787 | break; |
788 | } |
789 | } |
790 | else |
791 | { |
792 | error.SetErrorStringWithFormat ("invalid option with value %i", val); |
793 | } |
794 | if (error.Fail()) |
795 | { |
796 | return error; |
797 | } |
798 | } |
799 | } |
800 | |
801 | if (error.Fail() || m_option_data.m_print_help) |
802 | { |
803 | ShowUsage (out_fh, g_options, m_option_data); |
804 | exiting = true; |
805 | } |
806 | else if (m_option_data.m_print_version) |
807 | { |
808 | ::fprintf (out_fh, "%s\n", m_debugger.GetVersionString()); |
809 | exiting = true; |
810 | } |
811 | else if (m_option_data.m_print_python_path) |
812 | { |
813 | SBFileSpec python_file_spec = SBHostOS::GetLLDBPythonPath(); |
814 | if (python_file_spec.IsValid()) |
815 | { |
816 | char python_path[PATH_MAX4096]; |
817 | size_t num_chars = python_file_spec.GetPath(python_path, PATH_MAX4096); |
818 | if (num_chars < PATH_MAX4096) |
819 | { |
820 | ::fprintf (out_fh, "%s\n", python_path); |
821 | } |
822 | else |
823 | ::fprintf (out_fh, "<PATH TOO LONG>\n"); |
824 | } |
825 | else |
826 | ::fprintf (out_fh, "<COULD NOT FIND PATH>\n"); |
827 | exiting = true; |
828 | } |
829 | else if (m_option_data.m_process_name.empty() && m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID0) |
830 | { |
831 | |
832 | |
833 | |
834 | |
835 | |
836 | |
837 | |
838 | argc -= optind; |
839 | argv += optind; |
840 | |
841 | if (argc > 0) |
842 | { |
843 | for (int arg_idx=0; arg_idx<argc; ++arg_idx) |
844 | { |
845 | const char *arg = argv[arg_idx]; |
846 | if (arg) |
847 | m_option_data.m_args.push_back (arg); |
848 | } |
849 | } |
850 | |
851 | } |
852 | else |
853 | { |
854 | |
855 | argc -= optind; |
856 | |
857 | |
858 | if (argc > 0) |
859 | ::fprintf (out_fh, "Warning: program arguments are ignored when attaching.\n"); |
860 | } |
861 | |
862 | return error; |
863 | } |
864 | |
865 | static ::FILE * |
866 | PrepareCommandsForSourcing (const char *commands_data, size_t commands_size, int fds[2]) |
867 | { |
868 | enum PIPES { READ, WRITE }; |
869 | |
870 | bool success = true; |
871 | ::FILE *commands_file = NULL__null; |
872 | fds[0] = -1; |
873 | fds[1] = -1; |
874 | int err = 0; |
875 | #ifdef _WIN32 |
876 | err = _pipe(fds, commands_size, O_BINARY); |
877 | #else |
878 | err = pipe(fds); |
879 | #endif |
880 | if (err == 0) |
881 | { |
882 | ssize_t nrwr = write(fds[WRITE], commands_data, commands_size); |
883 | if (nrwr < 0) |
884 | { |
885 | fprintf(stderrstderr, "error: write(%i, %p, %" PRIu64"l" "u" ") failed (errno = %i) " |
886 | "when trying to open LLDB commands pipe\n", |
887 | fds[WRITE], commands_data, static_cast<uint64_t>(commands_size), errno(*__errno_location ())); |
888 | success = false; |
889 | } |
890 | else if (static_cast<size_t>(nrwr) == commands_size) |
891 | { |
892 | |
893 | |
894 | |
895 | #ifdef _WIN32 |
896 | _close(fds[WRITE]); fds[WRITE] = -1; |
897 | #else |
898 | close(fds[WRITE]); fds[WRITE] = -1; |
899 | #endif |
900 | |
901 | |
902 | commands_file = fdopen(fds[READ], "r"); |
903 | if (commands_file) |
904 | { |
905 | fds[READ] = -1; |
906 | |
907 | } |
908 | else |
909 | { |
910 | fprintf(stderrstderr, |
911 | "error: fdopen(%i, \"r\") failed (errno = %i) when " |
912 | "trying to open LLDB commands pipe\n", |
913 | fds[READ], errno(*__errno_location ())); |
914 | success = false; |
915 | } |
916 | } |
917 | } |
918 | else |
919 | { |
920 | fprintf(stderrstderr, "error: can't create pipe file descriptors for LLDB commands\n"); |
921 | success = false; |
| Value stored to 'success' is never read |
922 | } |
923 | |
924 | return commands_file; |
925 | } |
926 | |
927 | void |
928 | CleanupAfterCommandSourcing (int fds[2]) |
929 | { |
930 | enum PIPES { READ, WRITE }; |
931 | |
932 | |
933 | if ( fds[WRITE] != -1) |
934 | { |
935 | #ifdef _WIN32 |
936 | _close(fds[WRITE]); fds[WRITE] = -1; |
937 | #else |
938 | close(fds[WRITE]); fds[WRITE] = -1; |
939 | #endif |
940 | |
941 | } |
942 | |
943 | if ( fds[READ] != -1) |
944 | { |
945 | #ifdef _WIN32 |
946 | _close(fds[READ]); fds[READ] = -1; |
947 | #else |
948 | close(fds[READ]); fds[READ] = -1; |
949 | #endif |
950 | } |
951 | |
952 | } |
953 | |
954 | std::string |
955 | EscapeString (std::string arg) |
956 | { |
957 | std::string::size_type pos = 0; |
958 | while ((pos = arg.find_first_of("\"\\", pos)) != std::string::npos) |
959 | { |
960 | arg.insert (pos, 1, '\\'); |
961 | pos += 2; |
962 | } |
963 | return '"' + arg + '"'; |
964 | } |
965 | |
966 | void |
967 | Driver::MainLoop () |
968 | { |
969 | if (::tcgetattr(STDIN_FILENO0, &g_old_stdin_termios) == 0) |
970 | { |
971 | g_old_stdin_termios_is_valid = true; |
972 | atexit (reset_stdin_termios); |
973 | } |
974 | |
975 | ::setbuf (stdinstdin, NULL__null); |
976 | ::setbuf (stdoutstdout, NULL__null); |
977 | |
978 | m_debugger.SetErrorFileHandle (stderrstderr, false); |
979 | m_debugger.SetOutputFileHandle (stdoutstdout, false); |
980 | m_debugger.SetInputFileHandle (stdinstdin, false); |
981 | |
982 | m_debugger.SetUseExternalEditor(m_option_data.m_use_external_editor); |
983 | |
984 | struct winsize window_size; |
985 | if (isatty (STDIN_FILENO0) |
986 | && ::ioctl (STDIN_FILENO0, TIOCGWINSZ0x5413, &window_size) == 0) |
987 | { |
988 | if (window_size.ws_col > 0) |
989 | m_debugger.SetTerminalWidth (window_size.ws_col); |
990 | } |
991 | |
992 | SBCommandInterpreter sb_interpreter = m_debugger.GetCommandInterpreter(); |
993 | |
994 | |
995 | |
996 | SBCommandReturnObject result; |
997 | sb_interpreter.SourceInitFileInHomeDirectory(result); |
998 | if (GetDebugMode()) |
999 | { |
1000 | result.PutError (m_debugger.GetErrorFileHandle()); |
1001 | result.PutOutput (m_debugger.GetOutputFileHandle()); |
1002 | } |
1003 | |
1004 | |
1005 | SBStream commands_stream; |
1006 | |
1007 | |
1008 | WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream); |
1009 | |
1010 | const size_t num_args = m_option_data.m_args.size(); |
1011 | if (num_args > 0) |
1012 | { |
1013 | char arch_name[64]; |
1014 | if (m_debugger.GetDefaultArchitecture (arch_name, sizeof (arch_name))) |
1015 | commands_stream.Printf("target create --arch=%s %s", arch_name, EscapeString(m_option_data.m_args[0]).c_str()); |
1016 | else |
1017 | commands_stream.Printf("target create %s", EscapeString(m_option_data.m_args[0]).c_str()); |
1018 | |
1019 | if (!m_option_data.m_core_file.empty()) |
1020 | { |
1021 | commands_stream.Printf(" --core %s", EscapeString(m_option_data.m_core_file).c_str()); |
1022 | } |
1023 | commands_stream.Printf("\n"); |
1024 | |
1025 | if (num_args > 1) |
1026 | { |
1027 | commands_stream.Printf ("settings set -- target.run-args "); |
1028 | for (size_t arg_idx = 1; arg_idx < num_args; ++arg_idx) |
1029 | commands_stream.Printf(" %s", EscapeString(m_option_data.m_args[arg_idx]).c_str()); |
1030 | commands_stream.Printf("\n"); |
1031 | } |
1032 | } |
1033 | else if (!m_option_data.m_core_file.empty()) |
1034 | { |
1035 | commands_stream.Printf("target create --core %s\n", EscapeString(m_option_data.m_core_file).c_str()); |
1036 | } |
1037 | else if (!m_option_data.m_process_name.empty()) |
1038 | { |
1039 | commands_stream.Printf ("process attach --name %s", EscapeString(m_option_data.m_process_name).c_str()); |
1040 | |
1041 | if (m_option_data.m_wait_for) |
1042 | commands_stream.Printf(" --waitfor"); |
1043 | |
1044 | commands_stream.Printf("\n"); |
1045 | |
1046 | } |
1047 | else if (LLDB_INVALID_PROCESS_ID0 != m_option_data.m_process_pid) |
1048 | { |
1049 | commands_stream.Printf ("process attach --pid %" PRIu64"l" "u" "\n", m_option_data.m_process_pid); |
1050 | } |
1051 | |
1052 | WriteCommandsForSourcing(eCommandPlacementAfterFile, commands_stream); |
1053 | |
1054 | if (GetDebugMode()) |
1055 | { |
1056 | result.PutError(m_debugger.GetErrorFileHandle()); |
1057 | result.PutOutput(m_debugger.GetOutputFileHandle()); |
1058 | } |
1059 | |
1060 | bool handle_events = true; |
1061 | bool spawn_thread = false; |
1062 | |
1063 | |
1064 | |
1065 | const char *commands_data = commands_stream.GetData(); |
1066 | const size_t commands_size = commands_stream.GetSize(); |
1067 | |
1068 | |
1069 | bool quit_requested = false; |
1070 | bool stopped_for_crash = false; |
1071 | if (commands_data && commands_size) |
1072 | { |
1073 | int initial_commands_fds[2]; |
1074 | bool success = true; |
1075 | FILE *commands_file = PrepareCommandsForSourcing (commands_data, commands_size, initial_commands_fds); |
1076 | if (commands_file) |
1077 | { |
1078 | m_debugger.SetInputFileHandle (commands_file, true); |
1079 | |
1080 | |
1081 | |
1082 | bool old_async = m_debugger.GetAsync(); |
1083 | m_debugger.SetAsync(false); |
1084 | int num_errors; |
1085 | |
1086 | SBCommandInterpreterRunOptions options; |
1087 | options.SetStopOnError (true); |
1088 | if (m_option_data.m_batch) |
1089 | options.SetStopOnCrash (true); |
1090 | |
1091 | m_debugger.RunCommandInterpreter(handle_events, |
1092 | spawn_thread, |
1093 | options, |
1094 | num_errors, |
1095 | quit_requested, |
1096 | stopped_for_crash); |
1097 | |
1098 | if (m_option_data.m_batch && stopped_for_crash && !m_option_data.m_after_crash_commands.empty()) |
1099 | { |
1100 | int crash_command_fds[2]; |
1101 | SBStream crash_commands_stream; |
1102 | WriteCommandsForSourcing (eCommandPlacementAfterCrash, crash_commands_stream); |
1103 | const char *crash_commands_data = crash_commands_stream.GetData(); |
1104 | const size_t crash_commands_size = crash_commands_stream.GetSize(); |
1105 | commands_file = PrepareCommandsForSourcing (crash_commands_data, crash_commands_size, crash_command_fds); |
1106 | if (commands_file) |
1107 | { |
1108 | bool local_quit_requested; |
1109 | bool local_stopped_for_crash; |
1110 | m_debugger.SetInputFileHandle (commands_file, true); |
1111 | |
1112 | m_debugger.RunCommandInterpreter(handle_events, |
1113 | spawn_thread, |
1114 | options, |
1115 | num_errors, |
1116 | local_quit_requested, |
1117 | local_stopped_for_crash); |
1118 | if (local_quit_requested) |
1119 | quit_requested = true; |
1120 | |
1121 | } |
1122 | } |
1123 | m_debugger.SetAsync(old_async); |
1124 | } |
1125 | else |
1126 | success = false; |
1127 | |
1128 | |
1129 | CleanupAfterCommandSourcing(initial_commands_fds); |
1130 | |
1131 | |
1132 | if (!success) |
1133 | { |
1134 | exit(1); |
1135 | } |
1136 | |
1137 | } |
1138 | |
1139 | |
1140 | |
1141 | |
1142 | |
1143 | bool go_interactive = true; |
1144 | if (quit_requested) |
1145 | go_interactive = false; |
1146 | else if (m_option_data.m_batch && !stopped_for_crash) |
1147 | go_interactive = false; |
1148 | |
1149 | if (go_interactive) |
1150 | { |
1151 | m_debugger.SetInputFileHandle (stdinstdin, true); |
1152 | m_debugger.RunCommandInterpreter(handle_events, spawn_thread); |
1153 | } |
1154 | |
1155 | reset_stdin_termios(); |
1156 | fclose (stdinstdin); |
1157 | |
1158 | SBDebugger::Destroy (m_debugger); |
1159 | } |
1160 | |
1161 | |
1162 | void |
1163 | Driver::ResizeWindow (unsigned short col) |
1164 | { |
1165 | GetDebugger().SetTerminalWidth (col); |
1166 | } |
1167 | |
1168 | void |
1169 | sigwinch_handler (int signo) |
1170 | { |
1171 | struct winsize window_size; |
1172 | if (isatty (STDIN_FILENO0) |
1173 | && ::ioctl (STDIN_FILENO0, TIOCGWINSZ0x5413, &window_size) == 0) |
1174 | { |
1175 | if ((window_size.ws_col > 0) && g_driver != NULL__null) |
1176 | { |
1177 | g_driver->ResizeWindow (window_size.ws_col); |
1178 | } |
1179 | } |
1180 | } |
1181 | |
1182 | void |
1183 | sigint_handler (int signo) |
1184 | { |
1185 | static bool g_interrupt_sent = false; |
1186 | if (g_driver) |
1187 | { |
1188 | if (!g_interrupt_sent) |
1189 | { |
1190 | g_interrupt_sent = true; |
1191 | g_driver->GetDebugger().DispatchInputInterrupt(); |
1192 | g_interrupt_sent = false; |
1193 | return; |
1194 | } |
1195 | } |
1196 | |
1197 | exit (signo); |
1198 | } |
1199 | |
1200 | void |
1201 | sigtstp_handler (int signo) |
1202 | { |
1203 | g_driver->GetDebugger().SaveInputTerminalState(); |
1204 | signal (signo, SIG_DFL((__sighandler_t) 0)); |
1205 | kill (getpid(), signo); |
1206 | signal (signo, sigtstp_handler); |
1207 | } |
1208 | |
1209 | void |
1210 | sigcont_handler (int signo) |
1211 | { |
1212 | g_driver->GetDebugger().RestoreInputTerminalState(); |
1213 | signal (signo, SIG_DFL((__sighandler_t) 0)); |
1214 | kill (getpid(), signo); |
1215 | signal (signo, sigcont_handler); |
1216 | } |
1217 | |
1218 | int |
1219 | main (int argc, char const *argv[], const char *envp[]) |
1220 | { |
1221 | #ifdef _MSC_VER |
1222 | |
1223 | setvbuf(stdoutstdout, NULL__null, _IONBF2, 0); |
1224 | setvbuf(stdinstdin , NULL__null, _IONBF2, 0); |
1225 | #endif |
1226 | |
1227 | SBDebugger::Initialize(); |
1228 | |
1229 | SBHostOS::ThreadCreated ("<lldb.driver.main-thread>"); |
1230 | |
1231 | signal (SIGPIPE13, SIG_IGN((__sighandler_t) 1)); |
1232 | signal (SIGWINCH28, sigwinch_handler); |
1233 | signal (SIGINT2, sigint_handler); |
1234 | signal (SIGTSTP20, sigtstp_handler); |
1235 | signal (SIGCONT18, sigcont_handler); |
1236 | |
1237 | |
1238 | |
1239 | { |
1240 | Driver driver; |
1241 | |
1242 | bool exiting = false; |
1243 | SBError error (driver.ParseArgs (argc, argv, stdoutstdout, exiting)); |
1244 | if (error.Fail()) |
1245 | { |
1246 | const char *error_cstr = error.GetCString (); |
1247 | if (error_cstr) |
1248 | ::fprintf (stderrstderr, "error: %s\n", error_cstr); |
1249 | } |
1250 | else if (!exiting) |
1251 | { |
1252 | driver.MainLoop (); |
1253 | } |
1254 | } |
1255 | |
1256 | SBDebugger::Terminate(); |
1257 | return 0; |
1258 | } |