Add command-line parameter -p|--pid.
[llgdb.git] / src / LLGDBMain.cpp
1 //===-- LLGDBMain.cpp -------------------------------------------*- C++ -*-===//
2 //
3 //
4 //===----------------------------------------------------------------------===//
5
6 #include <lldb/API/SBHostOS.h>
7 #include <llvm/ADT/StringRef.h>
8 #include <llvm/Support/PrettyStackTrace.h>
9 #include <lldb/API/SBDebugger.h>
10 #include <lldb/Utility/StreamString.h>
11 #include <llvm/Support/Signals.h>
12 #include <lldb/API/SBCommandInterpreter.h>
13 #include <lldb/API/SBCommandReturnObject.h>
14 #include <lldb/API/SBStringList.h>
15 #include <boost/format.hpp>
16 #include <readline/readline.h>
17 #include <readline/history.h>
18 #include <vector>
19 #include <deque>
20 #include <setjmp.h>
21 #include <signal.h>
22 #include <error.h>
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, const char *type) {
119   SBCommandReturnObject Result;
120   dbg.GetCommandInterpreter().HandleCommand(("settings show " + std::string(setting)).c_str(), Result, /*add_to_history*/ false);
121   assert(Result.Succeeded());
122   // <plugin.process.gdb-remote.packet-timeout (unsigned) = 1\n
123   std::string str = Result.GetOutput();
124   const std::string expect = (boost::format("%1% (%2%) = ") % setting % type).str();
125   if (str.compare(0,expect.length(),expect)!=0||str[str.length()-1]!='\n')
126     error(1,0,"Unexpected 'settings show' response: expected=\"%s[...]\\n\" got=\"%s\"",expect.c_str(),str.c_str());
127   return str.substr(expect.length(),str.length()-1-expect.length());
128 }
129
130 #if 0
131 static int settings_show_int(SBDebugger &dbg, const char *setting) {
132   std::string string = settings_show_raw(dbg, setting,"unsigned");
133   long l;
134   int retval;
135   char *end;
136
137   errno = 0;
138   retval = l = strtol(string.c_str(), &end, 10);
139   assert(errno == 0 && (end == NULL || *end == '\0') && retval == l);
140   return retval;
141 }
142
143 static bool settings_show_bool(SBDebugger &dbg, const char *setting) {
144   std::string string = settings_show_raw(dbg, setting,"unsigned");
145   bool retval;
146
147   retval = (string == "on");
148   assert(retval || string == "off");
149   return retval;
150 }
151 #endif
152
153 static std::string settings_show_string(SBDebugger &dbg, const char *setting) {
154   std::string val = settings_show_raw(dbg,setting,"string");
155   if (val.length()<2 || val[0] != '"' || val[val.length()-1]!='"')
156     error(1,0,"Unexpected 'settings show' string response: expected '\"' quoting: %s", val.c_str());
157   return val.substr(1,val.length()-2);
158 }
159
160 static SBCommandReturnObject subcommand(SBDebugger &dbg, const char *cmd, bool print_command=true, bool add_to_history=false) {
161   if (print_command)
162     printf("%s%s\n",settings_show_string(dbg, "prompt").c_str(),cmd);
163   SBCommandReturnObject Result;
164   dbg.GetCommandInterpreter().HandleCommand(cmd, Result, add_to_history);
165   Result.PutOutput(stdout);
166   Result.PutError(stdout);
167   return Result;
168 }
169
170 static void default_command(SBDebugger &dbg, const char *cmd) {
171   subcommand(dbg, cmd, false/*print_command*/, true/*add_to_history*/);
172 }
173
174 static void quit_command(SBDebugger &dbg, const char *cmd) {
175   exit(EXIT_SUCCESS);
176 }
177
178 static void start_command(SBDebugger &dbg, const char *cmd) {
179   if (!subcommand(dbg, "b main").Succeeded())
180     return;
181   subcommand(dbg, ("run "+std::string(cmd)).c_str());
182 }
183
184 static void commands_command(SBDebugger &dbg, const char *cmd_param) {
185 #if 0
186   char *cmd = xstrdup("");
187   size_t cmd_len = 0;
188   mi_output *res;
189   char *bpnum = NULL;
190   int nesting;
191
192   if (*cmd_param == '\0') {
193     mi_set_console_cb(h, commands_command_console_cb, &bpnum);
194     dbg.HandleCommand(h, "output $bpnum");
195     mi_set_console_cb(h, console_cb, NULL);
196     assert(bpnum != NULL);
197     cmd_param = bpnum;
198   }
199
200   printf(_("Type commands for breakpoint(s) %s, one per line.\n"
201            "End with a line saying just \"end\".\n"),
202          cmd_param);
203
204   nesting = 0;
205   while (nesting >= 0) {
206     char *prompt, *data, *data2, *start, *end;
207     size_t data2_len;
208     int do_nest = 0;
209
210     prompt = xstrprintf("%*s>", (int)nesting);
211     data = llgdb_readline(prompt);
212     free(prompt);
213     if (data == NULL)
214       data = xstrdup("end");
215
216     for (start = data; isspace(*start); start++)
217       ;
218     for (end = data + strlen(data); end > start && isspace(end[-1]); end--)
219       ;
220     data2 = malloc(end - start + 1);
221     memcpy(data2, start, end - start);
222     data2[end - start] = '\0';
223     if (strcmp(data2, "python") == 0)
224       do_nest = 1;
225     if (strcmp(data2, "end") == 0)
226       do_nest = -1;
227     for (end = data2; *end && !isspace(*end); end++)
228       ;
229     *end = '\0';
230
231     /* Here is a bug in GDB, it does not recognize command shortcuts.  */
232     if (strcmp(data2, "while") == 0 || strcmp(data2, "if") == 0 ||
233         strcmp(data2, "commands") == 0 ||
234         strcmp(data2, "while-stepping") == 0 ||
235         strcmp(data2, "stepping") == 0 || strcmp(data2, "ws") == 0)
236       do_nest = 1;
237     free(data2);
238
239     nesting += do_nest;
240     if (nesting < 0)
241       break;
242
243     data2 = mi_escape(data);
244     free(data);
245     data2_len = strlen(data2);
246     cmd = xrealloc(cmd, cmd_len + 2 + data2_len + 2);
247     cmd[cmd_len] = ' ';
248     cmd[cmd_len + 1] = '"';
249     memcpy(&cmd[cmd_len + 2], data2, data2_len);
250     cmd[cmd_len + 2 + data2_len] = '"';
251     cmd[cmd_len + 2 + data2_len + 1] = '\0';
252     cmd_len += 2 + data2_len + 1;
253     free(data2);
254   }
255
256   mi_send(h, "-break-commands %s%s\n", cmd_param, cmd);
257   free(cmd);
258   free(bpnum);
259
260   res = mi_get_response_blk(h);
261   assert(res != NULL && res->next == NULL && res->tclass == MI_CL_DONE &&
262              res->c == NULL);
263   mi_free_output(res);
264 #endif
265 }
266
267 struct Cmd {
268   const char *name;
269   int min_shortcut;
270   typedef void (func_t)(SBDebugger &dbg, const char *cmd);
271   func_t *func;
272   Cmd(const char *name_, int min_shortcut_, func_t *func_):name(name_),min_shortcut(min_shortcut_),func(func_) {}
273 };
274 const Cmd cmds[] = {
275   Cmd("quit", 1, quit_command),
276   Cmd("start", 5, start_command),
277   Cmd("commands", 4, commands_command),
278 };
279
280 static void executecommand(SBDebugger &dbg, const char *cmd_s) {
281   const char *start, *end, *cs;
282
283   cs = cmd_s;
284   while (isspace(*cs))
285     cs++;
286   start = cs;
287   while (isalnum(*cs))
288     cs++;
289   end = cs;
290   while (isspace(*cs))
291     cs++;
292
293   for (const Cmd &cmd : cmds)
294     if (cmd.min_shortcut <= end - start &&
295         size_t(end - start) <= strlen(cmd.name) &&
296         strncmp(start, cmd.name, end - start) == 0)
297       return cmd.func(dbg, cs);
298
299   return default_command(dbg, cmd_s);
300 }
301
302 static char *xstrdup(const char *cs) {
303   char *s = static_cast<char *>(malloc(strlen(cs)+1));
304   assert(s);
305   strcpy(s,cs);
306   return s;
307 }
308
309 static SBDebugger *completion_entry_function_dbgp;
310 static char *completion_entry_function(const char *text, int matches) {
311   SBDebugger &dbg = *completion_entry_function_dbgp;
312   static std::vector<std::string> found_vec;
313   assert(matches >= 0);
314   if (matches == 0) {
315     found_vec.clear();
316     SBStringList matches;
317     // LLDB: FIXME: These two values are not implemented. m_match_start_point; m_max_return_elements;
318     const int match_start_point = 0;
319     // LLDB: Only max_return_elements == -1 is supported at present:
320     const int max_return_elements = -1;
321     int got=dbg.GetCommandInterpreter().HandleCompletion(text, strlen(text)/*cursor_pos*/, match_start_point, max_return_elements, matches);
322     assert(matches.IsValid());
323     if (unsigned(got+1)!=matches.GetSize())
324       error(1,0,"HandleCompletion=%d +1 != %u=matches.GetSize()",got,matches.GetSize());
325     //for (size_t ix=0;ix<matches.GetSize();++ix)
326     //  fprintf(stderr,"match[%zu]=<%s>\n",ix,matches.GetStringAtIndex(ix));
327     const char *first=matches.GetStringAtIndex(0);
328     assert(matches.GetSize()>=1);
329     if (first[0]) {
330       std::string str(first);
331       while (!str.empty()&&str.back()==' ')
332         str.pop_back();
333       found_vec.push_back(text + str);
334     } else
335       for (size_t ix=1;ix<matches.GetSize();++ix)
336         found_vec.push_back(matches.GetStringAtIndex(ix));
337   }
338   if (size_t(matches) < found_vec.size())
339     return xstrdup(found_vec[matches].c_str());
340   else if (size_t(matches) == found_vec.size())
341     return nullptr;
342   else
343     assert(0);
344 }
345
346 static std::deque<std::string> opt_iex, opt_ex;
347 static std::optional<std::string> opt_exec, opt_core;
348 static std::optional<lldb::pid_t> opt_pid;
349 static std::vector<std::string> opt_args;
350
351 std::string string_quote(std::string &&str) {
352   std::string retval = "\"";
353   for (const char c : str) {
354     if (c != '"')
355       retval.push_back(c);
356     else
357       retval += "\\\"";
358   }
359   return retval + "\"";
360 }
361
362 std::optional<std::string> next_arg() {
363   if (!opt_iex.empty()) {
364     std::string retval = opt_iex.front();
365     opt_iex.pop_front();
366     return retval;
367   }
368   if (opt_exec || opt_core) {
369     std::string cmd = "target create";
370     if (opt_exec) {
371       cmd += " " + string_quote(std::move(*opt_exec));
372       opt_exec.reset();
373     }
374     if (opt_core) {
375       cmd += " --core " + string_quote(std::move(*opt_core));
376       opt_core.reset();
377     }
378     return cmd;
379   }
380   if (opt_pid) {
381     std::string retval = (boost::format("attach %1%") % *opt_pid).str();
382     opt_pid.reset();
383     return retval;
384   }
385   if (!opt_ex.empty()) {
386     std::string retval = opt_ex.front();
387     opt_ex.pop_front();
388     return retval;
389   }
390   return {};
391 }
392
393 int main(int argc, char **argv) {
394   llvm::StringRef ToolName = argv[0];
395   llvm::sys::PrintStackTraceOnErrorSignal(ToolName);
396   llvm::PrettyStackTraceProgram X(argc, argv);
397
398   for (int argi = 1; argi < argc; ++argi) {
399     std::string arg = argv[argi];
400     auto str_to_pid = [](const std::string &str){
401       lldb::pid_t pid_test = atoi(str.c_str());
402       return (boost::format("%1%") % pid_test).str() != str || pid_test <= 0 ? 0 : pid_test;
403     };
404     if (arg == "-iex") {
405       const char *cmd = argv[++argi];
406       assert(cmd);
407       opt_iex.push_back(cmd);
408     } else if (arg == "-ex") {
409       const char *cmd = argv[++argi];
410       assert(cmd);
411       opt_ex.push_back(cmd);
412     } else if (arg == "--args") {
413       const char *exec = argv[++argi];
414       assert(exec);
415       assert(!opt_exec);
416       assert(!opt_core);
417       assert(opt_args.empty());
418       opt_exec=std::string(exec);
419       while (++argi<argc) {
420         arg = argv[argi];
421         opt_args.push_back(std::move(arg));
422       }
423     } else if (arg == "-p" || arg == "--pid") {
424       opt_pid = str_to_pid(argv[++argi]);
425       if (opt_pid == 0)
426         error(1,0,"Invalid PID: %s",argv[argi]);
427     } else if (!opt_exec)
428       opt_exec=std::move(arg);
429     else if (!opt_pid && str_to_pid(arg) > 0)
430       opt_pid = str_to_pid(arg);
431     else if (!opt_core)
432       opt_core=std::move(arg);
433     else
434       error(1,0,"Excessive argument: %s",arg.c_str());
435   }
436
437   SBDebugger::Initialize();
438   SBDebugger dbg = SBDebugger::Create();
439   dbg.HandleCommand("settings set symbols.enable-external-lookup false");
440
441   completion_entry_function_dbgp=&dbg;
442   rl_completion_entry_function = completion_entry_function;
443   rl_readline_name = "llgdb";
444
445   if (true/*settings_show_bool(dbg, "history save")*/) {
446     int history_size = 1000000; //settings_show_int(dbg, "history size");
447     const char *HOME = getenv("HOME");
448     assert(HOME);
449     static const std::string history_filename = std::string(HOME) + "/.llgdb_history"; // settings_show_string(dbg, "history filename");
450     //gdb_set_bool(dbg, "history save", false);
451     stifle_history(history_size);
452     read_history(history_filename.c_str());
453     on_exit(history_save, static_cast<void *>(const_cast<char *>(history_filename.c_str())));
454     /* Do not free HISTORY_FILENAME.  */
455   }
456
457   for (;;) {
458
459     std::string prompt = settings_show_string(dbg, "prompt");
460     std::optional<std::string> arg = next_arg();
461     std::string cmd;
462     if (!arg) {
463       char *cmd_s = llgdb_readline(prompt.c_str());
464       if (cmd_s)
465         add_history(cmd_s);
466       cmd = cmd_s?:"quit";
467       free(cmd_s);
468     } else {
469       puts((prompt + *arg).c_str());
470       cmd = std::move(*arg);
471     }
472
473 #if 0
474     /* FIXME: -ex commands do not have pagination set.  */
475     if (false /*!settings_show_bool(h, "pagination")*/)
476       console_cb_rows = 0;
477     else
478       rl_get_screen_size(&console_cb_rows, &console_cb_columns);
479     console_cb_drop = false;
480     console_cb_row = console_cb_column = 0;
481 #endif
482
483     executecommand(dbg, cmd.c_str());
484   }
485 }