1 //===-- LLGDBMain.cpp -------------------------------------------*- C++ -*-===//
4 //===----------------------------------------------------------------------===//
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>
25 using namespace lldb_private;
27 static jmp_buf llgdb_readline_jmp_buf;
29 static void llgdb_readline_sigint_handler(int signum) {
30 assert(signum == SIGINT);
31 longjmp(llgdb_readline_jmp_buf, 1);
34 static char *llgdb_readline(const char *prompt) {
36 sighandler_t saved_handler;
40 i = sigprocmask(SIG_SETMASK, NULL, &mask);
42 saved_handler = signal(SIGINT, llgdb_readline_sigint_handler);
43 if (setjmp(llgdb_readline_jmp_buf) != 0) {
45 rl_cleanup_after_signal();
47 /* GDB prints this. */
50 i = sigprocmask(SIG_SETMASK, &mask, NULL);
53 retval = readline(prompt);
54 saved_handler = signal(SIGINT, saved_handler);
55 assert(saved_handler == llgdb_readline_sigint_handler);
60 static int console_cb_rows, console_cb_columns, console_cb_row,
62 static bool console_cb_drop;
64 static void console_cb(const char *str, void *data) {
69 if (console_cb_rows == 0) {
74 while (*str != '\0') {
79 cs = strchr(str, '\n');
81 cs = &str[strlen(str)];
82 columns = std::min(cs - str, long(console_cb_columns - console_cb_column));
84 size = fwrite(str, 1, columns, stdout);
85 assert(size == size_t(columns));
87 console_cb_column += columns;
92 else assert(console_cb_column < console_cb_columns);
95 console_cb_column = 0;
96 if (console_cb_row < console_cb_rows - 1)
98 answer = llgdb_readline("---Type <return> to continue, or q <return> to quit---");
99 for (cs = answer; isspace(*cs); cs++)
104 console_cb_drop = true;
113 static void history_save(int rc, void *arg) {
114 const char *history_filename = static_cast<const char *>(arg);
115 write_history(history_filename);
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());
131 static int settings_show_int(SBDebugger &dbg, const char *setting) {
132 std::string string = settings_show_raw(dbg, setting,"unsigned");
138 retval = l = strtol(string.c_str(), &end, 10);
139 assert(errno == 0 && (end == NULL || *end == '\0') && retval == l);
143 static bool settings_show_bool(SBDebugger &dbg, const char *setting) {
144 std::string string = settings_show_raw(dbg, setting,"unsigned");
147 retval = (string == "on");
148 assert(retval || string == "off");
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);
160 static SBCommandReturnObject subcommand(SBDebugger &dbg, const char *cmd, bool print_command=true, bool add_to_history=false) {
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);
170 static void default_command(SBDebugger &dbg, const char *cmd) {
171 subcommand(dbg, cmd, false/*print_command*/, true/*add_to_history*/);
174 static void quit_command(SBDebugger &dbg, const char *cmd) {
178 static void start_command(SBDebugger &dbg, const char *cmd) {
179 if (!subcommand(dbg, "b main").Succeeded())
181 subcommand(dbg, ("run "+std::string(cmd)).c_str());
184 static void commands_command(SBDebugger &dbg, const char *cmd_param) {
186 char *cmd = xstrdup("");
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);
200 printf(_("Type commands for breakpoint(s) %s, one per line.\n"
201 "End with a line saying just \"end\".\n"),
205 while (nesting >= 0) {
206 char *prompt, *data, *data2, *start, *end;
210 prompt = xstrprintf("%*s>", (int)nesting);
211 data = llgdb_readline(prompt);
214 data = xstrdup("end");
216 for (start = data; isspace(*start); start++)
218 for (end = data + strlen(data); end > start && isspace(end[-1]); end--)
220 data2 = malloc(end - start + 1);
221 memcpy(data2, start, end - start);
222 data2[end - start] = '\0';
223 if (strcmp(data2, "python") == 0)
225 if (strcmp(data2, "end") == 0)
227 for (end = data2; *end && !isspace(*end); end++)
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)
243 data2 = mi_escape(data);
245 data2_len = strlen(data2);
246 cmd = xrealloc(cmd, cmd_len + 2 + data2_len + 2);
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;
256 mi_send(h, "-break-commands %s%s\n", cmd_param, cmd);
260 res = mi_get_response_blk(h);
261 assert(res != NULL && res->next == NULL && res->tclass == MI_CL_DONE &&
270 typedef void (func_t)(SBDebugger &dbg, const char *cmd);
272 Cmd(const char *name_, int min_shortcut_, func_t *func_):name(name_),min_shortcut(min_shortcut_),func(func_) {}
275 Cmd("quit", 1, quit_command),
276 Cmd("start", 5, start_command),
277 Cmd("commands", 4, commands_command),
280 static void executecommand(SBDebugger &dbg, const char *cmd_s) {
281 const char *start, *end, *cs;
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);
299 return default_command(dbg, cmd_s);
302 static char *xstrdup(const char *cs) {
303 char *s = static_cast<char *>(malloc(strlen(cs)+1));
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);
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);
330 std::string str(first);
331 while (!str.empty()&&str.back()==' ')
333 found_vec.push_back(text + str);
335 for (size_t ix=1;ix<matches.GetSize();++ix)
336 found_vec.push_back(matches.GetStringAtIndex(ix));
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())
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;
351 std::string string_quote(std::string &&str) {
352 std::string retval = "\"";
353 for (const char c : str) {
359 return retval + "\"";
362 std::optional<std::string> next_arg() {
363 if (!opt_iex.empty()) {
364 std::string retval = opt_iex.front();
368 if (opt_exec || opt_core) {
369 std::string cmd = "target create";
371 cmd += " " + string_quote(std::move(*opt_exec));
375 cmd += " --core " + string_quote(std::move(*opt_core));
381 std::string retval = (boost::format("attach %1%") % *opt_pid).str();
385 if (!opt_ex.empty()) {
386 std::string retval = opt_ex.front();
393 int main(int argc, char **argv) {
394 llvm::StringRef ToolName = argv[0];
395 llvm::sys::PrintStackTraceOnErrorSignal(ToolName);
396 llvm::PrettyStackTraceProgram X(argc, argv);
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;
405 const char *cmd = argv[++argi];
407 opt_iex.push_back(cmd);
408 } else if (arg == "-ex") {
409 const char *cmd = argv[++argi];
411 opt_ex.push_back(cmd);
412 } else if (arg == "--args") {
413 const char *exec = argv[++argi];
417 assert(opt_args.empty());
418 opt_exec=std::string(exec);
419 while (++argi<argc) {
421 opt_args.push_back(std::move(arg));
423 } else if (arg == "-p" || arg == "--pid") {
424 opt_pid = str_to_pid(argv[++argi]);
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);
432 opt_core=std::move(arg);
434 error(1,0,"Excessive argument: %s",arg.c_str());
437 SBDebugger::Initialize();
438 SBDebugger dbg = SBDebugger::Create();
439 dbg.HandleCommand("settings set symbols.enable-external-lookup false");
441 completion_entry_function_dbgp=&dbg;
442 rl_completion_entry_function = completion_entry_function;
443 rl_readline_name = "llgdb";
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");
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. */
459 std::string prompt = settings_show_string(dbg, "prompt");
460 std::optional<std::string> arg = next_arg();
463 char *cmd_s = llgdb_readline(prompt.c_str());
469 puts((prompt + *arg).c_str());
470 cmd = std::move(*arg);
474 /* FIXME: -ex commands do not have pagination set. */
475 if (false /*!settings_show_bool(h, "pagination")*/)
478 rl_get_screen_size(&console_cb_rows, &console_cb_columns);
479 console_cb_drop = false;
480 console_cb_row = console_cb_column = 0;
483 executecommand(dbg, cmd.c_str());