init; based on: https://github.com/lldb-tools/lldb-mi
authorJan Kratochvil <jan.kratochvil@redhat.com>
Wed, 24 Jul 2019 09:46:09 +0000 (11:46 +0200)
committerJan Kratochvil <jan.kratochvil@redhat.com>
Tue, 6 Aug 2019 10:44:20 +0000 (12:44 +0200)
.gitignore [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
src/CMakeLists.txt [new file with mode: 0644]
src/LLGDBMain.cpp [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..f21acfe
--- /dev/null
@@ -0,0 +1,5 @@
+CMakeFiles
+Makefile
+cmake_install.cmake
+/CMakeCache.txt
+src/llgdb
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3add18c
--- /dev/null
@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 3.11)
+project(llgdb)
+
+find_package(LLVM REQUIRED CONFIG)
+message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
+message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
+
+include_directories(${LLVM_INCLUDE_DIRS})
+if(LLVM_BUILD_MAIN_SRC_DIR)
+  include_directories(${LLVM_BUILD_MAIN_SRC_DIR}/tools/clang/include)
+  include_directories(${LLVM_BUILD_BINARY_DIR}/tools/clang/include)
+endif()
+link_directories(${LLVM_LIBRARY_DIRS})
+add_definitions(${LLVM_DEFINITIONS})
+
+add_definitions(-D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS)
+
+include(CheckCXXCompilerFlag)
+check_cxx_compiler_flag(-Wall temp)
+if(temp)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
+endif()
+
+set(CMAKE_CXX_STANDARD 17)
+
+add_subdirectory(src)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..df35350
--- /dev/null
@@ -0,0 +1,5 @@
+add_executable(llgdb
+  LLGDBMain.cpp
+)
+
+target_link_libraries(llgdb lldb LLVM readline)
diff --git a/src/LLGDBMain.cpp b/src/LLGDBMain.cpp
new file mode 100644 (file)
index 0000000..39dac35
--- /dev/null
@@ -0,0 +1,469 @@
+//===-- 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());
+  }
+}