--- /dev/null
+//===-- LLGDBMain.cpp -------------------------------------------*- C++ -*-===//
+//
+//
+//===----------------------------------------------------------------------===//
+
+#include <lldb/API/SBHostOS.h>
+#include <llvm/ADT/StringRef.h>
+#include <llvm/Support/PrettyStackTrace.h>
+#include <lldb/API/SBDebugger.h>
+#include <lldb/Utility/StreamString.h>
+#include <llvm/Support/Signals.h>
+#include <lldb/API/SBCommandInterpreter.h>
+#include <lldb/API/SBCommandReturnObject.h>
+#include <lldb/API/SBStringList.h>
+#include <boost/format.hpp>
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <vector>
+#include <deque>
+#include <setjmp.h>
+#include <signal.h>
+#include <error.h>
+
+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 <return> to continue, or q <return> 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<const char *>(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());
+ // <plugin.process.gdb-remote.packet-timeout (unsigned) = 1\n
+ std::string str = Result.GetOutput();
+ const std::string expect = (boost::format("%1% (%2%) = ") % setting % type).str();
+ if (str.compare(0,expect.length(),expect)!=0||str[str.length()-1]!='\n')
+ error(1,0,"Unexpected 'settings show' response: expected=\"%s[...]\\n\" got=\"%s\"",expect.c_str(),str.c_str());
+ return str.substr(expect.length(),str.length()-1-expect.length());
+}
+
+#if 0
+static int settings_show_int(SBDebugger &dbg, const char *setting) {
+ std::string string = settings_show_raw(dbg, setting,"unsigned");
+ long l;
+ int retval;
+ char *end;
+
+ errno = 0;
+ retval = l = strtol(string.c_str(), &end, 10);
+ assert(errno == 0 && (end == NULL || *end == '\0') && retval == l);
+ return retval;
+}
+
+static bool settings_show_bool(SBDebugger &dbg, const char *setting) {
+ std::string string = settings_show_raw(dbg, setting,"unsigned");
+ bool retval;
+
+ retval = (string == "on");
+ assert(retval || string == "off");
+ return retval;
+}
+#endif
+
+static std::string settings_show_string(SBDebugger &dbg, const char *setting) {
+ std::string val = settings_show_raw(dbg,setting,"string");
+ if (val.length()<2 || val[0] != '"' || val[val.length()-1]!='"')
+ error(1,0,"Unexpected 'settings show' string response: expected '\"' quoting: %s", val.c_str());
+ return val.substr(1,val.length()-2);
+}
+
+static SBCommandReturnObject subcommand(SBDebugger &dbg, const char *cmd, bool print_command=true, bool add_to_history=false) {
+ if (print_command)
+ printf("%s%s\n",settings_show_string(dbg, "prompt").c_str(),cmd);
+ SBCommandReturnObject Result;
+ dbg.GetCommandInterpreter().HandleCommand(cmd, Result, add_to_history);
+ Result.PutOutput(stdout);
+ Result.PutError(stdout);
+ return Result;
+}
+
+static void default_command(SBDebugger &dbg, const char *cmd) {
+ subcommand(dbg, cmd, false/*print_command*/, true/*add_to_history*/);
+}
+
+static void quit_command(SBDebugger &dbg, const char *cmd) {
+ exit(EXIT_SUCCESS);
+}
+
+static void start_command(SBDebugger &dbg, const char *cmd) {
+ if (!subcommand(dbg, "b main").Succeeded())
+ return;
+ subcommand(dbg, ("run "+std::string(cmd)).c_str());
+}
+
+static void commands_command(SBDebugger &dbg, const char *cmd_param) {
+#if 0
+ char *cmd = xstrdup("");
+ size_t cmd_len = 0;
+ mi_output *res;
+ char *bpnum = NULL;
+ int nesting;
+
+ if (*cmd_param == '\0') {
+ mi_set_console_cb(h, commands_command_console_cb, &bpnum);
+ dbg.HandleCommand(h, "output $bpnum");
+ mi_set_console_cb(h, console_cb, NULL);
+ assert(bpnum != NULL);
+ cmd_param = bpnum;
+ }
+
+ printf(_("Type commands for breakpoint(s) %s, one per line.\n"
+ "End with a line saying just \"end\".\n"),
+ cmd_param);
+
+ nesting = 0;
+ while (nesting >= 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<char *>(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<std::string> 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<matches.GetSize();++ix)
+ // fprintf(stderr,"match[%zu]=<%s>\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<matches.GetSize();++ix)
+ found_vec.push_back(matches.GetStringAtIndex(ix));
+ }
+ if (size_t(matches) < found_vec.size())
+ return xstrdup(found_vec[matches].c_str());
+ else if (size_t(matches) == found_vec.size())
+ return nullptr;
+ else
+ assert(0);
+}
+
+static std::deque<std::string> opt_iex, opt_ex;
+static std::optional<std::string> opt_exec, opt_core;
+static std::vector<std::string> 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<std::string> 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<argc) {
+ arg = argv[argi];
+ opt_args.push_back(std::move(arg));
+ }
+ } else if (!opt_exec)
+ opt_exec=std::move(arg);
+ else if (!opt_core)
+ opt_core=std::move(arg);
+ else
+ error(1,0,"Excessive argument: %s",arg.c_str());
+ }
+
+ SBDebugger::Initialize();
+ SBDebugger dbg = SBDebugger::Create();
+ dbg.HandleCommand("settings set symbols.enable-external-lookup false");
+
+ completion_entry_function_dbgp=&dbg;
+ rl_completion_entry_function = completion_entry_function;
+ rl_readline_name = "llgdb";
+
+ if (true/*settings_show_bool(dbg, "history save")*/) {
+ int history_size = 1000000; //settings_show_int(dbg, "history size");
+ const char *HOME = getenv("HOME");
+ assert(HOME);
+ static const std::string history_filename = std::string(HOME) + "/.llgdb_history"; // settings_show_string(dbg, "history filename");
+ //gdb_set_bool(dbg, "history save", false);
+ stifle_history(history_size);
+ read_history(history_filename.c_str());
+ on_exit(history_save, static_cast<void *>(const_cast<char *>(history_filename.c_str())));
+ /* Do not free HISTORY_FILENAME. */
+ }
+
+ for (;;) {
+
+ std::string prompt = settings_show_string(dbg, "prompt");
+ std::optional<std::string> 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());
+ }
+}