//===-- LLGDBMain.cpp -------------------------------------------*- C++ -*-===// // // //===----------------------------------------------------------------------===// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace lldb; using namespace lldb_private; static jmp_buf llgdb_readline_jmp_buf; static void llgdb_readline_sigint_handler(int signum) { assert(signum == SIGINT); longjmp(llgdb_readline_jmp_buf, 1); } static char *llgdb_readline(const char *prompt) { char *retval; sighandler_t saved_handler; sigset_t mask; int i; i = sigprocmask(SIG_SETMASK, NULL, &mask); assert(i == 0); saved_handler = signal(SIGINT, llgdb_readline_sigint_handler); if (setjmp(llgdb_readline_jmp_buf) != 0) { rl_free_line_state(); rl_cleanup_after_signal(); /* GDB prints this. */ puts("Quit"); i = sigprocmask(SIG_SETMASK, &mask, NULL); assert(i == 0); } retval = readline(prompt); saved_handler = signal(SIGINT, saved_handler); assert(saved_handler == llgdb_readline_sigint_handler); return retval; } #if 0 static int console_cb_rows, console_cb_columns, console_cb_row, console_cb_column; static bool console_cb_drop; static void console_cb(const char *str, void *data) { const char *cs; if (console_cb_drop) return; if (console_cb_rows == 0) { fputs(str, stdout); return; } while (*str != '\0') { int columns; size_t size; char *answer; cs = strchr(str, '\n'); if (cs == NULL) cs = &str[strlen(str)]; columns = std::min(cs - str, long(console_cb_columns - console_cb_column)); if (columns > 0) { size = fwrite(str, 1, columns, stdout); assert(size == size_t(columns)); str += columns; console_cb_column += columns; continue; } if (*str == '\n') str++; else assert(console_cb_column < console_cb_columns); putchar('\n'); console_cb_row++; console_cb_column = 0; if (console_cb_row < console_cb_rows - 1) continue; answer = llgdb_readline("---Type to continue, or q to quit---"); for (cs = answer; isspace(*cs); cs++) ; if (*cs == 'q') { free(answer); puts("Quit"); console_cb_drop = true; return; } free(answer); console_cb_row = 0; } } #endif static void history_save(int rc, void *arg) { const char *history_filename = static_cast(arg); write_history(history_filename); } static std::string settings_show_raw(SBDebugger &dbg, const char *setting, const char *type) { SBCommandReturnObject Result; dbg.GetCommandInterpreter().HandleCommand(("settings show " + std::string(setting)).c_str(), Result, /*add_to_history*/ false); assert(Result.Succeeded()); // = 0) { char *prompt, *data, *data2, *start, *end; size_t data2_len; int do_nest = 0; prompt = xstrprintf("%*s>", (int)nesting); data = llgdb_readline(prompt); free(prompt); if (data == NULL) data = xstrdup("end"); for (start = data; isspace(*start); start++) ; for (end = data + strlen(data); end > start && isspace(end[-1]); end--) ; data2 = malloc(end - start + 1); memcpy(data2, start, end - start); data2[end - start] = '\0'; if (strcmp(data2, "python") == 0) do_nest = 1; if (strcmp(data2, "end") == 0) do_nest = -1; for (end = data2; *end && !isspace(*end); end++) ; *end = '\0'; /* Here is a bug in GDB, it does not recognize command shortcuts. */ if (strcmp(data2, "while") == 0 || strcmp(data2, "if") == 0 || strcmp(data2, "commands") == 0 || strcmp(data2, "while-stepping") == 0 || strcmp(data2, "stepping") == 0 || strcmp(data2, "ws") == 0) do_nest = 1; free(data2); nesting += do_nest; if (nesting < 0) break; data2 = mi_escape(data); free(data); data2_len = strlen(data2); cmd = xrealloc(cmd, cmd_len + 2 + data2_len + 2); cmd[cmd_len] = ' '; cmd[cmd_len + 1] = '"'; memcpy(&cmd[cmd_len + 2], data2, data2_len); cmd[cmd_len + 2 + data2_len] = '"'; cmd[cmd_len + 2 + data2_len + 1] = '\0'; cmd_len += 2 + data2_len + 1; free(data2); } mi_send(h, "-break-commands %s%s\n", cmd_param, cmd); free(cmd); free(bpnum); res = mi_get_response_blk(h); assert(res != NULL && res->next == NULL && res->tclass == MI_CL_DONE && res->c == NULL); mi_free_output(res); #endif } struct Cmd { const char *name; int min_shortcut; typedef void (func_t)(SBDebugger &dbg, const char *cmd); func_t *func; Cmd(const char *name_, int min_shortcut_, func_t *func_):name(name_),min_shortcut(min_shortcut_),func(func_) {} }; const Cmd cmds[] = { Cmd("quit", 1, quit_command), Cmd("start", 5, start_command), Cmd("commands", 4, commands_command), }; static void executecommand(SBDebugger &dbg, const char *cmd_s) { const char *start, *end, *cs; cs = cmd_s; while (isspace(*cs)) cs++; start = cs; while (isalnum(*cs)) cs++; end = cs; while (isspace(*cs)) cs++; for (const Cmd &cmd : cmds) if (cmd.min_shortcut <= end - start && size_t(end - start) <= strlen(cmd.name) && strncmp(start, cmd.name, end - start) == 0) return cmd.func(dbg, cs); return default_command(dbg, cmd_s); } static char *xstrdup(const char *cs) { char *s = static_cast(malloc(strlen(cs)+1)); assert(s); strcpy(s,cs); return s; } static SBDebugger *completion_entry_function_dbgp; static char *completion_entry_function(const char *text, int matches) { SBDebugger &dbg = *completion_entry_function_dbgp; static std::vector found_vec; assert(matches >= 0); if (matches == 0) { found_vec.clear(); SBStringList matches; // LLDB: FIXME: These two values are not implemented. m_match_start_point; m_max_return_elements; const int match_start_point = 0; // LLDB: Only max_return_elements == -1 is supported at present: const int max_return_elements = -1; int got=dbg.GetCommandInterpreter().HandleCompletion(text, strlen(text)/*cursor_pos*/, match_start_point, max_return_elements, matches); assert(matches.IsValid()); if (unsigned(got+1)!=matches.GetSize()) error(1,0,"HandleCompletion=%d +1 != %u=matches.GetSize()",got,matches.GetSize()); //for (size_t ix=0;ix\n",ix,matches.GetStringAtIndex(ix)); const char *first=matches.GetStringAtIndex(0); assert(matches.GetSize()>=1); if (first[0]) { std::string str(first); while (!str.empty()&&str.back()==' ') str.pop_back(); found_vec.push_back(text + str); } else for (size_t ix=1;ix opt_iex, opt_ex; static std::optional opt_exec, opt_core; static std::vector opt_args; std::string string_quote(std::string &&str) { std::string retval = "\""; for (const char c : str) { if (c != '"') retval.push_back(c); else retval += "\\\""; } return retval + "\""; } std::optional next_arg() { if (!opt_iex.empty()) { std::string retval = opt_iex.front(); opt_iex.pop_front(); return retval; } if (opt_exec || opt_core) { std::string cmd = "target create"; if (opt_exec) { cmd += " " + string_quote(std::move(*opt_exec)); opt_exec.reset(); } if (opt_core) { cmd += " --core " + string_quote(std::move(*opt_core)); opt_core.reset(); } return cmd; } if (!opt_ex.empty()) { std::string retval = opt_ex.front(); opt_ex.pop_front(); return retval; } return {}; } int main(int argc, char **argv) { llvm::StringRef ToolName = argv[0]; llvm::sys::PrintStackTraceOnErrorSignal(ToolName); llvm::PrettyStackTraceProgram X(argc, argv); for (int argi = 1; argi < argc; ++argi) { std::string arg = argv[argi]; if (arg == "-iex") { const char *cmd = argv[++argi]; assert(cmd); opt_iex.push_back(cmd); } else if (arg == "-ex") { const char *cmd = argv[++argi]; assert(cmd); opt_ex.push_back(cmd); } else if (arg == "--args") { const char *exec = argv[++argi]; assert(exec); assert(!opt_exec); assert(!opt_core); assert(opt_args.empty()); opt_exec=std::string(exec); while (++argi(const_cast(history_filename.c_str()))); /* Do not free HISTORY_FILENAME. */ } for (;;) { std::string prompt = settings_show_string(dbg, "prompt"); std::optional arg = next_arg(); std::string cmd; if (!arg) { char *cmd_s = llgdb_readline(prompt.c_str()); if (cmd_s) add_history(cmd_s); cmd = cmd_s?:"quit"; free(cmd_s); } else { puts((prompt + *arg).c_str()); cmd = std::move(*arg); } #if 0 /* FIXME: -ex commands do not have pagination set. */ if (false /*!settings_show_bool(h, "pagination")*/) console_cb_rows = 0; else rl_get_screen_size(&console_cb_rows, &console_cb_columns); console_cb_drop = false; console_cb_row = console_cb_column = 0; #endif executecommand(dbg, cmd.c_str()); } }