Add SetAsync=false; not sure of the real effect now.
[llgdb.git] / src / LLGDBMain.cpp
1 //===-- LLGDBMain.cpp -------------------------------------------*- C++ -*-===//
2 //
3 //
4 //===----------------------------------------------------------------------===//
5
6 #include <boost/format.hpp>
7 #include <deque>
8 #include <error.h>
9 #include <lldb/API/SBCommandInterpreter.h>
10 #include <lldb/API/SBCommandReturnObject.h>
11 #include <lldb/API/SBDebugger.h>
12 #include <lldb/API/SBHostOS.h>
13 #include <lldb/API/SBStringList.h>
14 #include <lldb/Utility/StreamString.h>
15 #include <llvm/ADT/StringRef.h>
16 #include <llvm/Support/PrettyStackTrace.h>
17 #include <llvm/Support/Signals.h>
18 #include <readline/history.h>
19 #include <readline/readline.h>
20 #include <setjmp.h>
21 #include <signal.h>
22 #include <vector>
23
24 using namespace lldb;
25 using namespace lldb_private;
26
27 static jmp_buf llgdb_readline_jmp_buf;
28
29 static void llgdb_readline_sigint_handler(int signum) {
30   assert(signum == SIGINT);
31   longjmp(llgdb_readline_jmp_buf, 1);
32 }
33
34 static char *llgdb_readline(const char *prompt) {
35   char *retval;
36   sighandler_t saved_handler;
37   sigset_t mask;
38   int i;
39
40   i = sigprocmask(SIG_SETMASK, NULL, &mask);
41   assert(i == 0);
42   saved_handler = signal(SIGINT, llgdb_readline_sigint_handler);
43   if (setjmp(llgdb_readline_jmp_buf) != 0) {
44     rl_free_line_state();
45     rl_cleanup_after_signal();
46
47     /* GDB prints this.  */
48     puts("Quit");
49
50     i = sigprocmask(SIG_SETMASK, &mask, NULL);
51     assert(i == 0);
52   }
53   retval = readline(prompt);
54   saved_handler = signal(SIGINT, saved_handler);
55   assert(saved_handler == llgdb_readline_sigint_handler);
56   return retval;
57 }
58
59 #if 0
60 static int console_cb_rows, console_cb_columns, console_cb_row,
61     console_cb_column;
62 static bool console_cb_drop;
63
64 static void console_cb(const char *str, void *data) {
65   const char *cs;
66
67   if (console_cb_drop)
68     return;
69   if (console_cb_rows == 0) {
70     fputs(str, stdout);
71     return;
72   }
73
74   while (*str != '\0') {
75     int columns;
76     size_t size;
77     char *answer;
78
79     cs = strchr(str, '\n');
80     if (cs == NULL)
81       cs = &str[strlen(str)];
82     columns = std::min(cs - str, long(console_cb_columns - console_cb_column));
83     if (columns > 0) {
84       size = fwrite(str, 1, columns, stdout);
85       assert(size == size_t(columns));
86       str += columns;
87       console_cb_column += columns;
88       continue;
89     }
90     if (*str == '\n')
91       str++;
92     else assert(console_cb_column < console_cb_columns);
93     putchar('\n');
94     console_cb_row++;
95     console_cb_column = 0;
96     if (console_cb_row < console_cb_rows - 1)
97       continue;
98     answer = llgdb_readline("---Type <return> to continue, or q <return> to quit---");
99     for (cs = answer; isspace(*cs); cs++)
100       ;
101     if (*cs == 'q') {
102       free(answer);
103       puts("Quit");
104       console_cb_drop = true;
105       return;
106     }
107     free(answer);
108     console_cb_row = 0;
109   }
110 }
111 #endif
112
113 static void history_save(int rc, void *arg) {
114   const char *history_filename = static_cast<const char *>(arg);
115   write_history(history_filename);
116 }
117
118 static std::string settings_show_raw(SBDebugger &dbg, const char *setting,
119                                      const char *type) {
120   SBCommandReturnObject Result;
121   dbg.GetCommandInterpreter().HandleCommand(
122       ("settings show " + std::string(setting)).c_str(), Result,
123       /*add_to_history*/ false);
124   assert(Result.Succeeded());
125   // <plugin.process.gdb-remote.packet-timeout (unsigned) = 1\n
126   std::string str = Result.GetOutput();
127   const std::string expect =
128       (boost::format("%1% (%2%) = ") % setting % type).str();
129   if (str.compare(0, expect.length(), expect) != 0 ||
130       str[str.length() - 1] != '\n')
131     error(1, 0,
132           "Unexpected 'settings show' response: expected=\"%s[...]\\n\" "
133           "got=\"%s\"",
134           expect.c_str(), str.c_str());
135   return str.substr(expect.length(), str.length() - 1 - expect.length());
136 }
137
138 #if 0
139 static int settings_show_int(SBDebugger &dbg, const char *setting) {
140   std::string string = settings_show_raw(dbg, setting,"unsigned");
141   long l;
142   int retval;
143   char *end;
144
145   errno = 0;
146   retval = l = strtol(string.c_str(), &end, 10);
147   assert(errno == 0 && (end == NULL || *end == '\0') && retval == l);
148   return retval;
149 }
150
151 static bool settings_show_bool(SBDebugger &dbg, const char *setting) {
152   std::string string = settings_show_raw(dbg, setting,"unsigned");
153   bool retval;
154
155   retval = (string == "on");
156   assert(retval || string == "off");
157   return retval;
158 }
159 #endif
160
161 static std::string settings_show_string(SBDebugger &dbg, const char *setting) {
162   std::string val = settings_show_raw(dbg, setting, "string");
163   if (val.length() < 2 || val[0] != '"' || val[val.length() - 1] != '"')
164     error(
165         1, 0,
166         "Unexpected 'settings show' string response: expected '\"' quoting: %s",
167         val.c_str());
168   return val.substr(1, val.length() - 2);
169 }
170
171 static SBCommandReturnObject subcommand(SBDebugger &dbg, const char *cmd,
172                                         bool print_command = true,
173                                         bool add_to_history = false) {
174   if (print_command)
175     printf("%s%s\n", settings_show_string(dbg, "prompt").c_str(), cmd);
176   SBCommandReturnObject Result;
177   dbg.GetCommandInterpreter().HandleCommand(cmd, Result, add_to_history);
178   Result.PutOutput(stdout);
179   Result.PutError(stdout);
180   return Result;
181 }
182
183 static void default_command(SBDebugger &dbg, const char *cmd) {
184   subcommand(dbg, cmd, false /*print_command*/, true /*add_to_history*/);
185 }
186
187 static void quit_command(SBDebugger &dbg, const char *cmd) {
188   exit(EXIT_SUCCESS);
189 }
190
191 static void start_command(SBDebugger &dbg, const char *cmd) {
192   if (!subcommand(dbg, "b main").Succeeded())
193     return;
194   subcommand(dbg, ("run " + std::string(cmd)).c_str());
195 }
196
197 static void commands_command(SBDebugger &dbg, const char *cmd_param) {
198 #if 0
199   char *cmd = xstrdup("");
200   size_t cmd_len = 0;
201   mi_output *res;
202   char *bpnum = NULL;
203   int nesting;
204
205   if (*cmd_param == '\0') {
206     mi_set_console_cb(h, commands_command_console_cb, &bpnum);
207     dbg.HandleCommand(h, "output $bpnum");
208     mi_set_console_cb(h, console_cb, NULL);
209     assert(bpnum != NULL);
210     cmd_param = bpnum;
211   }
212
213   printf(_("Type commands for breakpoint(s) %s, one per line.\n"
214            "End with a line saying just \"end\".\n"),
215          cmd_param);
216
217   nesting = 0;
218   while (nesting >= 0) {
219     char *prompt, *data, *data2, *start, *end;
220     size_t data2_len;
221     int do_nest = 0;
222
223     prompt = xstrprintf("%*s>", (int)nesting);
224     data = llgdb_readline(prompt);
225     free(prompt);
226     if (data == NULL)
227       data = xstrdup("end");
228
229     for (start = data; isspace(*start); start++)
230       ;
231     for (end = data + strlen(data); end > start && isspace(end[-1]); end--)
232       ;
233     data2 = malloc(end - start + 1);
234     memcpy(data2, start, end - start);
235     data2[end - start] = '\0';
236     if (strcmp(data2, "python") == 0)
237       do_nest = 1;
238     if (strcmp(data2, "end") == 0)
239       do_nest = -1;
240     for (end = data2; *end && !isspace(*end); end++)
241       ;
242     *end = '\0';
243
244     /* Here is a bug in GDB, it does not recognize command shortcuts.  */
245     if (strcmp(data2, "while") == 0 || strcmp(data2, "if") == 0 ||
246         strcmp(data2, "commands") == 0 ||
247         strcmp(data2, "while-stepping") == 0 ||
248         strcmp(data2, "stepping") == 0 || strcmp(data2, "ws") == 0)
249       do_nest = 1;
250     free(data2);
251
252     nesting += do_nest;
253     if (nesting < 0)
254       break;
255
256     data2 = mi_escape(data);
257     free(data);
258     data2_len = strlen(data2);
259     cmd = xrealloc(cmd, cmd_len + 2 + data2_len + 2);
260     cmd[cmd_len] = ' ';
261     cmd[cmd_len + 1] = '"';
262     memcpy(&cmd[cmd_len + 2], data2, data2_len);
263     cmd[cmd_len + 2 + data2_len] = '"';
264     cmd[cmd_len + 2 + data2_len + 1] = '\0';
265     cmd_len += 2 + data2_len + 1;
266     free(data2);
267   }
268
269   mi_send(h, "-break-commands %s%s\n", cmd_param, cmd);
270   free(cmd);
271   free(bpnum);
272
273   res = mi_get_response_blk(h);
274   assert(res != NULL && res->next == NULL && res->tclass == MI_CL_DONE &&
275              res->c == NULL);
276   mi_free_output(res);
277 #endif
278 }
279
280 struct Cmd {
281   const char *name;
282   int min_shortcut;
283   typedef void(func_t)(SBDebugger &dbg, const char *cmd);
284   func_t *func;
285   Cmd(const char *name_, int min_shortcut_, func_t *func_)
286       : name(name_), min_shortcut(min_shortcut_), func(func_) {}
287 };
288 const Cmd cmds[] = {
289     Cmd("quit", 1, quit_command),
290     Cmd("start", 5, start_command),
291     Cmd("commands", 4, commands_command),
292 };
293
294 static void executecommand(SBDebugger &dbg, const char *cmd_s) {
295   const char *start, *end, *cs;
296
297   cs = cmd_s;
298   while (isspace(*cs))
299     cs++;
300   start = cs;
301   while (isalnum(*cs))
302     cs++;
303   end = cs;
304   while (isspace(*cs))
305     cs++;
306
307   for (const Cmd &cmd : cmds)
308     if (cmd.min_shortcut <= end - start &&
309         size_t(end - start) <= strlen(cmd.name) &&
310         strncmp(start, cmd.name, end - start) == 0)
311       return cmd.func(dbg, cs);
312
313   return default_command(dbg, cmd_s);
314 }
315
316 static char *xstrdup(const char *cs) {
317   char *s = static_cast<char *>(malloc(strlen(cs) + 1));
318   assert(s);
319   strcpy(s, cs);
320   return s;
321 }
322
323 static SBDebugger *completion_entry_function_dbgp;
324 static char *completion_entry_function(const char *text, int matches) {
325   SBDebugger &dbg = *completion_entry_function_dbgp;
326   static std::vector<std::string> found_vec;
327   assert(matches >= 0);
328   if (matches == 0) {
329     found_vec.clear();
330     SBStringList matches;
331     // LLDB: FIXME: These two values are not implemented. m_match_start_point;
332     // m_max_return_elements;
333     const int match_start_point = 0;
334     // LLDB: Only max_return_elements == -1 is supported at present:
335     const int max_return_elements = -1;
336     int got = dbg.GetCommandInterpreter().HandleCompletion(
337         text, strlen(text) /*cursor_pos*/, match_start_point,
338         max_return_elements, matches);
339     assert(matches.IsValid());
340     if (unsigned(got + 1) != matches.GetSize())
341       error(1, 0, "HandleCompletion=%d +1 != %u=matches.GetSize()", got,
342             matches.GetSize());
343     // for (size_t ix=0;ix<matches.GetSize();++ix)
344     //  fprintf(stderr,"match[%zu]=<%s>\n",ix,matches.GetStringAtIndex(ix));
345     const char *first = matches.GetStringAtIndex(0);
346     assert(matches.GetSize() >= 1);
347     if (first[0]) {
348       std::string str(first);
349       while (!str.empty() && str.back() == ' ')
350         str.pop_back();
351       found_vec.push_back(text + str);
352     } else
353       for (size_t ix = 1; ix < matches.GetSize(); ++ix)
354         found_vec.push_back(matches.GetStringAtIndex(ix));
355   }
356   if (size_t(matches) < found_vec.size())
357     return xstrdup(found_vec[matches].c_str());
358   else if (size_t(matches) == found_vec.size())
359     return nullptr;
360   else
361     assert(0);
362 }
363
364 static std::deque<std::string> opt_iex, opt_ex;
365 static std::optional<std::string> opt_exec, opt_core;
366 static std::optional<lldb::pid_t> opt_pid;
367 static std::vector<std::string> opt_args;
368
369 std::string string_quote(std::string &&str) {
370   std::string retval = "\"";
371   for (const char c : str) {
372     if (c != '"')
373       retval.push_back(c);
374     else
375       retval += "\\\"";
376   }
377   return retval + "\"";
378 }
379
380 std::optional<std::string> next_arg() {
381   if (!opt_iex.empty()) {
382     std::string retval = opt_iex.front();
383     opt_iex.pop_front();
384     return retval;
385   }
386   if (opt_exec || opt_core) {
387     std::string cmd = "target create";
388     if (opt_exec) {
389       cmd += " " + string_quote(std::move(*opt_exec));
390       opt_exec.reset();
391     }
392     if (opt_core) {
393       cmd += " --core " + string_quote(std::move(*opt_core));
394       opt_core.reset();
395     }
396     return cmd;
397   }
398   if (opt_pid) {
399     std::string retval = (boost::format("attach %1%") % *opt_pid).str();
400     opt_pid.reset();
401     return retval;
402   }
403   if (!opt_ex.empty()) {
404     std::string retval = opt_ex.front();
405     opt_ex.pop_front();
406     return retval;
407   }
408   return {};
409 }
410
411 int main(int argc, char **argv) {
412   llvm::StringRef ToolName = argv[0];
413   llvm::sys::PrintStackTraceOnErrorSignal(ToolName);
414   llvm::PrettyStackTraceProgram X(argc, argv);
415
416   for (int argi = 1; argi < argc; ++argi) {
417     std::string arg = argv[argi];
418     auto str_to_pid = [](const std::string &str) {
419       lldb::pid_t pid_test = atoi(str.c_str());
420       return (boost::format("%1%") % pid_test).str() != str || pid_test <= 0
421                  ? 0
422                  : pid_test;
423     };
424     if (arg == "-iex") {
425       const char *cmd = argv[++argi];
426       assert(cmd);
427       opt_iex.push_back(cmd);
428     } else if (arg == "-ex") {
429       const char *cmd = argv[++argi];
430       assert(cmd);
431       opt_ex.push_back(cmd);
432     } else if (arg == "--args") {
433       const char *exec = argv[++argi];
434       assert(exec);
435       assert(!opt_exec);
436       assert(!opt_core);
437       assert(opt_args.empty());
438       opt_exec = std::string(exec);
439       while (++argi < argc) {
440         arg = argv[argi];
441         opt_args.push_back(std::move(arg));
442       }
443     } else if ((arg == "-e" || arg == "--exec") && argv[argi + 1])
444       opt_exec = argv[++argi];
445     else if ((arg == "-p" || arg == "--pid") && argv[argi + 1]) {
446       opt_pid = str_to_pid(argv[++argi]);
447       if (opt_pid == 0)
448         error(1, 0, "Invalid PID: %s", argv[argi]);
449     } else if ((arg == "-c" || arg == "--core") && argv[argi + 1])
450       opt_core = argv[++argi];
451     else if (arg == "-h" || arg == "--help") {
452       puts("\
453 This is the GNU debugger emulation by LLDB.  Usage:\n\
454 \n\
455     llgdb [options] [executable-file [core-file or process-id]]\n\
456     llgdb [options] --args executable-file [inferior-arguments ...]\n\
457 \n\
458 Selection of debuggee and its files:\n\
459 \n\
460   --args             Arguments after executable-file are passed to inferior\n\
461   --core=COREFILE    Analyze the core dump COREFILE.\n\
462   --exec=EXECFILE    Use EXECFILE as the executable.\n\
463   --pid=PID          Attach to running process PID.\n\
464 \n\
465 Initial commands and command files:\n\
466 \n\
467   -ex COMMAND\n\
468                      Execute a single GDB command.\n\
469                      May be used multiple times and in conjunction\n\
470                      with --command.\n\
471   -iex COMMAND\n\
472                      Like -ex but before loading inferior.\n\
473 \n\
474 Operating modes:\n\
475 \n\
476   --help             Print this message and then exit.\n\
477 \n\
478 Report bugs to jan.kratochvil -at- redhat.com.\n\
479 ");
480       exit(1);
481     } else if (!opt_exec)
482       opt_exec = std::move(arg);
483     else if (!opt_pid && str_to_pid(arg) > 0)
484       opt_pid = str_to_pid(arg);
485     else if (!opt_core)
486       opt_core = std::move(arg);
487     else
488       error(1, 0, "Excessive argument: %s", arg.c_str());
489   }
490
491   SBDebugger::Initialize();
492   SBDebugger dbg = SBDebugger::Create(true /*source_init_files*/);
493   dbg.SetAsync(false);
494
495   completion_entry_function_dbgp = &dbg;
496   rl_completion_entry_function = completion_entry_function;
497   rl_readline_name = "llgdb";
498
499   if (true /*settings_show_bool(dbg, "history save")*/) {
500     int history_size = 1000000; // settings_show_int(dbg, "history size");
501     const char *HOME = getenv("HOME");
502     assert(HOME);
503     static const std::string history_filename =
504         std::string(HOME) +
505         "/.llgdb_history"; // settings_show_string(dbg, "history filename");
506     // gdb_set_bool(dbg, "history save", false);
507     stifle_history(history_size);
508     read_history(history_filename.c_str());
509     on_exit(history_save,
510             static_cast<void *>(const_cast<char *>(history_filename.c_str())));
511     /* Do not free HISTORY_FILENAME.  */
512   }
513
514   for (;;) {
515
516     std::string prompt = settings_show_string(dbg, "prompt");
517     std::optional<std::string> arg = next_arg();
518     std::string cmd;
519     if (!arg) {
520       char *cmd_s = llgdb_readline(prompt.c_str());
521       if (cmd_s)
522         add_history(cmd_s);
523       cmd = cmd_s ?: "quit";
524       free(cmd_s);
525     } else {
526       puts((prompt + *arg).c_str());
527       cmd = std::move(*arg);
528     }
529
530 #if 0
531     /* FIXME: -ex commands do not have pagination set.  */
532     if (false /*!settings_show_bool(h, "pagination")*/)
533       console_cb_rows = 0;
534     else
535       rl_get_screen_size(&console_cb_rows, &console_cb_columns);
536     console_cb_drop = false;
537     console_cb_row = console_cb_column = 0;
538 #endif
539
540     executecommand(dbg, cmd.c_str());
541   }
542 }