[Lldb-commits] [lldb] r366830 - [Utils] Add back utils directory

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Tue Jul 23 10:47:09 PDT 2019


Author: jdevlieghere
Date: Tue Jul 23 10:47:08 2019
New Revision: 366830

URL: http://llvm.org/viewvc/llvm-project?rev=366830&view=rev
Log:
[Utils] Add back utils directory

Due to a bug my earlier commit removed the whole utils directory:
https://reviews.llvm.org/D65123

Added:
    lldb/trunk/utils/
    lldb/trunk/utils/TableGen/
    lldb/trunk/utils/TableGen/CMakeLists.txt
    lldb/trunk/utils/TableGen/LLDBOptionDefEmitter.cpp
    lldb/trunk/utils/TableGen/LLDBTableGen.cpp
    lldb/trunk/utils/TableGen/LLDBTableGenBackends.h
    lldb/trunk/utils/lit-cpuid/
    lldb/trunk/utils/lit-cpuid/CMakeLists.txt
    lldb/trunk/utils/lit-cpuid/lit-cpuid.cpp
    lldb/trunk/utils/lldb-dotest/
    lldb/trunk/utils/lldb-dotest/CMakeLists.txt
    lldb/trunk/utils/lldb-dotest/lldb-dotest.in   (with props)
    lldb/trunk/utils/lui/
    lldb/trunk/utils/lui/Readme
    lldb/trunk/utils/lui/breakwin.py
    lldb/trunk/utils/lui/commandwin.py
    lldb/trunk/utils/lui/cui.py   (with props)
    lldb/trunk/utils/lui/debuggerdriver.py
    lldb/trunk/utils/lui/eventwin.py
    lldb/trunk/utils/lui/lldbutil.py
    lldb/trunk/utils/lui/lui.py   (with props)
    lldb/trunk/utils/lui/sandbox.py   (with props)
    lldb/trunk/utils/lui/sourcewin.py
    lldb/trunk/utils/lui/statuswin.py
    lldb/trunk/utils/sync-source/
    lldb/trunk/utils/sync-source/README.txt
    lldb/trunk/utils/sync-source/lib/
    lldb/trunk/utils/sync-source/lib/transfer/
    lldb/trunk/utils/sync-source/lib/transfer/__init__.py
    lldb/trunk/utils/sync-source/lib/transfer/protocol.py
    lldb/trunk/utils/sync-source/lib/transfer/rsync.py
    lldb/trunk/utils/sync-source/lib/transfer/transfer_spec.py
    lldb/trunk/utils/sync-source/pylintrc
    lldb/trunk/utils/sync-source/syncsource.py
    lldb/trunk/utils/test/
    lldb/trunk/utils/test/README-disasm
    lldb/trunk/utils/test/README-lldb-disasm
    lldb/trunk/utils/test/README-run-until-faulted
    lldb/trunk/utils/test/disasm.py   (with props)
    lldb/trunk/utils/test/lldb-disasm.py   (with props)
    lldb/trunk/utils/test/llvm-mc-shell.py   (with props)
    lldb/trunk/utils/test/main.c
    lldb/trunk/utils/test/ras.py   (with props)
    lldb/trunk/utils/test/run-dis.py   (with props)
    lldb/trunk/utils/test/run-until-faulted.py   (with props)
    lldb/trunk/utils/vim-lldb/
    lldb/trunk/utils/vim-lldb/README
    lldb/trunk/utils/vim-lldb/doc/
    lldb/trunk/utils/vim-lldb/doc/lldb.txt
    lldb/trunk/utils/vim-lldb/plugin/
    lldb/trunk/utils/vim-lldb/plugin/lldb.vim
    lldb/trunk/utils/vim-lldb/python-vim-lldb/
    lldb/trunk/utils/vim-lldb/python-vim-lldb/import_lldb.py
    lldb/trunk/utils/vim-lldb/python-vim-lldb/lldb_controller.py
    lldb/trunk/utils/vim-lldb/python-vim-lldb/plugin.py
    lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_panes.py
    lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_signs.py
    lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_ui.py

Added: lldb/trunk/utils/TableGen/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/TableGen/CMakeLists.txt?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/TableGen/CMakeLists.txt (added)
+++ lldb/trunk/utils/TableGen/CMakeLists.txt Tue Jul 23 10:47:08 2019
@@ -0,0 +1,14 @@
+# tablegen targets get exported via llvm for LLVMConfig.cmake. So standalone
+# builds of lldb can potentially import this via LLVMConfig and also attempt to
+# build it in tree. So only build it if it doesn't exist.
+if (TARGET lldb-tblgen)
+  set(LLDB_TABLEGEN_EXE $<TARGET_FILE:lldb-tblgen> CACHE STRING "")
+else()
+  set(LLVM_LINK_COMPONENTS Support)
+
+  add_tablegen(lldb-tblgen LLDB
+    LLDBOptionDefEmitter.cpp
+    LLDBTableGen.cpp
+    )
+  set_target_properties(lldb-tblgen PROPERTIES FOLDER "LLDB tablegenning")
+endif()

Added: lldb/trunk/utils/TableGen/LLDBOptionDefEmitter.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/TableGen/LLDBOptionDefEmitter.cpp?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/TableGen/LLDBOptionDefEmitter.cpp (added)
+++ lldb/trunk/utils/TableGen/LLDBOptionDefEmitter.cpp Tue Jul 23 10:47:08 2019
@@ -0,0 +1,153 @@
+//===- TableGen.cpp - Top-Level TableGen implementation for Clang ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// These tablegen backends emits LLDB's OptionDefinition values for different
+// LLDB commands.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDBTableGenBackends.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/StringMatcher.h"
+#include "llvm/TableGen/TableGenBackend.h"
+#include <map>
+#include <vector>
+
+using namespace llvm;
+
+/// Map of command names to their associated records. Also makes sure our
+/// commands are sorted in a deterministic way.
+typedef std::map<std::string, std::vector<Record *>> RecordsByCommand;
+
+/// Groups all records by their command.
+static RecordsByCommand getCommandList(std::vector<Record *> Options) {
+  RecordsByCommand result;
+  for (Record *Option : Options)
+    result[Option->getValueAsString("Command").str()].push_back(Option);
+  return result;
+}
+
+static void emitOption(Record *Option, raw_ostream &OS) {
+  OS << "  {";
+
+  // List of option groups this option is in.
+  std::vector<std::string> GroupsArg;
+
+  if (Option->getValue("Groups")) {
+    // The user specified a list of groups.
+    auto Groups = Option->getValueAsListOfInts("Groups");
+    for (int Group : Groups)
+      GroupsArg.push_back("LLDB_OPT_SET_" + std::to_string(Group));
+  } else if (Option->getValue("GroupStart")) {
+    // The user specified a range of groups (with potentially only one element).
+    int GroupStart = Option->getValueAsInt("GroupStart");
+    int GroupEnd = Option->getValueAsInt("GroupEnd");
+    for (int i = GroupStart; i <= GroupEnd; ++i)
+      GroupsArg.push_back("LLDB_OPT_SET_" + std::to_string(i));
+  }
+
+  // If we have any groups, we merge them. Otherwise we move this option into
+  // the all group.
+  if (GroupsArg.empty())
+    OS << "LLDB_OPT_SET_ALL";
+  else
+    OS << llvm::join(GroupsArg.begin(), GroupsArg.end(), " | ");
+
+  OS << ", ";
+
+  // Check if this option is required.
+  OS << (Option->getValue("Required") ? "true" : "false");
+
+  // Add the full and short name for this option.
+  OS << ", \"" << Option->getValueAsString("FullName") << "\", ";
+  OS << '\'' << Option->getValueAsString("ShortName") << "'";
+
+  auto ArgType = Option->getValue("ArgType");
+  bool IsOptionalArg = Option->getValue("OptionalArg") != nullptr;
+
+  // Decide if we have either an option, required or no argument for this
+  // option.
+  OS << ", OptionParser::";
+  if (ArgType) {
+    if (IsOptionalArg)
+      OS << "eOptionalArgument";
+    else
+      OS << "eRequiredArgument";
+  } else
+    OS << "eNoArgument";
+  OS << ", nullptr, ";
+
+  if (Option->getValue("ArgEnum"))
+    OS << Option->getValueAsString("ArgEnum");
+  else
+    OS << "{}";
+  OS << ", ";
+
+  // Read the tab completions we offer for this option (if there are any)
+  if (Option->getValue("Completions")) {
+    auto Completions = Option->getValueAsListOfStrings("Completions");
+    std::vector<std::string> CompletionArgs;
+    for (llvm::StringRef Completion : Completions)
+      CompletionArgs.push_back("CommandCompletions::e" + Completion.str() +
+                               "Completion");
+
+    OS << llvm::join(CompletionArgs.begin(), CompletionArgs.end(), " | ");
+  } else {
+    OS << "CommandCompletions::eNoCompletion";
+  }
+
+  // Add the argument type.
+  OS << ", eArgType";
+  if (ArgType) {
+    OS << ArgType->getValue()->getAsUnquotedString();
+  } else
+    OS << "None";
+  OS << ", ";
+
+  // Add the description if there is any.
+  if (auto D = Option->getValue("Description")) {
+    OS << "\"";
+    llvm::printEscapedString(D->getValue()->getAsUnquotedString(), OS);
+    OS << "\"";
+  } else
+    OS << "\"\"";
+  OS << "},\n";
+}
+
+/// Emits all option initializers to the raw_ostream.
+static void emitOptions(std::string Command, std::vector<Record *> Option,
+                        raw_ostream &OS) {
+  // Generate the macro that the user needs to define before including the
+  // *.inc file.
+  std::string NeededMacro = "LLDB_OPTIONS_" + Command;
+  std::replace(NeededMacro.begin(), NeededMacro.end(), ' ', '_');
+
+  // All options are in one file, so we need put them behind macros and ask the
+  // user to define the macro for the options that are needed.
+  OS << "// Options for " << Command << "\n";
+  OS << "#ifdef " << NeededMacro << "\n";
+  for (Record *R : Option)
+    emitOption(R, OS);
+  // We undefine the macro for the user like Clang's include files are doing it.
+  OS << "#undef " << NeededMacro << "\n";
+  OS << "#endif // " << Command << " command\n\n";
+}
+
+void lldb_private::EmitOptionDefs(RecordKeeper &Records, raw_ostream &OS) {
+
+  std::vector<Record *> Options = Records.getAllDerivedDefinitions("Option");
+
+  emitSourceFileHeader("Options for LLDB command line commands.", OS);
+
+  RecordsByCommand ByCommand = getCommandList(Options);
+
+  for (auto &CommandRecordPair : ByCommand) {
+    emitOptions(CommandRecordPair.first, CommandRecordPair.second, OS);
+  }
+}

Added: lldb/trunk/utils/TableGen/LLDBTableGen.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/TableGen/LLDBTableGen.cpp?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/TableGen/LLDBTableGen.cpp (added)
+++ lldb/trunk/utils/TableGen/LLDBTableGen.cpp Tue Jul 23 10:47:08 2019
@@ -0,0 +1,71 @@
+//===- TableGen.cpp - Top-Level TableGen implementation for Clang ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the main function for Clang's TableGen.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDBTableGenBackends.h" // Declares all backends.
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Main.h"
+#include "llvm/TableGen/Record.h"
+
+using namespace llvm;
+using namespace lldb_private;
+
+enum ActionType {
+  PrintRecords,
+  DumpJSON,
+  GenOptionDefs,
+};
+
+static cl::opt<ActionType>
+    Action(cl::desc("Action to perform:"),
+           cl::values(clEnumValN(PrintRecords, "print-records",
+                                 "Print all records to stdout (default)"),
+                      clEnumValN(DumpJSON, "dump-json",
+                                 "Dump all records as machine-readable JSON"),
+                      clEnumValN(GenOptionDefs, "gen-lldb-option-defs",
+                                 "Generate clang attribute clases")));
+
+static bool LLDBTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
+  switch (Action) {
+  case PrintRecords:
+    OS << Records; // No argument, dump all contents
+    break;
+  case DumpJSON:
+    EmitJSON(Records, OS);
+    break;
+  case GenOptionDefs:
+    EmitOptionDefs(Records, OS);
+    break;
+  }
+  return false;
+}
+
+int main(int argc, char **argv) {
+  sys::PrintStackTraceOnErrorSignal(argv[0]);
+  PrettyStackTraceProgram X(argc, argv);
+  cl::ParseCommandLineOptions(argc, argv);
+
+  llvm_shutdown_obj Y;
+
+  return TableGenMain(argv[0], &LLDBTableGenMain);
+}
+
+#ifdef __has_feature
+#if __has_feature(address_sanitizer)
+#include <sanitizer/lsan_interface.h>
+// Disable LeakSanitizer for this binary as it has too many leaks that are not
+// very interesting to fix. See compiler-rt/include/sanitizer/lsan_interface.h .
+int __lsan_is_turned_off() { return 1; }
+#endif // __has_feature(address_sanitizer)
+#endif // defined(__has_feature)

Added: lldb/trunk/utils/TableGen/LLDBTableGenBackends.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/TableGen/LLDBTableGenBackends.h?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/TableGen/LLDBTableGenBackends.h (added)
+++ lldb/trunk/utils/TableGen/LLDBTableGenBackends.h Tue Jul 23 10:47:08 2019
@@ -0,0 +1,34 @@
+//===- TableGen.cpp - Top-Level TableGen implementation for Clang ---------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the declarations for all of the LLDB TableGen
+// backends. A "TableGen backend" is just a function. See
+// "$LLVM_ROOT/utils/TableGen/TableGenBackends.h" for more info.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LLDB_UTILS_TABLEGEN_TABLEGENBACKENDS_H
+#define LLVM_LLDB_UTILS_TABLEGEN_TABLEGENBACKENDS_H
+
+#include <string>
+
+namespace llvm {
+class raw_ostream;
+class RecordKeeper;
+} // namespace llvm
+
+using llvm::raw_ostream;
+using llvm::RecordKeeper;
+
+namespace lldb_private {
+
+void EmitOptionDefs(RecordKeeper &RK, raw_ostream &OS);
+
+} // namespace lldb_private
+
+#endif

Added: lldb/trunk/utils/lit-cpuid/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lit-cpuid/CMakeLists.txt?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lit-cpuid/CMakeLists.txt (added)
+++ lldb/trunk/utils/lit-cpuid/CMakeLists.txt Tue Jul 23 10:47:08 2019
@@ -0,0 +1,6 @@
+add_llvm_utility(lit-cpuid
+  lit-cpuid.cpp
+  )
+
+target_link_libraries(lit-cpuid PRIVATE LLVMSupport)
+set_target_properties(lit-cpuid PROPERTIES FOLDER "lldb utils")

Added: lldb/trunk/utils/lit-cpuid/lit-cpuid.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lit-cpuid/lit-cpuid.cpp?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lit-cpuid/lit-cpuid.cpp (added)
+++ lldb/trunk/utils/lit-cpuid/lit-cpuid.cpp Tue Jul 23 10:47:08 2019
@@ -0,0 +1,37 @@
+//===- lit-cpuid.cpp - Get CPU feature flags for lit exported features ----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// lit-cpuid obtains the feature list for the currently running CPU, and outputs
+// those flags that are interesting for LLDB lit tests.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+int main(int argc, char **argv) {
+#if defined(__i386__) || defined(_M_IX86) || \
+    defined(__x86_64__) || defined(_M_X64)
+  StringMap<bool> features;
+
+  if (!sys::getHostCPUFeatures(features))
+    return 1;
+
+  if (features["sse"])
+    outs() << "sse\n";
+  if (features["avx"])
+    outs() << "avx\n";
+  if (features["avx512f"])
+    outs() << "avx512f\n";
+#endif
+
+  return 0;
+}

Added: lldb/trunk/utils/lldb-dotest/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lldb-dotest/CMakeLists.txt?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lldb-dotest/CMakeLists.txt (added)
+++ lldb/trunk/utils/lldb-dotest/CMakeLists.txt Tue Jul 23 10:47:08 2019
@@ -0,0 +1,47 @@
+# Make lldb-dotest a custom target.
+add_custom_target(lldb-dotest)
+add_dependencies(lldb-dotest ${LLDB_TEST_DEPS})
+set_target_properties(lldb-dotest PROPERTIES FOLDER "lldb utils")
+
+get_property(LLDB_DOTEST_ARGS GLOBAL PROPERTY LLDB_DOTEST_ARGS_PROPERTY)
+
+# Generate lldb-dotest Python driver script for each build mode.
+if(LLDB_BUILT_STANDALONE)
+  set(config_types ".")
+  if(CMAKE_CONFIGURATION_TYPES)
+    set(config_types ${CMAKE_CONFIGURATION_TYPES})
+  endif()
+  foreach(config_type ${config_types})
+    # In paths to our build-tree, replace CMAKE_CFG_INTDIR with our actual configuration names.
+    string(REPLACE ${CMAKE_CFG_INTDIR} ${config_type} config_runtime_output_dir ${LLVM_RUNTIME_OUTPUT_INTDIR})
+    string(REPLACE ${LLVM_RUNTIME_OUTPUT_INTDIR} ${config_runtime_output_dir} LLDB_DOTEST_ARGS "${LLDB_DOTEST_ARGS}")
+
+    # Remaining ones must be paths to the provided LLVM build-tree.
+    if(${config_type} IN_LIST LLVM_CONFIGURATION_TYPES)
+      # Multi-configuration generator like Xcode (with a matching config).
+      string(REPLACE ${CMAKE_CFG_INTDIR} ${config_type} LLDB_DOTEST_ARGS "${LLDB_DOTEST_ARGS}")
+    else()
+      # Single-configuration generator like Ninja.
+      string(REPLACE ${CMAKE_CFG_INTDIR} "." LLDB_DOTEST_ARGS "${LLDB_DOTEST_ARGS}")
+    endif()
+
+    configure_file(
+      lldb-dotest.in
+      ${config_runtime_output_dir}/lldb-dotest @ONLY
+    )
+  endforeach()
+elseif(NOT "${CMAKE_CFG_INTDIR}" STREQUAL ".")
+  foreach(LLVM_BUILD_MODE ${CMAKE_CONFIGURATION_TYPES})
+    string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} LLDB_DOTEST_DIR ${LLVM_RUNTIME_OUTPUT_INTDIR})
+    string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} LLDB_DOTEST_ARGS "${LLDB_DOTEST_ARGS}")
+    configure_file(
+      lldb-dotest.in
+      ${LLDB_DOTEST_DIR}/lldb-dotest
+      )
+  endforeach()
+else()
+  configure_file(
+    lldb-dotest.in
+    ${LLVM_RUNTIME_OUTPUT_INTDIR}/lldb-dotest
+    )
+endif()

Added: lldb/trunk/utils/lldb-dotest/lldb-dotest.in
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lldb-dotest/lldb-dotest.in?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lldb-dotest/lldb-dotest.in (added)
+++ lldb/trunk/utils/lldb-dotest/lldb-dotest.in Tue Jul 23 10:47:08 2019
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+import subprocess
+import sys
+
+dotest_path = '@LLDB_SOURCE_DIR@/test/dotest.py'
+dotest_args_str = '@LLDB_DOTEST_ARGS@'
+
+if __name__ == '__main__':
+    wrapper_args = sys.argv[1:]
+    dotest_args = dotest_args_str.split(';')
+    # Build dotest.py command.
+    cmd = [sys.executable, dotest_path, '-q']
+    cmd.extend(dotest_args)
+    cmd.extend(wrapper_args)
+    # Invoke dotest.py and return exit code.
+    print(' '.join(cmd))
+    sys.exit(subprocess.call(cmd))

Propchange: lldb/trunk/utils/lldb-dotest/lldb-dotest.in
------------------------------------------------------------------------------
    svn:executable = *

Added: lldb/trunk/utils/lui/Readme
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lui/Readme?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lui/Readme (added)
+++ lldb/trunk/utils/lui/Readme Tue Jul 23 10:47:08 2019
@@ -0,0 +1,36 @@
+
+LLDB (Terminal) User Interface
+------------------------------
+
+This directory contains the curses user interface for LLDB. To use it, ensure Python can find your lldb module. You may have to modify PYTHONPATH for that purpose:
+
+$ export PYTHONPATH=/path/to/lldb/module
+
+Then, run the lui.py. To load a core file:
+$ ./lui.py --core core
+
+To create a target from an executable:
+$ ./lui.py /bin/echo "hello world"
+
+To attach to a running process:
+$ ./lui.py --attach <pid>
+
+
+Known Issues
+------------
+1. Resizing the terminal will most likely cause lui to crash.
+2. Missing paging in command-window
+3. Only minimal testing (on Ubuntu Linux x86_64)
+
+Missing Features
+----------------
+- stdin/stdout/stderr windows
+- memory window
+- backtrace window
+- threads window
+- tab-completion
+- syntax-highlighting (via pygments library)
+- (local) variables window
+- registers window
+- disassembly window
+- custom layout

Added: lldb/trunk/utils/lui/breakwin.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lui/breakwin.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lui/breakwin.py (added)
+++ lldb/trunk/utils/lui/breakwin.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,93 @@
+##===-- breakwin.py ------------------------------------------*- Python -*-===##
+##
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+##
+##===----------------------------------------------------------------------===##
+
+import cui
+import curses
+import lldb
+import lldbutil
+import re
+
+
+class BreakWin(cui.ListWin):
+
+    def __init__(self, driver, x, y, w, h):
+        super(BreakWin, self).__init__(x, y, w, h)
+        self.driver = driver
+        self.update()
+        self.showDetails = {}
+
+    def handleEvent(self, event):
+        if isinstance(event, lldb.SBEvent):
+            if lldb.SBBreakpoint.EventIsBreakpointEvent(event):
+                self.update()
+        if isinstance(event, int):
+            if event == ord('d'):
+                self.deleteSelected()
+            if event == curses.ascii.NL or event == curses.ascii.SP:
+                self.toggleSelected()
+            elif event == curses.ascii.TAB:
+                if self.getSelected() != -1:
+                    target = self.driver.getTarget()
+                    if not target.IsValid():
+                        return
+                    i = target.GetBreakpointAtIndex(self.getSelected()).id
+                    self.showDetails[i] = not self.showDetails[i]
+                    self.update()
+        super(BreakWin, self).handleEvent(event)
+
+    def toggleSelected(self):
+        if self.getSelected() == -1:
+            return
+        target = self.driver.getTarget()
+        if not target.IsValid():
+            return
+        bp = target.GetBreakpointAtIndex(self.getSelected())
+        bp.SetEnabled(not bp.IsEnabled())
+
+    def deleteSelected(self):
+        if self.getSelected() == -1:
+            return
+        target = self.driver.getTarget()
+        if not target.IsValid():
+            return
+        bp = target.GetBreakpointAtIndex(self.getSelected())
+        target.BreakpointDelete(bp.id)
+
+    def update(self):
+        target = self.driver.getTarget()
+        if not target.IsValid():
+            self.win.erase()
+            self.win.noutrefresh()
+            return
+        selected = self.getSelected()
+        self.clearItems()
+        for i in range(0, target.GetNumBreakpoints()):
+            bp = target.GetBreakpointAtIndex(i)
+            if bp.IsInternal():
+                continue
+            text = lldbutil.get_description(bp)
+            # FIXME: Use an API for this, not parsing the description.
+            match = re.search('SBBreakpoint: id = ([^,]+), (.*)', text)
+            try:
+                id = match.group(1)
+                desc = match.group(2).strip()
+                if bp.IsEnabled():
+                    text = '%s: %s' % (id, desc)
+                else:
+                    text = '%s: (disabled) %s' % (id, desc)
+            except ValueError as e:
+                # bp unparsable
+                pass
+
+            if self.showDetails.setdefault(bp.id, False):
+                for location in bp:
+                    desc = lldbutil.get_description(
+                        location, lldb.eDescriptionLevelFull)
+                    text += '\n  ' + desc
+            self.addItem(text)
+        self.setSelected(selected)

Added: lldb/trunk/utils/lui/commandwin.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lui/commandwin.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lui/commandwin.py (added)
+++ lldb/trunk/utils/lui/commandwin.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,130 @@
+##===-- commandwin.py ----------------------------------------*- Python -*-===##
+##
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+##
+##===----------------------------------------------------------------------===##
+
+import cui
+import curses
+import lldb
+from itertools import islice
+
+
+class History(object):
+
+    def __init__(self):
+        self.data = {}
+        self.pos = 0
+        self.tempEntry = ''
+
+    def previous(self, curr):
+        if self.pos == len(self.data):
+            self.tempEntry = curr
+
+        if self.pos < 0:
+            return ''
+        if self.pos == 0:
+            self.pos -= 1
+            return ''
+        if self.pos > 0:
+            self.pos -= 1
+            return self.data[self.pos]
+
+    def next(self):
+        if self.pos < len(self.data):
+            self.pos += 1
+
+        if self.pos < len(self.data):
+            return self.data[self.pos]
+        elif self.tempEntry != '':
+            return self.tempEntry
+        else:
+            return ''
+
+    def add(self, c):
+        self.tempEntry = ''
+        self.pos = len(self.data)
+        if self.pos == 0 or self.data[self.pos - 1] != c:
+            self.data[self.pos] = c
+            self.pos += 1
+
+
+class CommandWin(cui.TitledWin):
+
+    def __init__(self, driver, x, y, w, h):
+        super(CommandWin, self).__init__(x, y, w, h, "Commands")
+        self.command = ""
+        self.data = ""
+        driver.setSize(w, h)
+
+        self.win.scrollok(1)
+
+        self.driver = driver
+        self.history = History()
+
+        def enterCallback(content):
+            self.handleCommand(content)
+
+        def tabCompleteCallback(content):
+            self.data = content
+            matches = lldb.SBStringList()
+            commandinterpreter = self.getCommandInterpreter()
+            commandinterpreter.HandleCompletion(
+                self.data, self.el.index, 0, -1, matches)
+            if matches.GetSize() == 2:
+                self.el.content += matches.GetStringAtIndex(0)
+                self.el.index = len(self.el.content)
+                self.el.draw()
+            else:
+                self.win.move(self.el.starty, self.el.startx)
+                self.win.scroll(1)
+                self.win.addstr("Available Completions:")
+                self.win.scroll(1)
+                for m in islice(matches, 1, None):
+                    self.win.addstr(self.win.getyx()[0], 0, m)
+                    self.win.scroll(1)
+                self.el.draw()
+
+        self.startline = self.win.getmaxyx()[0] - 2
+
+        self.el = cui.CursesEditLine(
+            self.win,
+            self.history,
+            enterCallback,
+            tabCompleteCallback)
+        self.el.prompt = self.driver.getPrompt()
+        self.el.showPrompt(self.startline, 0)
+
+    def handleCommand(self, cmd):
+       # enter!
+        self.win.scroll(1)  # TODO: scroll more for longer commands
+        if cmd == '':
+            cmd = self.history.previous('')
+        elif cmd in ('q', 'quit'):
+            self.driver.terminate()
+            return
+
+        self.history.add(cmd)
+        ret = self.driver.handleCommand(cmd)
+        if ret.Succeeded():
+            out = ret.GetOutput()
+            attr = curses.A_NORMAL
+        else:
+            out = ret.GetError()
+            attr = curses.color_pair(3)  # red on black
+        self.win.addstr(self.startline, 0, out + '\n', attr)
+        self.win.scroll(1)
+        self.el.showPrompt(self.startline, 0)
+
+    def handleEvent(self, event):
+        if isinstance(event, int):
+            if event == curses.ascii.EOT and self.el.content == '':
+                # When the command is empty, treat CTRL-D as EOF.
+                self.driver.terminate()
+                return
+            self.el.handleEvent(event)
+
+    def getCommandInterpreter(self):
+        return self.driver.getCommandInterpreter()

Added: lldb/trunk/utils/lui/cui.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lui/cui.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lui/cui.py (added)
+++ lldb/trunk/utils/lui/cui.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,338 @@
+##===-- cui.py -----------------------------------------------*- Python -*-===##
+##
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+##
+##===----------------------------------------------------------------------===##
+
+import curses
+import curses.ascii
+import threading
+
+
+class CursesWin(object):
+
+    def __init__(self, x, y, w, h):
+        self.win = curses.newwin(h, w, y, x)
+        self.focus = False
+
+    def setFocus(self, focus):
+        self.focus = focus
+
+    def getFocus(self):
+        return self.focus
+
+    def canFocus(self):
+        return True
+
+    def handleEvent(self, event):
+        return
+
+    def draw(self):
+        return
+
+
+class TextWin(CursesWin):
+
+    def __init__(self, x, y, w):
+        super(TextWin, self).__init__(x, y, w, 1)
+        self.win.bkgd(curses.color_pair(1))
+        self.text = ''
+        self.reverse = False
+
+    def canFocus(self):
+        return False
+
+    def draw(self):
+        w = self.win.getmaxyx()[1]
+        text = self.text
+        if len(text) > w:
+            #trunc_length = len(text) - w
+            text = text[-w + 1:]
+        if self.reverse:
+            self.win.addstr(0, 0, text, curses.A_REVERSE)
+        else:
+            self.win.addstr(0, 0, text)
+        self.win.noutrefresh()
+
+    def setReverse(self, reverse):
+        self.reverse = reverse
+
+    def setText(self, text):
+        self.text = text
+
+
+class TitledWin(CursesWin):
+
+    def __init__(self, x, y, w, h, title):
+        super(TitledWin, self).__init__(x, y + 1, w, h - 1)
+        self.title = title
+        self.title_win = TextWin(x, y, w)
+        self.title_win.setText(title)
+        self.draw()
+
+    def setTitle(self, title):
+        self.title_win.setText(title)
+
+    def draw(self):
+        self.title_win.setReverse(self.getFocus())
+        self.title_win.draw()
+        self.win.noutrefresh()
+
+
+class ListWin(CursesWin):
+
+    def __init__(self, x, y, w, h):
+        super(ListWin, self).__init__(x, y, w, h)
+        self.items = []
+        self.selected = 0
+        self.first_drawn = 0
+        self.win.leaveok(True)
+
+    def draw(self):
+        if len(self.items) == 0:
+            self.win.erase()
+            return
+
+        h, w = self.win.getmaxyx()
+
+        allLines = []
+        firstSelected = -1
+        lastSelected = -1
+        for i, item in enumerate(self.items):
+            lines = self.items[i].split('\n')
+            lines = lines if lines[len(lines) - 1] != '' else lines[:-1]
+            if len(lines) == 0:
+                lines = ['']
+
+            if i == self.getSelected():
+                firstSelected = len(allLines)
+            allLines.extend(lines)
+            if i == self.selected:
+                lastSelected = len(allLines) - 1
+
+        if firstSelected < self.first_drawn:
+            self.first_drawn = firstSelected
+        elif lastSelected >= self.first_drawn + h:
+            self.first_drawn = lastSelected - h + 1
+
+        self.win.erase()
+
+        begin = self.first_drawn
+        end = begin + h
+
+        y = 0
+        for i, line in list(enumerate(allLines))[begin:end]:
+            attr = curses.A_NORMAL
+            if i >= firstSelected and i <= lastSelected:
+                attr = curses.A_REVERSE
+                line = '{0:{width}}'.format(line, width=w - 1)
+
+            # Ignore the error we get from drawing over the bottom-right char.
+            try:
+                self.win.addstr(y, 0, line[:w], attr)
+            except curses.error:
+                pass
+            y += 1
+        self.win.noutrefresh()
+
+    def getSelected(self):
+        if self.items:
+            return self.selected
+        return -1
+
+    def setSelected(self, selected):
+        self.selected = selected
+        if self.selected < 0:
+            self.selected = 0
+        elif self.selected >= len(self.items):
+            self.selected = len(self.items) - 1
+
+    def handleEvent(self, event):
+        if isinstance(event, int):
+            if len(self.items) > 0:
+                if event == curses.KEY_UP:
+                    self.setSelected(self.selected - 1)
+                if event == curses.KEY_DOWN:
+                    self.setSelected(self.selected + 1)
+                if event == curses.ascii.NL:
+                    self.handleSelect(self.selected)
+
+    def addItem(self, item):
+        self.items.append(item)
+
+    def clearItems(self):
+        self.items = []
+
+    def handleSelect(self, index):
+        return
+
+
+class InputHandler(threading.Thread):
+
+    def __init__(self, screen, queue):
+        super(InputHandler, self).__init__()
+        self.screen = screen
+        self.queue = queue
+
+    def run(self):
+        while True:
+            c = self.screen.getch()
+            self.queue.put(c)
+
+
+class CursesUI(object):
+    """ Responsible for updating the console UI with curses. """
+
+    def __init__(self, screen, event_queue):
+        self.screen = screen
+        self.event_queue = event_queue
+
+        curses.start_color()
+        curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
+        curses.init_pair(2, curses.COLOR_YELLOW, curses.COLOR_BLACK)
+        curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK)
+        self.screen.bkgd(curses.color_pair(1))
+        self.screen.clear()
+
+        self.input_handler = InputHandler(self.screen, self.event_queue)
+        self.input_handler.daemon = True
+
+        self.focus = 0
+
+        self.screen.refresh()
+
+    def focusNext(self):
+        self.wins[self.focus].setFocus(False)
+        old = self.focus
+        while True:
+            self.focus += 1
+            if self.focus >= len(self.wins):
+                self.focus = 0
+            if self.wins[self.focus].canFocus():
+                break
+        self.wins[self.focus].setFocus(True)
+
+    def handleEvent(self, event):
+        if isinstance(event, int):
+            if event == curses.KEY_F3:
+                self.focusNext()
+
+    def eventLoop(self):
+
+        self.input_handler.start()
+        self.wins[self.focus].setFocus(True)
+
+        while True:
+            self.screen.noutrefresh()
+
+            for i, win in enumerate(self.wins):
+                if i != self.focus:
+                    win.draw()
+            # Draw the focused window last so that the cursor shows up.
+            if self.wins:
+                self.wins[self.focus].draw()
+            curses.doupdate()  # redraw the physical screen
+
+            event = self.event_queue.get()
+
+            for win in self.wins:
+                if isinstance(event, int):
+                    if win.getFocus() or not win.canFocus():
+                        win.handleEvent(event)
+                else:
+                    win.handleEvent(event)
+            self.handleEvent(event)
+
+
+class CursesEditLine(object):
+    """ Embed an 'editline'-compatible prompt inside a CursesWin. """
+
+    def __init__(self, win, history, enterCallback, tabCompleteCallback):
+        self.win = win
+        self.history = history
+        self.enterCallback = enterCallback
+        self.tabCompleteCallback = tabCompleteCallback
+
+        self.prompt = ''
+        self.content = ''
+        self.index = 0
+        self.startx = -1
+        self.starty = -1
+
+    def draw(self, prompt=None):
+        if not prompt:
+            prompt = self.prompt
+        (h, w) = self.win.getmaxyx()
+        if (len(prompt) + len(self.content)) / w + self.starty >= h - 1:
+            self.win.scroll(1)
+            self.starty -= 1
+            if self.starty < 0:
+                raise RuntimeError('Input too long; aborting')
+        (y, x) = (self.starty, self.startx)
+
+        self.win.move(y, x)
+        self.win.clrtobot()
+        self.win.addstr(y, x, prompt)
+        remain = self.content
+        self.win.addstr(remain[:w - len(prompt)])
+        remain = remain[w - len(prompt):]
+        while remain != '':
+            y += 1
+            self.win.addstr(y, 0, remain[:w])
+            remain = remain[w:]
+
+        length = self.index + len(prompt)
+        self.win.move(self.starty + length / w, length % w)
+
+    def showPrompt(self, y, x, prompt=None):
+        self.content = ''
+        self.index = 0
+        self.startx = x
+        self.starty = y
+        self.draw(prompt)
+
+    def handleEvent(self, event):
+        if not isinstance(event, int):
+            return  # not handled
+        key = event
+
+        if self.startx == -1:
+            raise RuntimeError('Trying to handle input without prompt')
+
+        if key == curses.ascii.NL:
+            self.enterCallback(self.content)
+        elif key == curses.ascii.TAB:
+            self.tabCompleteCallback(self.content)
+        elif curses.ascii.isprint(key):
+            self.content = self.content[:self.index] + \
+                chr(key) + self.content[self.index:]
+            self.index += 1
+        elif key == curses.KEY_BACKSPACE or key == curses.ascii.BS:
+            if self.index > 0:
+                self.index -= 1
+                self.content = self.content[
+                    :self.index] + self.content[self.index + 1:]
+        elif key == curses.KEY_DC or key == curses.ascii.DEL or key == curses.ascii.EOT:
+            self.content = self.content[
+                :self.index] + self.content[self.index + 1:]
+        elif key == curses.ascii.VT:  # CTRL-K
+            self.content = self.content[:self.index]
+        elif key == curses.KEY_LEFT or key == curses.ascii.STX:  # left or CTRL-B
+            if self.index > 0:
+                self.index -= 1
+        elif key == curses.KEY_RIGHT or key == curses.ascii.ACK:  # right or CTRL-F
+            if self.index < len(self.content):
+                self.index += 1
+        elif key == curses.ascii.SOH:  # CTRL-A
+            self.index = 0
+        elif key == curses.ascii.ENQ:  # CTRL-E
+            self.index = len(self.content)
+        elif key == curses.KEY_UP or key == curses.ascii.DLE:  # up or CTRL-P
+            self.content = self.history.previous(self.content)
+            self.index = len(self.content)
+        elif key == curses.KEY_DOWN or key == curses.ascii.SO:  # down or CTRL-N
+            self.content = self.history.next()
+            self.index = len(self.content)
+        self.draw()

Propchange: lldb/trunk/utils/lui/cui.py
------------------------------------------------------------------------------
    svn:executable = *

Added: lldb/trunk/utils/lui/debuggerdriver.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lui/debuggerdriver.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lui/debuggerdriver.py (added)
+++ lldb/trunk/utils/lui/debuggerdriver.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,142 @@
+##===-- debuggerdriver.py ------------------------------------*- Python -*-===##
+##
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+##
+##===----------------------------------------------------------------------===##
+
+
+import lldb
+import lldbutil
+import sys
+from threading import Thread
+
+
+class DebuggerDriver(Thread):
+    """ Drives the debugger and responds to events. """
+
+    def __init__(self, debugger, event_queue):
+        Thread.__init__(self)
+        self.event_queue = event_queue
+        # This is probably not great because it does not give liblldb a chance
+        # to clean up
+        self.daemon = True
+        self.initialize(debugger)
+
+    def initialize(self, debugger):
+        self.done = False
+        self.debugger = debugger
+        self.listener = debugger.GetListener()
+        if not self.listener.IsValid():
+            raise "Invalid listener"
+
+        self.listener.StartListeningForEventClass(self.debugger,
+                                                  lldb.SBTarget.GetBroadcasterClassName(),
+                                                  lldb.SBTarget.eBroadcastBitBreakpointChanged
+                                                  #| lldb.SBTarget.eBroadcastBitModuleLoaded
+                                                  #| lldb.SBTarget.eBroadcastBitModuleUnloaded
+                                                  | lldb.SBTarget.eBroadcastBitWatchpointChanged
+                                                  #| lldb.SBTarget.eBroadcastBitSymbolLoaded
+                                                  )
+
+        self.listener.StartListeningForEventClass(self.debugger,
+                                                  lldb.SBThread.GetBroadcasterClassName(),
+                                                  lldb.SBThread.eBroadcastBitStackChanged
+                                                  #  lldb.SBThread.eBroadcastBitBreakpointChanged
+                                                  | lldb.SBThread.eBroadcastBitThreadSuspended
+                                                  | lldb.SBThread.eBroadcastBitThreadResumed
+                                                  | lldb.SBThread.eBroadcastBitSelectedFrameChanged
+                                                  | lldb.SBThread.eBroadcastBitThreadSelected
+                                                  )
+
+        self.listener.StartListeningForEventClass(self.debugger,
+                                                  lldb.SBProcess.GetBroadcasterClassName(),
+                                                  lldb.SBProcess.eBroadcastBitStateChanged
+                                                  | lldb.SBProcess.eBroadcastBitInterrupt
+                                                  | lldb.SBProcess.eBroadcastBitSTDOUT
+                                                  | lldb.SBProcess.eBroadcastBitSTDERR
+                                                  | lldb.SBProcess.eBroadcastBitProfileData
+                                                  )
+        self.listener.StartListeningForEventClass(self.debugger,
+                                                  lldb.SBCommandInterpreter.GetBroadcasterClass(),
+                                                  lldb.SBCommandInterpreter.eBroadcastBitThreadShouldExit
+                                                  | lldb.SBCommandInterpreter.eBroadcastBitResetPrompt
+                                                  | lldb.SBCommandInterpreter.eBroadcastBitQuitCommandReceived
+                                                  | lldb.SBCommandInterpreter.eBroadcastBitAsynchronousOutputData
+                                                  | lldb.SBCommandInterpreter.eBroadcastBitAsynchronousErrorData
+                                                  )
+
+    def createTarget(self, target_image, args=None):
+        self.handleCommand("target create %s" % target_image)
+        if args is not None:
+            self.handleCommand("settings set target.run-args %s" % args)
+
+    def attachProcess(self, pid):
+        self.handleCommand("process attach -p %d" % pid)
+        pass
+
+    def loadCore(self, corefile):
+        self.handleCommand("target create -c %s" % corefile)
+        pass
+
+    def setDone(self):
+        self.done = True
+
+    def isDone(self):
+        return self.done
+
+    def getPrompt(self):
+        return self.debugger.GetPrompt()
+
+    def getCommandInterpreter(self):
+        return self.debugger.GetCommandInterpreter()
+
+    def getSourceManager(self):
+        return self.debugger.GetSourceManager()
+
+    def setSize(self, width, height):
+        # FIXME: respect height
+        self.debugger.SetTerminalWidth(width)
+
+    def getTarget(self):
+        return self.debugger.GetTargetAtIndex(0)
+
+    def handleCommand(self, cmd):
+        ret = lldb.SBCommandReturnObject()
+        self.getCommandInterpreter().HandleCommand(cmd, ret)
+        return ret
+
+    def eventLoop(self):
+        while not self.isDone():
+            event = lldb.SBEvent()
+            got_event = self.listener.WaitForEvent(lldb.UINT32_MAX, event)
+            if got_event and not event.IsValid():
+                self.winAddStr("Warning: Invalid or no event...")
+                continue
+            elif not event.GetBroadcaster().IsValid():
+                continue
+
+            self.event_queue.put(event)
+
+    def run(self):
+        self.eventLoop()
+
+    def terminate(self):
+        lldb.SBDebugger.Terminate()
+        sys.exit(0)
+
+
+def createDriver(debugger, event_queue):
+    driver = DebuggerDriver(debugger, event_queue)
+    # driver.start()
+    # if pid specified:
+    # - attach to pid
+    # else if core file specified
+    # - create target from corefile
+    # else
+    # - create target from file
+    # - settings append target.run-args <args-from-cmdline>
+    # source .lldbinit file
+
+    return driver

Added: lldb/trunk/utils/lui/eventwin.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lui/eventwin.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lui/eventwin.py (added)
+++ lldb/trunk/utils/lui/eventwin.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,26 @@
+##===-- eventwin.py ------------------------------------------*- Python -*-===##
+##
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+##
+##===----------------------------------------------------------------------===##
+
+import cui
+import lldb
+import lldbutil
+
+
+class EventWin(cui.TitledWin):
+
+    def __init__(self, x, y, w, h):
+        super(EventWin, self).__init__(x, y, w, h, 'LLDB Event Log')
+        self.win.scrollok(1)
+        super(EventWin, self).draw()
+
+    def handleEvent(self, event):
+        if isinstance(event, lldb.SBEvent):
+            self.win.scroll()
+            h = self.win.getmaxyx()[0]
+            self.win.addstr(h - 1, 0, lldbutil.get_description(event))
+        return

Added: lldb/trunk/utils/lui/lldbutil.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lui/lldbutil.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lui/lldbutil.py (added)
+++ lldb/trunk/utils/lui/lldbutil.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,1040 @@
+##===-- lldbutil.py ------------------------------------------*- Python -*-===##
+##
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+##
+##===----------------------------------------------------------------------===##
+
+"""
+This LLDB module contains miscellaneous utilities.
+Some of the test suite takes advantage of the utility functions defined here.
+They can also be useful for general purpose lldb scripting.
+"""
+
+from __future__ import print_function
+
+import lldb
+import os
+import sys
+import io
+
+# ===================================================
+# Utilities for locating/checking executable programs
+# ===================================================
+
+
+def is_exe(fpath):
+    """Returns True if fpath is an executable."""
+    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+
+
+def which(program):
+    """Returns the full path to a program; None otherwise."""
+    fpath, fname = os.path.split(program)
+    if fpath:
+        if is_exe(program):
+            return program
+    else:
+        for path in os.environ["PATH"].split(os.pathsep):
+            exe_file = os.path.join(path, program)
+            if is_exe(exe_file):
+                return exe_file
+    return None
+
+# ===================================================
+# Disassembly for an SBFunction or an SBSymbol object
+# ===================================================
+
+
+def disassemble(target, function_or_symbol):
+    """Disassemble the function or symbol given a target.
+
+    It returns the disassembly content in a string object.
+    """
+    buf = io.StringIO()
+    insts = function_or_symbol.GetInstructions(target)
+    for i in insts:
+        print(i, file=buf)
+    return buf.getvalue()
+
+# ==========================================================
+# Integer (byte size 1, 2, 4, and 8) to bytearray conversion
+# ==========================================================
+
+
+def int_to_bytearray(val, bytesize):
+    """Utility function to convert an integer into a bytearray.
+
+    It returns the bytearray in the little endian format.  It is easy to get the
+    big endian format, just do ba.reverse() on the returned object.
+    """
+    import struct
+
+    if bytesize == 1:
+        return bytearray([val])
+
+    # Little endian followed by a format character.
+    template = "<%c"
+    if bytesize == 2:
+        fmt = template % 'h'
+    elif bytesize == 4:
+        fmt = template % 'i'
+    elif bytesize == 4:
+        fmt = template % 'q'
+    else:
+        return None
+
+    packed = struct.pack(fmt, val)
+    return bytearray(ord(x) for x in packed)
+
+
+def bytearray_to_int(bytes, bytesize):
+    """Utility function to convert a bytearray into an integer.
+
+    It interprets the bytearray in the little endian format. For a big endian
+    bytearray, just do ba.reverse() on the object before passing it in.
+    """
+    import struct
+
+    if bytesize == 1:
+        return bytes[0]
+
+    # Little endian followed by a format character.
+    template = "<%c"
+    if bytesize == 2:
+        fmt = template % 'h'
+    elif bytesize == 4:
+        fmt = template % 'i'
+    elif bytesize == 4:
+        fmt = template % 'q'
+    else:
+        return None
+
+    unpacked = struct.unpack(fmt, str(bytes))
+    return unpacked[0]
+
+
+# ==============================================================
+# Get the description of an lldb object or None if not available
+# ==============================================================
+def get_description(obj, option=None):
+    """Calls lldb_obj.GetDescription() and returns a string, or None.
+
+    For SBTarget, SBBreakpointLocation, and SBWatchpoint lldb objects, an extra
+    option can be passed in to describe the detailed level of description
+    desired:
+        o lldb.eDescriptionLevelBrief
+        o lldb.eDescriptionLevelFull
+        o lldb.eDescriptionLevelVerbose
+    """
+    method = getattr(obj, 'GetDescription')
+    if not method:
+        return None
+    tuple = (lldb.SBTarget, lldb.SBBreakpointLocation, lldb.SBWatchpoint)
+    if isinstance(obj, tuple):
+        if option is None:
+            option = lldb.eDescriptionLevelBrief
+
+    stream = lldb.SBStream()
+    if option is None:
+        success = method(stream)
+    else:
+        success = method(stream, option)
+    if not success:
+        return None
+    return stream.GetData()
+
+
+# =================================================
+# Convert some enum value to its string counterpart
+# =================================================
+
+def state_type_to_str(enum):
+    """Returns the stateType string given an enum."""
+    if enum == lldb.eStateInvalid:
+        return "invalid"
+    elif enum == lldb.eStateUnloaded:
+        return "unloaded"
+    elif enum == lldb.eStateConnected:
+        return "connected"
+    elif enum == lldb.eStateAttaching:
+        return "attaching"
+    elif enum == lldb.eStateLaunching:
+        return "launching"
+    elif enum == lldb.eStateStopped:
+        return "stopped"
+    elif enum == lldb.eStateRunning:
+        return "running"
+    elif enum == lldb.eStateStepping:
+        return "stepping"
+    elif enum == lldb.eStateCrashed:
+        return "crashed"
+    elif enum == lldb.eStateDetached:
+        return "detached"
+    elif enum == lldb.eStateExited:
+        return "exited"
+    elif enum == lldb.eStateSuspended:
+        return "suspended"
+    else:
+        raise Exception("Unknown StateType enum")
+
+
+def stop_reason_to_str(enum):
+    """Returns the stopReason string given an enum."""
+    if enum == lldb.eStopReasonInvalid:
+        return "invalid"
+    elif enum == lldb.eStopReasonNone:
+        return "none"
+    elif enum == lldb.eStopReasonTrace:
+        return "trace"
+    elif enum == lldb.eStopReasonBreakpoint:
+        return "breakpoint"
+    elif enum == lldb.eStopReasonWatchpoint:
+        return "watchpoint"
+    elif enum == lldb.eStopReasonSignal:
+        return "signal"
+    elif enum == lldb.eStopReasonException:
+        return "exception"
+    elif enum == lldb.eStopReasonPlanComplete:
+        return "plancomplete"
+    elif enum == lldb.eStopReasonThreadExiting:
+        return "threadexiting"
+    else:
+        raise Exception("Unknown StopReason enum")
+
+
+def symbol_type_to_str(enum):
+    """Returns the symbolType string given an enum."""
+    if enum == lldb.eSymbolTypeInvalid:
+        return "invalid"
+    elif enum == lldb.eSymbolTypeAbsolute:
+        return "absolute"
+    elif enum == lldb.eSymbolTypeCode:
+        return "code"
+    elif enum == lldb.eSymbolTypeData:
+        return "data"
+    elif enum == lldb.eSymbolTypeTrampoline:
+        return "trampoline"
+    elif enum == lldb.eSymbolTypeRuntime:
+        return "runtime"
+    elif enum == lldb.eSymbolTypeException:
+        return "exception"
+    elif enum == lldb.eSymbolTypeSourceFile:
+        return "sourcefile"
+    elif enum == lldb.eSymbolTypeHeaderFile:
+        return "headerfile"
+    elif enum == lldb.eSymbolTypeObjectFile:
+        return "objectfile"
+    elif enum == lldb.eSymbolTypeCommonBlock:
+        return "commonblock"
+    elif enum == lldb.eSymbolTypeBlock:
+        return "block"
+    elif enum == lldb.eSymbolTypeLocal:
+        return "local"
+    elif enum == lldb.eSymbolTypeParam:
+        return "param"
+    elif enum == lldb.eSymbolTypeVariable:
+        return "variable"
+    elif enum == lldb.eSymbolTypeVariableType:
+        return "variabletype"
+    elif enum == lldb.eSymbolTypeLineEntry:
+        return "lineentry"
+    elif enum == lldb.eSymbolTypeLineHeader:
+        return "lineheader"
+    elif enum == lldb.eSymbolTypeScopeBegin:
+        return "scopebegin"
+    elif enum == lldb.eSymbolTypeScopeEnd:
+        return "scopeend"
+    elif enum == lldb.eSymbolTypeAdditional:
+        return "additional"
+    elif enum == lldb.eSymbolTypeCompiler:
+        return "compiler"
+    elif enum == lldb.eSymbolTypeInstrumentation:
+        return "instrumentation"
+    elif enum == lldb.eSymbolTypeUndefined:
+        return "undefined"
+
+
+def value_type_to_str(enum):
+    """Returns the valueType string given an enum."""
+    if enum == lldb.eValueTypeInvalid:
+        return "invalid"
+    elif enum == lldb.eValueTypeVariableGlobal:
+        return "global_variable"
+    elif enum == lldb.eValueTypeVariableStatic:
+        return "static_variable"
+    elif enum == lldb.eValueTypeVariableArgument:
+        return "argument_variable"
+    elif enum == lldb.eValueTypeVariableLocal:
+        return "local_variable"
+    elif enum == lldb.eValueTypeRegister:
+        return "register"
+    elif enum == lldb.eValueTypeRegisterSet:
+        return "register_set"
+    elif enum == lldb.eValueTypeConstResult:
+        return "constant_result"
+    else:
+        raise Exception("Unknown ValueType enum")
+
+
+# ==================================================
+# Get stopped threads due to each stop reason.
+# ==================================================
+
+def sort_stopped_threads(process,
+                         breakpoint_threads=None,
+                         crashed_threads=None,
+                         watchpoint_threads=None,
+                         signal_threads=None,
+                         exiting_threads=None,
+                         other_threads=None):
+    """ Fills array *_threads with threads stopped for the corresponding stop
+        reason.
+    """
+    for lst in [breakpoint_threads,
+                watchpoint_threads,
+                signal_threads,
+                exiting_threads,
+                other_threads]:
+        if lst is not None:
+            lst[:] = []
+
+    for thread in process:
+        dispatched = False
+        for (reason, list) in [(lldb.eStopReasonBreakpoint, breakpoint_threads),
+                               (lldb.eStopReasonException, crashed_threads),
+                               (lldb.eStopReasonWatchpoint, watchpoint_threads),
+                               (lldb.eStopReasonSignal, signal_threads),
+                               (lldb.eStopReasonThreadExiting, exiting_threads),
+                               (None, other_threads)]:
+            if not dispatched and list is not None:
+                if thread.GetStopReason() == reason or reason is None:
+                    list.append(thread)
+                    dispatched = True
+
+# ==================================================
+# Utility functions for setting breakpoints
+# ==================================================
+
+
+def run_break_set_by_file_and_line(
+        test,
+        file_name,
+        line_number,
+        extra_options=None,
+        num_expected_locations=1,
+        loc_exact=False,
+        module_name=None):
+    """Set a breakpoint by file and line, returning the breakpoint number.
+
+    If extra_options is not None, then we append it to the breakpoint set command.
+
+    If num_expected_locations is -1 we check that we got AT LEAST one location, otherwise we check that num_expected_locations equals the number of locations.
+
+    If loc_exact is true, we check that there is one location, and that location must be at the input file and line number."""
+
+    if file_name is None:
+        command = 'breakpoint set -l %d' % (line_number)
+    else:
+        command = 'breakpoint set -f "%s" -l %d' % (file_name, line_number)
+
+    if module_name:
+        command += " --shlib '%s'" % (module_name)
+
+    if extra_options:
+        command += " " + extra_options
+
+    break_results = run_break_set_command(test, command)
+
+    if num_expected_locations == 1 and loc_exact:
+        check_breakpoint_result(
+            test,
+            break_results,
+            num_locations=num_expected_locations,
+            file_name=file_name,
+            line_number=line_number,
+            module_name=module_name)
+    else:
+        check_breakpoint_result(
+            test,
+            break_results,
+            num_locations=num_expected_locations)
+
+    return get_bpno_from_match(break_results)
+
+
+def run_break_set_by_symbol(
+        test,
+        symbol,
+        extra_options=None,
+        num_expected_locations=-1,
+        sym_exact=False,
+        module_name=None):
+    """Set a breakpoint by symbol name.  Common options are the same as run_break_set_by_file_and_line.
+
+    If sym_exact is true, then the output symbol must match the input exactly, otherwise we do a substring match."""
+    command = 'breakpoint set -n "%s"' % (symbol)
+
+    if module_name:
+        command += " --shlib '%s'" % (module_name)
+
+    if extra_options:
+        command += " " + extra_options
+
+    break_results = run_break_set_command(test, command)
+
+    if num_expected_locations == 1 and sym_exact:
+        check_breakpoint_result(
+            test,
+            break_results,
+            num_locations=num_expected_locations,
+            symbol_name=symbol,
+            module_name=module_name)
+    else:
+        check_breakpoint_result(
+            test,
+            break_results,
+            num_locations=num_expected_locations)
+
+    return get_bpno_from_match(break_results)
+
+
+def run_break_set_by_selector(
+        test,
+        selector,
+        extra_options=None,
+        num_expected_locations=-1,
+        module_name=None):
+    """Set a breakpoint by selector.  Common options are the same as run_break_set_by_file_and_line."""
+
+    command = 'breakpoint set -S "%s"' % (selector)
+
+    if module_name:
+        command += ' --shlib "%s"' % (module_name)
+
+    if extra_options:
+        command += " " + extra_options
+
+    break_results = run_break_set_command(test, command)
+
+    if num_expected_locations == 1:
+        check_breakpoint_result(
+            test,
+            break_results,
+            num_locations=num_expected_locations,
+            symbol_name=selector,
+            symbol_match_exact=False,
+            module_name=module_name)
+    else:
+        check_breakpoint_result(
+            test,
+            break_results,
+            num_locations=num_expected_locations)
+
+    return get_bpno_from_match(break_results)
+
+
+def run_break_set_by_regexp(
+        test,
+        regexp,
+        extra_options=None,
+        num_expected_locations=-1):
+    """Set a breakpoint by regular expression match on symbol name.  Common options are the same as run_break_set_by_file_and_line."""
+
+    command = 'breakpoint set -r "%s"' % (regexp)
+    if extra_options:
+        command += " " + extra_options
+
+    break_results = run_break_set_command(test, command)
+
+    check_breakpoint_result(
+        test,
+        break_results,
+        num_locations=num_expected_locations)
+
+    return get_bpno_from_match(break_results)
+
+
+def run_break_set_by_source_regexp(
+        test,
+        regexp,
+        extra_options=None,
+        num_expected_locations=-1):
+    """Set a breakpoint by source regular expression.  Common options are the same as run_break_set_by_file_and_line."""
+    command = 'breakpoint set -p "%s"' % (regexp)
+    if extra_options:
+        command += " " + extra_options
+
+    break_results = run_break_set_command(test, command)
+
+    check_breakpoint_result(
+        test,
+        break_results,
+        num_locations=num_expected_locations)
+
+    return get_bpno_from_match(break_results)
+
+
+def run_break_set_command(test, command):
+    """Run the command passed in - it must be some break set variant - and analyze the result.
+    Returns a dictionary of information gleaned from the command-line results.
+    Will assert if the breakpoint setting fails altogether.
+
+    Dictionary will contain:
+        bpno          - breakpoint of the newly created breakpoint, -1 on error.
+        num_locations - number of locations set for the breakpoint.
+
+    If there is only one location, the dictionary MAY contain:
+        file          - source file name
+        line_no       - source line number
+        symbol        - symbol name
+        inline_symbol - inlined symbol name
+        offset        - offset from the original symbol
+        module        - module
+        address       - address at which the breakpoint was set."""
+
+    patterns = [
+        r"^Breakpoint (?P<bpno>[0-9]+): (?P<num_locations>[0-9]+) locations\.$",
+        r"^Breakpoint (?P<bpno>[0-9]+): (?P<num_locations>no) locations \(pending\)\.",
+        r"^Breakpoint (?P<bpno>[0-9]+): where = (?P<module>.*)`(?P<symbol>[+\-]{0,1}[^+]+)( \+ (?P<offset>[0-9]+)){0,1}( \[inlined\] (?P<inline_symbol>.*)){0,1} at (?P<file>[^:]+):(?P<line_no>[0-9]+), address = (?P<address>0x[0-9a-fA-F]+)$",
+        r"^Breakpoint (?P<bpno>[0-9]+): where = (?P<module>.*)`(?P<symbol>.*)( \+ (?P<offset>[0-9]+)){0,1}, address = (?P<address>0x[0-9a-fA-F]+)$"]
+    match_object = test.match(command, patterns)
+    break_results = match_object.groupdict()
+
+    # We always insert the breakpoint number, setting it to -1 if we couldn't find it
+    # Also, make sure it gets stored as an integer.
+    if not 'bpno' in break_results:
+        break_results['bpno'] = -1
+    else:
+        break_results['bpno'] = int(break_results['bpno'])
+
+    # We always insert the number of locations
+    # If ONE location is set for the breakpoint, then the output doesn't mention locations, but it has to be 1...
+    # We also make sure it is an integer.
+
+    if not 'num_locations' in break_results:
+        num_locations = 1
+    else:
+        num_locations = break_results['num_locations']
+        if num_locations == 'no':
+            num_locations = 0
+        else:
+            num_locations = int(break_results['num_locations'])
+
+    break_results['num_locations'] = num_locations
+
+    if 'line_no' in break_results:
+        break_results['line_no'] = int(break_results['line_no'])
+
+    return break_results
+
+
+def get_bpno_from_match(break_results):
+    return int(break_results['bpno'])
+
+
+def check_breakpoint_result(
+        test,
+        break_results,
+        file_name=None,
+        line_number=-1,
+        symbol_name=None,
+        symbol_match_exact=True,
+        module_name=None,
+        offset=-1,
+        num_locations=-1):
+
+    out_num_locations = break_results['num_locations']
+
+    if num_locations == -1:
+        test.assertTrue(out_num_locations > 0,
+                        "Expecting one or more locations, got none.")
+    else:
+        test.assertTrue(
+            num_locations == out_num_locations,
+            "Expecting %d locations, got %d." %
+            (num_locations,
+             out_num_locations))
+
+    if file_name:
+        out_file_name = ""
+        if 'file' in break_results:
+            out_file_name = break_results['file']
+        test.assertTrue(
+            file_name == out_file_name,
+            "Breakpoint file name '%s' doesn't match resultant name '%s'." %
+            (file_name,
+             out_file_name))
+
+    if line_number != -1:
+        out_file_line = -1
+        if 'line_no' in break_results:
+            out_line_number = break_results['line_no']
+
+        test.assertTrue(
+            line_number == out_line_number,
+            "Breakpoint line number %s doesn't match resultant line %s." %
+            (line_number,
+             out_line_number))
+
+    if symbol_name:
+        out_symbol_name = ""
+        # Look first for the inlined symbol name, otherwise use the symbol
+        # name:
+        if 'inline_symbol' in break_results and break_results['inline_symbol']:
+            out_symbol_name = break_results['inline_symbol']
+        elif 'symbol' in break_results:
+            out_symbol_name = break_results['symbol']
+
+        if symbol_match_exact:
+            test.assertTrue(
+                symbol_name == out_symbol_name,
+                "Symbol name '%s' doesn't match resultant symbol '%s'." %
+                (symbol_name,
+                 out_symbol_name))
+        else:
+            test.assertTrue(
+                out_symbol_name.find(symbol_name) != -
+                1,
+                "Symbol name '%s' isn't in resultant symbol '%s'." %
+                (symbol_name,
+                 out_symbol_name))
+
+    if module_name:
+        out_nodule_name = None
+        if 'module' in break_results:
+            out_module_name = break_results['module']
+
+        test.assertTrue(
+            module_name.find(out_module_name) != -
+            1,
+            "Symbol module name '%s' isn't in expected module name '%s'." %
+            (out_module_name,
+             module_name))
+
+# ==================================================
+# Utility functions related to Threads and Processes
+# ==================================================
+
+
+def get_stopped_threads(process, reason):
+    """Returns the thread(s) with the specified stop reason in a list.
+
+    The list can be empty if no such thread exists.
+    """
+    threads = []
+    for t in process:
+        if t.GetStopReason() == reason:
+            threads.append(t)
+    return threads
+
+
+def get_stopped_thread(process, reason):
+    """A convenience function which returns the first thread with the given stop
+    reason or None.
+
+    Example usages:
+
+    1. Get the stopped thread due to a breakpoint condition
+
+    ...
+        from lldbutil import get_stopped_thread
+        thread = get_stopped_thread(process, lldb.eStopReasonPlanComplete)
+        self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition")
+    ...
+
+    2. Get the thread stopped due to a breakpoint
+
+    ...
+        from lldbutil import get_stopped_thread
+        thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
+        self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint")
+    ...
+
+    """
+    threads = get_stopped_threads(process, reason)
+    if len(threads) == 0:
+        return None
+    return threads[0]
+
+
+def get_threads_stopped_at_breakpoint(process, bkpt):
+    """ For a stopped process returns the thread stopped at the breakpoint passed in bkpt"""
+    stopped_threads = []
+    threads = []
+
+    stopped_threads = get_stopped_threads(process, lldb.eStopReasonBreakpoint)
+
+    if len(stopped_threads) == 0:
+        return threads
+
+    for thread in stopped_threads:
+        # Make sure we've hit our breakpoint...
+        break_id = thread.GetStopReasonDataAtIndex(0)
+        if break_id == bkpt.GetID():
+            threads.append(thread)
+
+    return threads
+
+
+def continue_to_breakpoint(process, bkpt):
+    """ Continues the process, if it stops, returns the threads stopped at bkpt; otherwise, returns None"""
+    process.Continue()
+    if process.GetState() != lldb.eStateStopped:
+        return None
+    else:
+        return get_threads_stopped_at_breakpoint(process, bkpt)
+
+
+def get_caller_symbol(thread):
+    """
+    Returns the symbol name for the call site of the leaf function.
+    """
+    depth = thread.GetNumFrames()
+    if depth <= 1:
+        return None
+    caller = thread.GetFrameAtIndex(1).GetSymbol()
+    if caller:
+        return caller.GetName()
+    else:
+        return None
+
+
+def get_function_names(thread):
+    """
+    Returns a sequence of function names from the stack frames of this thread.
+    """
+    def GetFuncName(i):
+        return thread.GetFrameAtIndex(i).GetFunctionName()
+
+    return [GetFuncName(i) for i in range(thread.GetNumFrames())]
+
+
+def get_symbol_names(thread):
+    """
+    Returns a sequence of symbols for this thread.
+    """
+    def GetSymbol(i):
+        return thread.GetFrameAtIndex(i).GetSymbol().GetName()
+
+    return [GetSymbol(i) for i in range(thread.GetNumFrames())]
+
+
+def get_pc_addresses(thread):
+    """
+    Returns a sequence of pc addresses for this thread.
+    """
+    def GetPCAddress(i):
+        return thread.GetFrameAtIndex(i).GetPCAddress()
+
+    return [GetPCAddress(i) for i in range(thread.GetNumFrames())]
+
+
+def get_filenames(thread):
+    """
+    Returns a sequence of file names from the stack frames of this thread.
+    """
+    def GetFilename(i):
+        return thread.GetFrameAtIndex(
+            i).GetLineEntry().GetFileSpec().GetFilename()
+
+    return [GetFilename(i) for i in range(thread.GetNumFrames())]
+
+
+def get_line_numbers(thread):
+    """
+    Returns a sequence of line numbers from the stack frames of this thread.
+    """
+    def GetLineNumber(i):
+        return thread.GetFrameAtIndex(i).GetLineEntry().GetLine()
+
+    return [GetLineNumber(i) for i in range(thread.GetNumFrames())]
+
+
+def get_module_names(thread):
+    """
+    Returns a sequence of module names from the stack frames of this thread.
+    """
+    def GetModuleName(i):
+        return thread.GetFrameAtIndex(
+            i).GetModule().GetFileSpec().GetFilename()
+
+    return [GetModuleName(i) for i in range(thread.GetNumFrames())]
+
+
+def get_stack_frames(thread):
+    """
+    Returns a sequence of stack frames for this thread.
+    """
+    def GetStackFrame(i):
+        return thread.GetFrameAtIndex(i)
+
+    return [GetStackFrame(i) for i in range(thread.GetNumFrames())]
+
+
+def print_stacktrace(thread, string_buffer=False):
+    """Prints a simple stack trace of this thread."""
+
+    output = io.StringIO() if string_buffer else sys.stdout
+    target = thread.GetProcess().GetTarget()
+
+    depth = thread.GetNumFrames()
+
+    mods = get_module_names(thread)
+    funcs = get_function_names(thread)
+    symbols = get_symbol_names(thread)
+    files = get_filenames(thread)
+    lines = get_line_numbers(thread)
+    addrs = get_pc_addresses(thread)
+
+    if thread.GetStopReason() != lldb.eStopReasonInvalid:
+        desc = "stop reason=" + stop_reason_to_str(thread.GetStopReason())
+    else:
+        desc = ""
+    print("Stack trace for thread id={0:#x} name={1} queue={2} ".format(
+        thread.GetThreadID(), thread.GetName(), thread.GetQueueName()) + desc, file=output)
+
+    for i in range(depth):
+        frame = thread.GetFrameAtIndex(i)
+        function = frame.GetFunction()
+
+        load_addr = addrs[i].GetLoadAddress(target)
+        if not function:
+            file_addr = addrs[i].GetFileAddress()
+            start_addr = frame.GetSymbol().GetStartAddress().GetFileAddress()
+            symbol_offset = file_addr - start_addr
+            print("  frame #{num}: {addr:#016x} {mod}`{symbol} + {offset}".format(
+                num=i, addr=load_addr, mod=mods[i], symbol=symbols[i], offset=symbol_offset), file=output)
+        else:
+            print("  frame #{num}: {addr:#016x} {mod}`{func} at {file}:{line} {args}".format(
+                num=i, addr=load_addr, mod=mods[i], func='%s [inlined]' %
+                funcs[i] if frame.IsInlined() else funcs[i], file=files[i], line=lines[i], args=get_args_as_string(
+                    frame, showFuncName=False) if not frame.IsInlined() else '()'), file=output)
+
+    if string_buffer:
+        return output.getvalue()
+
+
+def print_stacktraces(process, string_buffer=False):
+    """Prints the stack traces of all the threads."""
+
+    output = io.StringIO() if string_buffer else sys.stdout
+
+    print("Stack traces for " + str(process), file=output)
+
+    for thread in process:
+        print(print_stacktrace(thread, string_buffer=True), file=output)
+
+    if string_buffer:
+        return output.getvalue()
+
+# ===================================
+# Utility functions related to Frames
+# ===================================
+
+
+def get_parent_frame(frame):
+    """
+    Returns the parent frame of the input frame object; None if not available.
+    """
+    thread = frame.GetThread()
+    parent_found = False
+    for f in thread:
+        if parent_found:
+            return f
+        if f.GetFrameID() == frame.GetFrameID():
+            parent_found = True
+
+    # If we reach here, no parent has been found, return None.
+    return None
+
+
+def get_args_as_string(frame, showFuncName=True):
+    """
+    Returns the args of the input frame object as a string.
+    """
+    # arguments     => True
+    # locals        => False
+    # statics       => False
+    # in_scope_only => True
+    vars = frame.GetVariables(True, False, False, True)  # type of SBValueList
+    args = []  # list of strings
+    for var in vars:
+        args.append("(%s)%s=%s" % (var.GetTypeName(),
+                                   var.GetName(),
+                                   var.GetValue()))
+    if frame.GetFunction():
+        name = frame.GetFunction().GetName()
+    elif frame.GetSymbol():
+        name = frame.GetSymbol().GetName()
+    else:
+        name = ""
+    if showFuncName:
+        return "%s(%s)" % (name, ", ".join(args))
+    else:
+        return "(%s)" % (", ".join(args))
+
+
+def print_registers(frame, string_buffer=False):
+    """Prints all the register sets of the frame."""
+
+    output = io.StringIO() if string_buffer else sys.stdout
+
+    print("Register sets for " + str(frame), file=output)
+
+    registerSet = frame.GetRegisters()  # Return type of SBValueList.
+    print("Frame registers (size of register set = %d):" % registerSet.GetSize(
+    ), file=output)
+    for value in registerSet:
+        #print >> output, value
+        print("%s (number of children = %d):" % (
+            value.GetName(), value.GetNumChildren()), file=output)
+        for child in value:
+            print("Name: %s, Value: %s" % (
+                child.GetName(), child.GetValue()), file=output)
+
+    if string_buffer:
+        return output.getvalue()
+
+
+def get_registers(frame, kind):
+    """Returns the registers given the frame and the kind of registers desired.
+
+    Returns None if there's no such kind.
+    """
+    registerSet = frame.GetRegisters()  # Return type of SBValueList.
+    for value in registerSet:
+        if kind.lower() in value.GetName().lower():
+            return value
+
+    return None
+
+
+def get_GPRs(frame):
+    """Returns the general purpose registers of the frame as an SBValue.
+
+    The returned SBValue object is iterable.  An example:
+        ...
+        from lldbutil import get_GPRs
+        regs = get_GPRs(frame)
+        for reg in regs:
+            print "%s => %s" % (reg.GetName(), reg.GetValue())
+        ...
+    """
+    return get_registers(frame, "general purpose")
+
+
+def get_FPRs(frame):
+    """Returns the floating point registers of the frame as an SBValue.
+
+    The returned SBValue object is iterable.  An example:
+        ...
+        from lldbutil import get_FPRs
+        regs = get_FPRs(frame)
+        for reg in regs:
+            print "%s => %s" % (reg.GetName(), reg.GetValue())
+        ...
+    """
+    return get_registers(frame, "floating point")
+
+
+def get_ESRs(frame):
+    """Returns the exception state registers of the frame as an SBValue.
+
+    The returned SBValue object is iterable.  An example:
+        ...
+        from lldbutil import get_ESRs
+        regs = get_ESRs(frame)
+        for reg in regs:
+            print "%s => %s" % (reg.GetName(), reg.GetValue())
+        ...
+    """
+    return get_registers(frame, "exception state")
+
+# ======================================
+# Utility classes/functions for SBValues
+# ======================================
+
+
+class BasicFormatter(object):
+    """The basic formatter inspects the value object and prints the value."""
+
+    def format(self, value, buffer=None, indent=0):
+        if not buffer:
+            output = io.StringIO()
+        else:
+            output = buffer
+        # If there is a summary, it suffices.
+        val = value.GetSummary()
+        # Otherwise, get the value.
+        if val is None:
+            val = value.GetValue()
+        if val is None and value.GetNumChildren() > 0:
+            val = "%s (location)" % value.GetLocation()
+        print("{indentation}({type}) {name} = {value}".format(
+            indentation=' ' * indent,
+            type=value.GetTypeName(),
+            name=value.GetName(),
+            value=val), file=output)
+        return output.getvalue()
+
+
+class ChildVisitingFormatter(BasicFormatter):
+    """The child visiting formatter prints the value and its immediate children.
+
+    The constructor takes a keyword arg: indent_child, which defaults to 2.
+    """
+
+    def __init__(self, indent_child=2):
+        """Default indentation of 2 SPC's for the children."""
+        self.cindent = indent_child
+
+    def format(self, value, buffer=None):
+        if not buffer:
+            output = io.StringIO()
+        else:
+            output = buffer
+
+        BasicFormatter.format(self, value, buffer=output)
+        for child in value:
+            BasicFormatter.format(
+                self, child, buffer=output, indent=self.cindent)
+
+        return output.getvalue()
+
+
+class RecursiveDecentFormatter(BasicFormatter):
+    """The recursive decent formatter prints the value and the decendents.
+
+    The constructor takes two keyword args: indent_level, which defaults to 0,
+    and indent_child, which defaults to 2.  The current indentation level is
+    determined by indent_level, while the immediate children has an additional
+    indentation by inden_child.
+    """
+
+    def __init__(self, indent_level=0, indent_child=2):
+        self.lindent = indent_level
+        self.cindent = indent_child
+
+    def format(self, value, buffer=None):
+        if not buffer:
+            output = io.StringIO()
+        else:
+            output = buffer
+
+        BasicFormatter.format(self, value, buffer=output, indent=self.lindent)
+        new_indent = self.lindent + self.cindent
+        for child in value:
+            if child.GetSummary() is not None:
+                BasicFormatter.format(
+                    self, child, buffer=output, indent=new_indent)
+            else:
+                if child.GetNumChildren() > 0:
+                    rdf = RecursiveDecentFormatter(indent_level=new_indent)
+                    rdf.format(child, buffer=output)
+                else:
+                    BasicFormatter.format(
+                        self, child, buffer=output, indent=new_indent)
+
+        return output.getvalue()

Added: lldb/trunk/utils/lui/lui.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lui/lui.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lui/lui.py (added)
+++ lldb/trunk/utils/lui/lui.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+##===-- lui.py -----------------------------------------------*- Python -*-===##
+##
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+##
+##===----------------------------------------------------------------------===##
+
+
+import curses
+
+import lldb
+import lldbutil
+
+from optparse import OptionParser
+import os
+import signal
+import sys
+
+try:
+    import queue
+except ImportError:
+    import Queue as queue
+
+import debuggerdriver
+import cui
+
+import breakwin
+import commandwin
+import eventwin
+import sourcewin
+import statuswin
+
+event_queue = None
+
+
+def handle_args(driver, argv):
+    parser = OptionParser()
+    parser.add_option(
+        "-p",
+        "--attach",
+        dest="pid",
+        help="Attach to specified Process ID",
+        type="int")
+    parser.add_option(
+        "-c",
+        "--core",
+        dest="core",
+        help="Load specified core file",
+        type="string")
+
+    (options, args) = parser.parse_args(argv)
+
+    if options.pid is not None:
+        try:
+            pid = int(options.pid)
+            driver.attachProcess(ui, pid)
+        except ValueError:
+            print("Error: expecting integer PID, got '%s'" % options.pid)
+    elif options.core is not None:
+        if not os.path.exists(options.core):
+            raise Exception(
+                "Specified core file '%s' does not exist." %
+                options.core)
+        driver.loadCore(options.core)
+    elif len(args) == 2:
+        if not os.path.isfile(args[1]):
+            raise Exception("Specified target '%s' does not exist" % args[1])
+        driver.createTarget(args[1])
+    elif len(args) > 2:
+        if not os.path.isfile(args[1]):
+            raise Exception("Specified target '%s' does not exist" % args[1])
+        driver.createTarget(args[1], args[2:])
+
+
+def sigint_handler(signal, frame):
+    global debugger
+    debugger.terminate()
+
+
+class LLDBUI(cui.CursesUI):
+
+    def __init__(self, screen, event_queue, driver):
+        super(LLDBUI, self).__init__(screen, event_queue)
+
+        self.driver = driver
+
+        h, w = self.screen.getmaxyx()
+
+        command_win_height = 20
+        break_win_width = 60
+
+        self.status_win = statuswin.StatusWin(0, h - 1, w, 1)
+        h -= 1
+        self.command_win = commandwin.CommandWin(
+            driver, 0, h - command_win_height, w, command_win_height)
+        h -= command_win_height
+        self.source_win = sourcewin.SourceWin(driver, 0, 0,
+                                              w - break_win_width - 1, h)
+        self.break_win = breakwin.BreakWin(driver, w - break_win_width, 0,
+                                           break_win_width, h)
+
+        self.wins = [self.status_win,
+                     # self.event_win,
+                     self.source_win,
+                     self.break_win,
+                     self.command_win,
+                     ]
+
+        self.focus = len(self.wins) - 1  # index of command window;
+
+    def handleEvent(self, event):
+        # hack
+        if isinstance(event, int):
+            if event == curses.KEY_F10:
+                self.driver.terminate()
+            if event == 20:  # ctrl-T
+                def foo(cmd):
+                    ret = lldb.SBCommandReturnObject()
+                    self.driver.getCommandInterpreter().HandleCommand(cmd, ret)
+                foo('target create a.out')
+                foo('b main')
+                foo('run')
+        super(LLDBUI, self).handleEvent(event)
+
+
+def main(screen):
+    signal.signal(signal.SIGINT, sigint_handler)
+
+    global event_queue
+    event_queue = queue.Queue()
+
+    global debugger
+    debugger = lldb.SBDebugger.Create()
+
+    driver = debuggerdriver.createDriver(debugger, event_queue)
+    view = LLDBUI(screen, event_queue, driver)
+
+    driver.start()
+
+    # hack to avoid hanging waiting for prompts!
+    driver.handleCommand("settings set auto-confirm true")
+
+    handle_args(driver, sys.argv)
+    view.eventLoop()
+
+if __name__ == "__main__":
+    try:
+        curses.wrapper(main)
+    except KeyboardInterrupt:
+        exit()

Propchange: lldb/trunk/utils/lui/lui.py
------------------------------------------------------------------------------
    svn:executable = *

Added: lldb/trunk/utils/lui/sandbox.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lui/sandbox.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lui/sandbox.py (added)
+++ lldb/trunk/utils/lui/sandbox.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+##===-- sandbox.py -------------------------------------------*- Python -*-===##
+##
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+##
+##===----------------------------------------------------------------------===##
+
+
+import curses
+
+import os
+import signal
+import sys
+
+try:
+    import queue
+except ImportError:
+    import Queue as queue
+
+import cui
+
+event_queue = None
+
+
+class SandboxUI(cui.CursesUI):
+
+    def __init__(self, screen, event_queue):
+        super(SandboxUI, self).__init__(screen, event_queue)
+
+        height, width = self.screen.getmaxyx()
+        w2 = width / 2
+        h2 = height / 2
+
+        self.wins = []
+        #self.wins.append(cui.TitledWin(w2, h2, w2, h2, "Test Window 4"))
+        list_win = cui.ListWin(w2, h2, w2, h2)
+        for i in range(0, 40):
+            list_win.addItem('Item %s' % i)
+        self.wins.append(list_win)
+        self.wins.append(cui.TitledWin(0, 0, w2, h2, "Test Window 1"))
+        self.wins.append(cui.TitledWin(w2, 0, w2, h2, "Test Window 2"))
+        self.wins.append(cui.TitledWin(0, h2, w2, h2, "Test Window 3"))
+
+        # def callback(s, content):
+        #  self.wins[0].win.scroll(1)
+        #  self.wins[0].win.addstr(10, 0, '%s: %s' % (s, content))
+        #  self.wins[0].win.scroll(1)
+        #  self.el.showPrompt(10, 0)
+
+        # self.wins[0].win.scrollok(1)
+        # self.el = cui.CursesEditLine(self.wins[0].win, None,
+        #  lambda c: callback('got', c), lambda c: callback('tab', c))
+        #self.el.prompt = '>>> '
+        #self.el.showPrompt(10, 0)
+
+    def handleEvent(self, event):
+        if isinstance(event, int):
+            if event == ord('q'):
+                sys.exit(0)
+            # self.el.handleEvent(event)
+        super(SandboxUI, self).handleEvent(event)
+
+
+def main(screen):
+    global event_queue
+    event_queue = queue.Queue()
+
+    sandbox = SandboxUI(screen, event_queue)
+    sandbox.eventLoop()
+
+if __name__ == "__main__":
+    try:
+        curses.wrapper(main)
+    except KeyboardInterrupt:
+        exit()

Propchange: lldb/trunk/utils/lui/sandbox.py
------------------------------------------------------------------------------
    svn:executable = *

Added: lldb/trunk/utils/lui/sourcewin.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lui/sourcewin.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lui/sourcewin.py (added)
+++ lldb/trunk/utils/lui/sourcewin.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,238 @@
+##===-- sourcewin.py -----------------------------------------*- Python -*-===##
+##
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+##
+##===----------------------------------------------------------------------===##
+
+import cui
+import curses
+import lldb
+import lldbutil
+import re
+import os
+
+
+class SourceWin(cui.TitledWin):
+
+    def __init__(self, driver, x, y, w, h):
+        super(SourceWin, self).__init__(x, y, w, h, "Source")
+        self.sourceman = driver.getSourceManager()
+        self.sources = {}
+
+        self.filename = None
+        self.pc_line = None
+        self.viewline = 0
+
+        self.breakpoints = {}
+
+        self.win.scrollok(1)
+
+        self.markerPC = ":) "
+        self.markerBP = "B> "
+        self.markerNone = "   "
+
+        try:
+            from pygments.formatters import TerminalFormatter
+            self.formatter = TerminalFormatter()
+        except ImportError:
+            #self.win.addstr("\nWarning: no 'pygments' library found. Syntax highlighting is disabled.")
+            self.lexer = None
+            self.formatter = None
+            pass
+
+        # FIXME: syntax highlight broken
+        self.formatter = None
+        self.lexer = None
+
+    def handleEvent(self, event):
+        if isinstance(event, int):
+            self.handleKey(event)
+            return
+
+        if isinstance(event, lldb.SBEvent):
+            if lldb.SBBreakpoint.EventIsBreakpointEvent(event):
+                self.handleBPEvent(event)
+
+            if lldb.SBProcess.EventIsProcessEvent(event) and \
+                    not lldb.SBProcess.GetRestartedFromEvent(event):
+                process = lldb.SBProcess.GetProcessFromEvent(event)
+                if not process.IsValid():
+                    return
+                if process.GetState() == lldb.eStateStopped:
+                    self.refreshSource(process)
+                elif process.GetState() == lldb.eStateExited:
+                    self.notifyExited(process)
+
+    def notifyExited(self, process):
+        self.win.erase()
+        target = lldbutil.get_description(process.GetTarget())
+        pid = process.GetProcessID()
+        ec = process.GetExitStatus()
+        self.win.addstr(
+            "\nProcess %s [%d] has exited with exit-code %d" %
+            (target, pid, ec))
+
+    def pageUp(self):
+        if self.viewline > 0:
+            self.viewline = self.viewline - 1
+            self.refreshSource()
+
+    def pageDown(self):
+        if self.viewline < len(self.content) - self.height + 1:
+            self.viewline = self.viewline + 1
+            self.refreshSource()
+        pass
+
+    def handleKey(self, key):
+        if key == curses.KEY_DOWN:
+            self.pageDown()
+        elif key == curses.KEY_UP:
+            self.pageUp()
+
+    def updateViewline(self):
+        half = self.height / 2
+        if self.pc_line < half:
+            self.viewline = 0
+        else:
+            self.viewline = self.pc_line - half + 1
+
+        if self.viewline < 0:
+            raise Exception(
+                "negative viewline: pc=%d viewline=%d" %
+                (self.pc_line, self.viewline))
+
+    def refreshSource(self, process=None):
+        (self.height, self.width) = self.win.getmaxyx()
+
+        if process is not None:
+            loc = process.GetSelectedThread().GetSelectedFrame().GetLineEntry()
+            f = loc.GetFileSpec()
+            self.pc_line = loc.GetLine()
+
+            if not f.IsValid():
+                self.win.addstr(0, 0, "Invalid source file")
+                return
+
+            self.filename = f.GetFilename()
+            path = os.path.join(f.GetDirectory(), self.filename)
+            self.setTitle(path)
+            self.content = self.getContent(path)
+            self.updateViewline()
+
+        if self.filename is None:
+            return
+
+        if self.formatter is not None:
+            from pygments.lexers import get_lexer_for_filename
+            self.lexer = get_lexer_for_filename(self.filename)
+
+        bps = [] if not self.filename in self.breakpoints else self.breakpoints[self.filename]
+        self.win.erase()
+        if self.content:
+            self.formatContent(self.content, self.pc_line, bps)
+
+    def getContent(self, path):
+        content = []
+        if path in self.sources:
+            content = self.sources[path]
+        else:
+            if os.path.exists(path):
+                with open(path) as x:
+                    content = x.readlines()
+                self.sources[path] = content
+        return content
+
+    def formatContent(self, content, pc_line, breakpoints):
+        source = ""
+        count = 1
+        self.win.erase()
+        end = min(len(content), self.viewline + self.height)
+        for i in range(self.viewline, end):
+            line_num = i + 1
+            marker = self.markerNone
+            attr = curses.A_NORMAL
+            if line_num == pc_line:
+                attr = curses.A_REVERSE
+            if line_num in breakpoints:
+                marker = self.markerBP
+            line = "%s%3d %s" % (marker, line_num, self.highlight(content[i]))
+            if len(line) >= self.width:
+                line = line[0:self.width - 1] + "\n"
+            self.win.addstr(line, attr)
+            source += line
+            count = count + 1
+        return source
+
+    def highlight(self, source):
+        if self.lexer and self.formatter:
+            from pygments import highlight
+            return highlight(source, self.lexer, self.formatter)
+        else:
+            return source
+
+    def addBPLocations(self, locations):
+        for path in locations:
+            lines = locations[path]
+            if path in self.breakpoints:
+                self.breakpoints[path].update(lines)
+            else:
+                self.breakpoints[path] = lines
+
+    def removeBPLocations(self, locations):
+        for path in locations:
+            lines = locations[path]
+            if path in self.breakpoints:
+                self.breakpoints[path].difference_update(lines)
+            else:
+                raise "Removing locations that were never added...no good"
+
+    def handleBPEvent(self, event):
+        def getLocations(event):
+            locs = {}
+
+            bp = lldb.SBBreakpoint.GetBreakpointFromEvent(event)
+
+            if bp.IsInternal():
+                # don't show anything for internal breakpoints
+                return
+
+            for location in bp:
+                # hack! getting the LineEntry via SBBreakpointLocation.GetAddress.GetLineEntry does not work good for
+                # inlined frames, so we get the description (which does take
+                # into account inlined functions) and parse it.
+                desc = lldbutil.get_description(
+                    location, lldb.eDescriptionLevelFull)
+                match = re.search('at\ ([^:]+):([\d]+)', desc)
+                try:
+                    path = match.group(1)
+                    line = int(match.group(2).strip())
+                except ValueError as e:
+                    # bp loc unparsable
+                    continue
+
+                if path in locs:
+                    locs[path].add(line)
+                else:
+                    locs[path] = set([line])
+            return locs
+
+        event_type = lldb.SBBreakpoint.GetBreakpointEventTypeFromEvent(event)
+        if event_type == lldb.eBreakpointEventTypeEnabled \
+                or event_type == lldb.eBreakpointEventTypeAdded \
+                or event_type == lldb.eBreakpointEventTypeLocationsResolved \
+                or event_type == lldb.eBreakpointEventTypeLocationsAdded:
+            self.addBPLocations(getLocations(event))
+        elif event_type == lldb.eBreakpointEventTypeRemoved \
+                or event_type == lldb.eBreakpointEventTypeLocationsRemoved \
+                or event_type == lldb.eBreakpointEventTypeDisabled:
+            self.removeBPLocations(getLocations(event))
+        elif event_type == lldb.eBreakpointEventTypeCommandChanged \
+                or event_type == lldb.eBreakpointEventTypeConditionChanged \
+                or event_type == lldb.eBreakpointEventTypeIgnoreChanged \
+                or event_type == lldb.eBreakpointEventTypeThreadChanged \
+                or event_type == lldb.eBreakpointEventTypeInvalidType:
+            # no-op
+            pass
+        self.refreshSource()

Added: lldb/trunk/utils/lui/statuswin.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/lui/statuswin.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/lui/statuswin.py (added)
+++ lldb/trunk/utils/lui/statuswin.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,41 @@
+##===-- statuswin.py -----------------------------------------*- Python -*-===##
+##
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+##
+##===----------------------------------------------------------------------===##
+
+import lldb
+import lldbutil
+import cui
+import curses
+
+
+class StatusWin(cui.TextWin):
+
+    def __init__(self, x, y, w, h):
+        super(StatusWin, self).__init__(x, y, w)
+
+        self.keys = [  # ('F1', 'Help', curses.KEY_F1),
+            ('F3', 'Cycle-focus', curses.KEY_F3),
+            ('F10', 'Quit', curses.KEY_F10)]
+
+    def draw(self):
+        self.win.addstr(0, 0, '')
+        for key in self.keys:
+            self.win.addstr('{0}'.format(key[0]), curses.A_REVERSE)
+            self.win.addstr(' {0} '.format(key[1]), curses.A_NORMAL)
+        super(StatusWin, self).draw()
+
+    def handleEvent(self, event):
+        if isinstance(event, int):
+            pass
+        elif isinstance(event, lldb.SBEvent):
+            if lldb.SBProcess.EventIsProcessEvent(event):
+                state = lldb.SBProcess.GetStateFromEvent(event)
+                status = lldbutil.state_type_to_str(state)
+                self.win.erase()
+                x = self.win.getmaxyx()[1] - len(status) - 1
+                self.win.addstr(0, x, status)
+        return

Added: lldb/trunk/utils/sync-source/README.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/sync-source/README.txt?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/sync-source/README.txt (added)
+++ lldb/trunk/utils/sync-source/README.txt Tue Jul 23 10:47:08 2019
@@ -0,0 +1,293 @@
+syncsource.py
+
+OVERVIEW
+
+The syncsource.py utility transfers groups of files between
+computers. The primary use case is to enable developing LLVM project
+software on one machine, transfer it efficiently to other machines ---
+possibly of other architectures --- and test it there. syncsource.py
+supports configurable, named source-to-destination mappings and has a
+transfer agent plug-in architecture. The current distribution provides
+an rsync-over-ssh transfer agent.
+
+The primary benefits of using syncsource.py are:
+
+* Provides a simple, reliable way to get a mirror copy of primary-
+  machine files onto several different destinations without concern
+  of compromising the patch during testing on different machines.
+
+* Handles directory-mapping differences between two machines.  For
+  LLDB, this is helpful when going between OS X and any other non-OS X
+  target system.
+
+EXAMPLE WORKFLOW
+
+This utility was developed in the context of working on the LLDB
+project.  Below we show the transfers we'd like to have happen,
+and the configuration that supports it.
+
+Workflow Example:
+
+* Develop on OS X (primary machine)
+* Test candidate changes on OS X.
+* Test candidate changes on a Linux machine (machine-name: lldb-linux).
+* Test candidate changes on a FreeBSD machine (machine-name: lldb-freebsd).
+* Do check-ins from OS X machine.
+
+Requirements:
+
+* OS X machine requires the lldb source layout: lldb, lldb/llvm,
+  lldb/llvm/tools/clang. Note this is different than the canonical
+  llvm, llvm/tools/clang, llvm/tools/lldb layout that we'll want on
+  the Linux and FreeBSD test machines.
+
+* Linux machine requires the llvm, llvm/tools/clang and
+  llvm/tools/lldb layout.
+
+* FreeBSD machine requires the same layout as the llvm machine.
+
+syncsource.py configuration in ~/.syncsourcerc:
+
+# This is my configuration with a comment.  Configuration
+# files are JSON-based.
+{ "configurations": [
+    # Here we have a collection of named configuration blocks.
+    # Configuration blocks can chain back to a parent by name.
+    {
+        # Every block has a name so it can be referenced from
+        # the command line or chained back to by a child block
+        # for sharing.
+        "name": "base_tot_settings",
+
+        # This directive lists the "directory ids" that we'll care
+        # about.  If your local repository has additional directories
+        # for other projects that need to ride along, add them here.
+        # For defaulting purposes, it makes sense to name the
+        # directory IDs as the most likely name for the directory
+        # itself.  For stock LLDB from top of tree, we generally only
+        # care about lldb, llvm and clang.
+        "dir_names": [ "llvm", "clang", "lldb" ],
+
+        # This section describes where the source files live on
+        # the primary machine.  There should always be a base_dir
+        # entry, which indicates where in the local filesystem the
+        # projects are rooted.  For each dir in dir_names, there
+        # should be either:
+        # 1. an entry named {dir-id}_dir (e.g. llvm_dir), which
+        #    specifies the source directory for the given dir id
+        #    relative to the base_dir entry, OR
+        # 2. no entry, in which case the directory is assumed to
+        #    be the same as {dir-id}.  In the example below, the
+        #    base_dir-relative directory for the "lldb" dir-id is
+        #    defaulted to being "lldb".  That's exactly what
+        #    we need in an OS X-style lldb dir layout.
+        "source": {
+            "base_dir": "~/work/lldb-tot",
+            "llvm_dir": "lldb/llvm",
+            "clang_dir": "lldb/llvm/tools/clang"
+        },
+
+        # source_excludes covers any exclusions that:
+        # * should be applied when copying files from the source
+        # * should be excluded from deletion on the destination
+        #
+        # By default, ".git", ".svn" and ".pyc" are added to
+        # all dir-id exclusions.  The default excludes can be
+        # controlled by the syncsource.py --default-excludes
+        # option.
+        #
+        # Below, I have transfer of the lldb dir skip everything
+        # rooted at "/llvm" below the lldb dir.  This is
+        # because we want the source OS X lldb to move to
+        # a destination of {some-dest-root}/llvm/tools/lldb, and
+        # not have the OS-X-inverted llvm copy over with the lldb
+        # transfer portion.  We'll see the complete picture of
+        # how this works when we get to specifying destinations
+        # later on in the config.
+        #
+        # We also exclude the "/build" and "/llvm-build" dir rooted in
+        # the OS X-side sources.  The Xcode configuration on this
+        # OS X machine will dump lldb builds in the /build directory
+        # relative to the lldb dir, and it will build llvm+clang in
+        # the /llvm-build dir relative to the lldb dir.
+        #
+        # Note the first forward slash in "/build" indicates to the
+        # transfer agent that we only want to exclude the
+        # ~/work/lldb-tot/lldb/build dir, not just any file or
+        # directory named "build" somewhere underneath the lldb
+        # directory.  Without the leading forward slash, any file
+        # or directory called build anywhere underneath the lldb dir
+        # will be excluded, which is definitely not what we want here.
+        #
+        # For the llvm dir, we do a source-side exclude for
+        # "/tools/clang".  We manage the clang transfer as a separate
+        # entity, so we don't want the movement of llvm to also move
+        # clang.
+        #
+        # The llvm_dir exclusion of "/tools/lldb" is the first example
+        # of an exclude targeting a requirement of the destination
+        # side.  Normally the transfer agent will delete anything on
+        # the destination that is not present on the source.  It is
+        # trying to mirror, and ensure both sides have the same
+        # content.  The source side of llvm on OS X does not have a
+        # "/tools/lldb", so at first this exclude looks non-sensical.
+        # But on the canonical destination layout, lldb lives in
+        # {some-dest-root}/llvm/tools/lldb.  Without this exclude,
+        # the transfer agent would blow away the tools/lldb directory
+        # on the destination every time we transfer, and then have to
+        # copy the lldb dir all over again.  For rsync+ssh, that
+        # totally would defeat the huge transfer efficiencies gained
+        # by using rsync in the first place.
+        #
+        # Note the overloading of both source and dest style excludes
+        # ultimately comes from the rsync-style exclude mechanism.
+        # If it wasn't for that, I would have separated source and
+        # dest excludes out better.
+        "source_excludes": {
+            "lldb_dir": ["/llvm", "/build", "/llvm-build"],
+            "llvm_dir": ["/tools/lldb", "/tools/clang"]
+        }
+    },
+
+    # Top of tree public, common settings for all destinations.
+    {
+        # The name for this config block.
+        "name": "common_tot",
+
+        # Here is our first chaining back to a parent config block.
+        # Any settings in "common_tot" not specified here are going
+        # to be retrieved from the parent.
+        "parent": "base_tot_settings",
+
+        # The transfer agent class to use.  Right now, the only one
+        # available is this one here that uses rsync over ssh.
+        # If some other mechanism is needed to reach this destination,
+        # it can be specified here in full [[package.]module.]class form.
+        "transfer_class": "transfer.rsync.RsyncOverSsh",
+
+        # Specifies the destination-root-relative directories.
+        # Here our desination is rooted at:
+        # {some-yet-to-be-specified-destination-root} + "base_dir".
+        # In other words, each destination will have some kind of root
+        # for all relative file placement.  We'll see those defined
+        # later, as they can change per destination machine.
+        # The block below describes the settings relative to that
+        # destination root.
+        #
+        # As before, each dir-id used in this configuration is
+        # expected to have either:
+        # 1. an entry named {dir-id}_dir (e.g. llvm_dir), which
+        #    specifies the destination directory for the given dir id
+        #    relative to the dest_root+base_dir entries, OR
+        # 2. no entry, in which case the directory is assumed to
+        #    be the same as {dir-id}.  In the example below, the
+        #    dest_root+base_dir-relative directory for the "llvm" dir-id is
+        #    defaulted to being "llvm".  That's exactly what
+        #    we need in a canonical llvm/clang/lldb setup on
+        #    Linux/FreeBSD.
+        #
+        #    Note we see the untangling of the OS X lldb-centric
+        #    directory structure to the canonical llvm,
+        #    llvm/tools/clang, llvm/tools/lldb structure below.
+        #    We are mapping lldb into a subdirectory of the llvm
+        #    directory.
+        #
+        #    The transfer logic figures out which directories to copy
+        #    first by finding the shortest destination absolute path
+        #    and doing them in that order.  For our case, this ensures
+        #    llvm is copied over before lldb or clang.
+        "dest": {
+            "base_dir": "work/mirror/git",
+            "lldb_dir": "llvm/tools/lldb",
+            "clang_dir": "llvm/tools/clang"
+        }
+    },
+
+    # Describe the lldb-linux destination.  With this,
+    # we're done with the mapping for transfer setup
+    # for the lldb-linux box.  This configuration can
+    # be used either by:
+    # 1. having a parent "default" blockthat points to this one,
+    #    which then gets used by default, or
+    # 2. using the --configuration/-c CONFIG option to
+    #    specify using this name on the syncsource.py command line.
+    {
+        "name": "lldb-linux"
+        "parent": "common_tot",
+
+        # The ssh block is understood by the rsync+ssh transfer
+        # agent.  Other agents would probably require different
+        # agent-specific details that they could read from
+        # other blocks.
+        "ssh": {
+            # This specifies the host name (or IP address) as would
+            # be used as the target for an ssh command.
+            "dest_host": "lldb-linux.example.com",
+
+            # root_dir specifies the global root directory for
+            # this destination.  All destinations on this target
+            # will be in a directory that is built from
+            # root_dir + base_dir + {dir_id}_dir.
+            "root_dir" : "/home/tfiala",
+
+            # The ssh user is specified here.
+            "user": "tfiala",
+
+            # The ssh port is specified here.
+            "port": 22
+        }
+    },
+
+    # Describe the lldb-freebsd destination.
+    # Very similar to the lldb-linux one.
+    {
+        "name": "lldb-freebsd"
+        "parent": "common_tot",
+        "ssh": {
+            "dest_host": "lldb-freebsd.example.com",
+            # Specify a different destination-specific root dir here.
+            "root_dir" : "/mnt/ssd02/fialato",
+            "user": "fialato",
+            # The ssh port is specified here.
+            "port": 2022
+        }
+    },
+
+    # If a block named "default" exists, and if no configuration
+    # is specified on the command line, then the default block
+    # will be used.  Use this block to point to the most common
+    # transfer destination you would use.
+    {
+        "name": "default",
+        "parent": "lldb-linux"
+    }
+]
+}
+
+Using it
+
+Now that we have a .syncsourcerc file set up, we can do a transfer.
+The .syncsourcerc file will be searched for as follows, using the
+first one that is found:
+
+* First check the --rc-file RCFILE option.  If this is specified
+  and doesn't exist, it will raise an error and quit.
+
+* Check if the current directory has a .syncsourcerc file.  If so,
+  use that.
+
+* Use the .syncsourcerc file from the user's home directory.
+
+Run the command:
+python /path/to/syncsource.rc -c {configuration-name}
+
+The -c {configuration-name} can be left off, in which case a
+configuration with the name 'default' will be used.
+
+After that, the transfer will occur.  With the rsync-over-ssh
+transfer agent, one rsync per dir-id will be used.  rsync output
+is redirected to the console.
+
+FEEDBACK
+
+Feel free to pass feedback along to Todd Fiala (todd.fiala at gmail.com).

Added: lldb/trunk/utils/sync-source/lib/transfer/__init__.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/sync-source/lib/transfer/__init__.py?rev=366830&view=auto
==============================================================================
    (empty)

Added: lldb/trunk/utils/sync-source/lib/transfer/protocol.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/sync-source/lib/transfer/protocol.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/sync-source/lib/transfer/protocol.py (added)
+++ lldb/trunk/utils/sync-source/lib/transfer/protocol.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,8 @@
+class Protocol(object):
+
+    def __init__(self, options, config):
+        self.options = options
+        self.config = config
+
+    def transfer(transfer_specs, dry_run):
+        raise "transfer must be overridden by transfer implementation"

Added: lldb/trunk/utils/sync-source/lib/transfer/rsync.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/sync-source/lib/transfer/rsync.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/sync-source/lib/transfer/rsync.py (added)
+++ lldb/trunk/utils/sync-source/lib/transfer/rsync.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,61 @@
+import os.path
+import pprint
+import subprocess
+import sys
+
+import transfer.protocol
+
+
+class RsyncOverSsh(transfer.protocol.Protocol):
+
+    def __init__(self, options, config):
+        super(RsyncOverSsh, self).__init__(options, config)
+        self.ssh_config = config.get_value("ssh")
+
+    def build_rsync_command(self, transfer_spec, dry_run):
+        dest_path = os.path.join(
+            self.ssh_config["root_dir"],
+            transfer_spec.dest_path)
+        flags = "-avz"
+        if dry_run:
+            flags += "n"
+        cmd = [
+            "rsync",
+            flags,
+            "-e",
+            "ssh -p {}".format(self.ssh_config["port"]),
+            "--rsync-path",
+            # The following command needs to know the right way to do
+            # this on the dest platform - ensures the target dir exists.
+            "mkdir -p {} && rsync".format(dest_path)
+        ]
+
+        # Add source dir exclusions
+        if transfer_spec.exclude_paths:
+            for exclude_path in transfer_spec.exclude_paths:
+                cmd.append("--exclude")
+                cmd.append(exclude_path)
+
+        cmd.extend([
+            "--delete",
+            transfer_spec.source_path + "/",
+            "{}@{}:{}".format(
+                self.ssh_config["user"],
+                self.ssh_config["dest_host"],
+                dest_path)])
+        return cmd
+
+    def transfer(self, transfer_specs, dry_run):
+        if self.options.verbose:
+            printer = pprint.PrettyPrinter()
+            for spec in transfer_specs:
+                printer.pprint(spec)
+
+        for spec in transfer_specs:
+            cmd = self.build_rsync_command(spec, dry_run)
+            if self.options.verbose:
+                print("executing the following command:\n{}".format(cmd))
+            result = subprocess.call(
+                cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)
+            if result != 0:
+                return result

Added: lldb/trunk/utils/sync-source/lib/transfer/transfer_spec.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/sync-source/lib/transfer/transfer_spec.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/sync-source/lib/transfer/transfer_spec.py (added)
+++ lldb/trunk/utils/sync-source/lib/transfer/transfer_spec.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,12 @@
+class TransferSpec(object):
+
+    def __init__(self, source_path, exclude_paths, dest_path):
+        self.source_path = source_path
+        self.exclude_paths = exclude_paths
+        self.dest_path = dest_path
+
+    def __repr__(self):
+        fmt = (
+            "TransferSpec(source_path='{}', exclude_paths='{}', "
+            "dest_path='{}')")
+        return fmt.format(self.source_path, self.exclude_paths, self.dest_path)

Added: lldb/trunk/utils/sync-source/pylintrc
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/sync-source/pylintrc?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/sync-source/pylintrc (added)
+++ lldb/trunk/utils/sync-source/pylintrc Tue Jul 23 10:47:08 2019
@@ -0,0 +1,2 @@
+[Master]
+init-hook='import os; import sys; sys.path.append(os.path.join(os.getcwd(), "lib")); print("hello from {}".format(os.getcwd()))'

Added: lldb/trunk/utils/sync-source/syncsource.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/sync-source/syncsource.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/sync-source/syncsource.py (added)
+++ lldb/trunk/utils/sync-source/syncsource.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,270 @@
+#!/usr/bin/env python
+"""
+Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+See https://llvm.org/LICENSE.txt for license information.
+SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+Sync lldb and related source from a local machine to a remote machine.
+
+This facilitates working on the lldb sourcecode on multiple machines
+and multiple OS types, verifying changes across all.
+"""
+
+import argparse
+import io
+import importlib
+import json
+import os.path
+import re
+import sys
+
+# Add the local lib directory to the python path.
+LOCAL_LIB_PATH = os.path.join(
+    os.path.dirname(os.path.realpath(__file__)),
+    "lib")
+sys.path.append(LOCAL_LIB_PATH)
+
+import transfer.transfer_spec
+
+
+DOTRC_BASE_FILENAME = ".syncsourcerc"
+
+
+class Configuration(object):
+    """Provides chaining configuration lookup."""
+
+    def __init__(self, rcdata_configs):
+        self.__rcdata_configs = rcdata_configs
+
+    def get_value(self, key):
+        """
+        Return the first value in the parent chain that has the key.
+
+        The traversal starts from the most derived configuration (i.e.
+        child) and works all the way up the parent chain.
+
+        @return the value of the first key in the parent chain that
+        contains a value for the given key.
+        """
+        for config in self.__rcdata_configs:
+            if key in config:
+                return config[key]
+        return None
+
+    def __getitem__(self, key):
+        value = self.get_value(key)
+        if value:
+            return value
+        else:
+            raise KeyError(key)
+
+
+def parse_args():
+    """@return options parsed from the command line."""
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--config-name", "-c", action="store", default="default",
+        help="specify configuration name to use")
+    parser.add_argument(
+        "--default-excludes", action="store", default="*.git,*.svn,*.pyc",
+        help=("comma-separated list of default file patterns to exclude "
+              "from each source directory and to protect from deletion "
+              "on each destination directory; if starting with forward "
+              "slash, it only matches at the top of the base directory"))
+    parser.add_argument(
+        "--dry-run", "-n", action="store_true",
+        help="do a dry run of the transfer operation, don't really transfer")
+    parser.add_argument(
+        "--rc-file", "-r", action="store",
+        help="specify the sync-source rc file to use for configurations")
+    parser.add_argument(
+        "--verbose", "-v", action="store_true", help="turn on verbose output")
+    return parser.parse_args()
+
+
+def read_rcfile(filename):
+    """Returns the json-parsed contents of the input file."""
+
+    # First parse file contents, removing all comments but
+    # preserving the line count.
+    regex = re.compile(r"#.*$")
+
+    comment_stripped_file = io.StringIO()
+    with open(filename, "r") as json_file:
+        for line in json_file:
+            comment_stripped_file.write(regex.sub("", line))
+    return json.load(io.StringIO(comment_stripped_file.getvalue()))
+
+
+def find_appropriate_rcfile(options):
+    # Use an options-specified rcfile if specified.
+    if options.rc_file and len(options.rc_file) > 0:
+        if not os.path.isfile(options.rc_file):
+            # If it doesn't exist, error out here.
+            raise "rcfile '{}' specified but doesn't exist".format(
+                options.rc_file)
+        return options.rc_file
+
+    # Check if current directory .sync-sourcerc exists.  If so, use it.
+    local_rc_filename = os.path.abspath(DOTRC_BASE_FILENAME)
+    if os.path.isfile(local_rc_filename):
+        return local_rc_filename
+
+    # Check if home directory .sync-sourcerc exists.  If so, use it.
+    homedir_rc_filename = os.path.abspath(
+        os.path.join(os.path.expanduser("~"), DOTRC_BASE_FILENAME))
+    if os.path.isfile(homedir_rc_filename):
+        return homedir_rc_filename
+
+    # Nothing matched.  We don't have an rc filename candidate.
+    return None
+
+
+def get_configuration(options, rcdata, config_name):
+    rcdata_configs = []
+    next_config_name = config_name
+    while next_config_name:
+        # Find the next rcdata configuration for the given name.
+        rcdata_config = next(
+            config for config in rcdata["configurations"]
+            if config["name"] == next_config_name)
+
+        # See if we found it.
+        if rcdata_config:
+            # This is our next configuration to use in the chain.
+            rcdata_configs.append(rcdata_config)
+
+            # If we have a parent, check that next.
+            if "parent" in rcdata_config:
+                next_config_name = rcdata_config["parent"]
+            else:
+                next_config_name = None
+        else:
+            raise "failed to find specified parent config '{}'".format(
+                next_config_name)
+    return Configuration(rcdata_configs)
+
+
+def create_transfer_agent(options, configuration):
+    transfer_class_spec = configuration.get_value("transfer_class")
+    if options.verbose:
+        print("specified transfer class: '{}'".format(transfer_class_spec))
+
+    # Load the module (possibly package-qualified).
+    components = transfer_class_spec.split(".")
+    module = importlib.import_module(".".join(components[:-1]))
+
+    # Create the class name we need to load.
+    clazz = getattr(module, components[-1])
+    return clazz(options, configuration)
+
+
+def sync_configured_sources(options, configuration, default_excludes):
+    # Look up the transfer method.
+    transfer_agent = create_transfer_agent(options, configuration)
+
+    # For each configured dir_names source, do the following transfer:
+    #   1. Start with base_dir + {source-dir-name}_dir
+    #   2. Copy all files recursively, but exclude
+    #      all dirs specified by source_excludes:
+    #      skip all base_dir + {source-dir-name}_dir +
+    #      {source-dir-name}_dir excludes.
+    source_dirs = configuration.get_value("source")
+    source_excludes = configuration.get_value("source_excludes")
+    dest_dirs = configuration.get_value("dest")
+
+    source_base_dir = source_dirs["base_dir"]
+    dest_base_dir = dest_dirs["base_dir"]
+    dir_ids = configuration.get_value("dir_names")
+    transfer_specs = []
+
+    for dir_id in dir_ids:
+        dir_key = "{}_dir".format(dir_id)
+
+        # Build the source dir (absolute) that we're copying from.
+        # Defaults the base-relative source dir to the source id (e.g. lldb)
+        rel_source_dir = source_dirs.get(dir_key, dir_id)
+        transfer_source_dir = os.path.expanduser(
+            os.path.join(source_base_dir, rel_source_dir))
+
+        # Exclude dirs do two things:
+        # 1) stop items from being copied on the source side, and
+        # 2) protect things from being deleted on the dest side.
+        #
+        # In both cases, they are specified relative to the base
+        # directory on either the source or dest side.
+        #
+        # Specifying a leading '/' in the directory will limit it to
+        # be rooted in the base directory.  i.e. "/.git" will only
+        # match {base-dir}/.git, not {base-dir}/subdir/.git, but
+        # ".svn" will match {base-dir}/.svn and
+        # {base-dir}/subdir/.svn.
+        #
+        # If excludes are specified for this dir_id, then pass along
+        # the excludes.  These are relative to the dir_id directory
+        # source, and get passed along that way as well.
+        transfer_source_excludes = []
+
+        # Add the source excludes for this dir.
+        skip_defaults = False
+        if source_excludes and dir_key in source_excludes:
+            transfer_source_excludes.extend(source_excludes[dir_key])
+            if "<no-defaults>" in source_excludes[dir_key]:
+                skip_defaults = True
+                transfer_source_excludes.remove("<no-defaults>")
+
+        if not skip_defaults and default_excludes is not None:
+            transfer_source_excludes.extend(list(default_excludes))
+
+        # Build the destination-base-relative dest dir into which
+        # we'll be syncing.  Relative directory defaults to the
+        # dir id
+        rel_dest_dir = dest_dirs.get(dir_key, dir_id)
+        transfer_dest_dir = os.path.join(dest_base_dir, rel_dest_dir)
+
+        # Add the exploded paths to the list that we'll ask the
+        # transfer agent to transfer for us.
+        transfer_specs.append(
+            transfer.transfer_spec.TransferSpec(
+                transfer_source_dir,
+                transfer_source_excludes,
+                transfer_dest_dir))
+
+    # Do the transfer.
+    if len(transfer_specs) > 0:
+        transfer_agent.transfer(transfer_specs, options.dry_run)
+    else:
+        raise Exception("nothing to transfer, bad configuration?")
+
+
+def main():
+    """Drives the main program."""
+    options = parse_args()
+
+    if options.default_excludes and len(options.default_excludes) > 0:
+        default_excludes = options.default_excludes.split(",")
+    else:
+        default_excludes = []
+
+    # Locate the rc filename to load, then load it.
+    rc_filename = find_appropriate_rcfile(options)
+    if rc_filename:
+        if options.verbose:
+            print("reading rc data from file '{}'".format(rc_filename))
+        rcdata = read_rcfile(rc_filename)
+    else:
+        sys.stderr.write("no rcfile specified, cannot guess configuration")
+        exit(1)
+
+    # Find configuration.
+    configuration = get_configuration(options, rcdata, options.config_name)
+    if not configuration:
+        sys.stderr.write("failed to find configuration for {}".format(
+            options.config_data))
+        exit(2)
+
+    # Kick off the transfer.
+    sync_configured_sources(options, configuration, default_excludes)
+
+if __name__ == "__main__":
+    main()

Added: lldb/trunk/utils/test/README-disasm
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/test/README-disasm?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/test/README-disasm (added)
+++ lldb/trunk/utils/test/README-disasm Tue Jul 23 10:47:08 2019
@@ -0,0 +1,406 @@
+This README describes a sample invocation of disasm.py whose purpose is to test
+the low level ARM/Thumb disassembly functionality from llvm using the llvm-mc
+command line.  We invoke gdb on an executable, try to disassemble a function,
+and then read the memory contents of the disassembled function.
+
+The byte contents are written into a file named disasm-input.txt and then we
+invoke llvm-mc -disassemble plus options (set with the -o/--options) on the
+byte contents.
+
+See the following for a sample session using this command:
+
+[16:26:57] johnny:/Volumes/data/Radar/9131529 $ /Volumes/data/lldb/svn/trunk/utils/test/disasm.py -C 'set shlib-path-substitutions /usr /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr /System /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System /Library /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/Library' -O '-arch armv7' -m /Volumes/data/lldb/llvm/Debug+Asserts/bin/llvm-mc -e /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib -f printf --options='-triple=thumb-apple-darwin -debug-only=arm-disassembler'
+gdb commands: ['set shlib-path-substitutions /usr /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr /System /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System /Library /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/Library']
+gdb options: -arch armv7
+executable: /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib
+function: printf
+llvm-mc: /Volumes/data/lldb/llvm/Debug+Asserts/bin/llvm-mc
+llvm-mc options: -triple=thumb-apple-darwin -debug-only=arm-disassembler
+GNU gdb 6.3.50-20050815 (Apple version gdb-1518) (Sat Feb 12 02:56:02 UTC 2011)
+Copyright 2004 Free Software Foundation, Inc.
+GDB is free software, covered by the GNU General Public License, and you are
+welcome to change it and/or distribute copies of it under certain conditions.
+Type "show copying" to see the conditions.
+There is absolutely no warranty for GDB.  Type "show warranty" for details.
+This GDB was configured as "--host=x86_64-apple-darwin --target=arm-apple-darwin".
+<Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/Library
+<eloper/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib
+Reading symbols for shared libraries ................ done
+Reading symbols from /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib...done.
+(gdb) disassemble printf
+Dump of assembler code for function printf:
+0x0704cdd0 <printf+0>:	push	{r0, r1, r2, r3}
+0x0704cdd2 <printf+2>:	push	{r4, r5, r7, lr}
+0x0704cdd4 <printf+4>:	add	r7, sp, #8
+0x0704cdd6 <printf+6>:	sub	sp, #4
+0x0704cdd8 <printf+8>:	add	r3, sp, #20
+0x0704cdda <printf+10>:	ldr.w	r5, [r3], #4
+0x0704cdde <printf+14>:	str	r3, [sp, #0]
+0x0704cde0 <printf+16>:	ldr	r3, [pc, #52]	(0x704ce18 <printf+72>)
+0x0704cde2 <printf+18>:	add	r3, pc
+0x0704cde4 <printf+20>:	ldr	r0, [r3, #0]
+0x0704cde6 <printf+22>:	ldr	r4, [r0, #0]
+0x0704cde8 <printf+24>:	ldr	r0, [pc, #48]	(0x704ce1c <printf+76>)
+0x0704cdea <printf+26>:	add	r0, pc
+0x0704cdec <printf+28>:	ldr	r0, [r0, #0]
+0x0704cdee <printf+30>:	ldr	r0, [r0, #0]
+0x0704cdf0 <printf+32>:	blx	0x707ba30 <pthread_getspecific>
+0x0704cdf4 <printf+36>:	cbnz	r0, 0x704cdfe <printf+46>
+0x0704cdf6 <printf+38>:	ldr	r1, [pc, #40]	(0x704ce20 <printf+80>)
+0x0704cdf8 <printf+40>:	add	r1, pc
+0x0704cdfa <printf+42>:	ldr	r1, [r1, #0]
+0x0704cdfc <printf+44>:	b.n	0x704ce00 <printf+48>
+0x0704cdfe <printf+46>:	mov	r1, r0
+0x0704ce00 <printf+48>:	mov	r0, r4
+0x0704ce02 <printf+50>:	mov	r2, r5
+0x0704ce04 <printf+52>:	ldr	r3, [sp, #0]
+0x0704ce06 <printf+54>:	bl	0x704ad44 <vfprintf_l>
+0x0704ce0a <printf+58>:	sub.w	sp, r7, #8	; 0x8
+0x0704ce0e <printf+62>:	ldmia.w	sp!, {r4, r5, r7, lr}
+0x0704ce12 <printf+66>:	add	sp, #16
+0x0704ce14 <printf+68>:	bx	lr
+0x0704ce16 <printf+70>:	nop
+0x0704ce18 <printf+72>:	movs	r3, #142
+0x0704ce1a <printf+74>:	lsls	r5, r0, #0
+0x0704ce1c <printf+76>:	adds	r1, #122
+0x0704ce1e <printf+78>:	lsls	r5, r0, #0
+0x0704ce20 <printf+80>:	adds	r1, #104
+0x0704ce22 <printf+82>:	lsls	r5, r0, #0
+End of assembler dump.
+(gdb) x /2b 0x0704cdd0
+0x704cdd0 <printf>:	0x0f	0xb4
+(gdb) x /2b 0x0704cdd2
+0x704cdd2 <printf+2>:	0xb0	0xb5
+(gdb) x /2b 0x0704cdd4
+0x704cdd4 <printf+4>:	0x02	0xaf
+(gdb) x /2b 0x0704cdd6
+0x704cdd6 <printf+6>:	0x81	0xb0
+(gdb) x /2b 0x0704cdd8
+0x704cdd8 <printf+8>:	0x05	0xab
+(gdb) x /4b 0x0704cdda
+0x704cdda <printf+10>:	0x53	0xf8	0x04	0x5b
+(gdb) x /2b 0x0704cdde
+0x704cdde <printf+14>:	0x00	0x93
+(gdb) x /2b 0x0704cde0
+0x704cde0 <printf+16>:	0x0d	0x4b
+(gdb) x /2b 0x0704cde2
+0x704cde2 <printf+18>:	0x7b	0x44
+(gdb) x /2b 0x0704cde4
+0x704cde4 <printf+20>:	0x18	0x68
+(gdb) x /2b 0x0704cde6
+0x704cde6 <printf+22>:	0x04	0x68
+(gdb) x /2b 0x0704cde8
+0x704cde8 <printf+24>:	0x0c	0x48
+(gdb) x /2b 0x0704cdea
+0x704cdea <printf+26>:	0x78	0x44
+(gdb) x /2b 0x0704cdec
+0x704cdec <printf+28>:	0x00	0x68
+(gdb) x /2b 0x0704cdee
+0x704cdee <printf+30>:	0x00	0x68
+(gdb) x /4b 0x0704cdf0
+0x704cdf0 <printf+32>:	0x2e	0xf0	0x1e	0xee
+(gdb) x /2b 0x0704cdf4
+0x704cdf4 <printf+36>:	0x18	0xb9
+(gdb) x /2b 0x0704cdf6
+0x704cdf6 <printf+38>:	0x0a	0x49
+(gdb) x /2b 0x0704cdf8
+0x704cdf8 <printf+40>:	0x79	0x44
+(gdb) x /2b 0x0704cdfa
+0x704cdfa <printf+42>:	0x09	0x68
+(gdb) x /2b 0x0704cdfc
+0x704cdfc <printf+44>:	0x00	0xe0
+(gdb) x /2b 0x0704cdfe
+0x704cdfe <printf+46>:	0x01	0x46
+(gdb) x /2b 0x0704ce00
+0x704ce00 <printf+48>:	0x20	0x46
+(gdb) x /2b 0x0704ce02
+0x704ce02 <printf+50>:	0x2a	0x46
+(gdb) x /2b 0x0704ce04
+0x704ce04 <printf+52>:	0x00	0x9b
+(gdb) x /4b 0x0704ce06
+0x704ce06 <printf+54>:	0xfd	0xf7	0x9d	0xff
+(gdb) x /4b 0x0704ce0a
+0x704ce0a <printf+58>:	0xa7	0xf1	0x08	0x0d
+(gdb) x /4b 0x0704ce0e
+0x704ce0e <printf+62>:	0xbd	0xe8	0xb0	0x40
+(gdb) x /2b 0x0704ce12
+0x704ce12 <printf+66>:	0x04	0xb0
+(gdb) x /2b 0x0704ce14
+0x704ce14 <printf+68>:	0x70	0x47
+(gdb) x /2b 0x0704ce16
+0x704ce16 <printf+70>:	0x00	0xbf
+(gdb) x /2b 0x0704ce18
+0x704ce18 <printf+72>:	0x8e	0x23
+(gdb) x /2b 0x0704ce1a
+0x704ce1a <printf+74>:	0x05	0x00
+(gdb) x /2b 0x0704ce1c
+0x704ce1c <printf+76>:	0x7a	0x31
+(gdb) x /2b 0x0704ce1e
+0x704ce1e <printf+78>:	0x05	0x00
+(gdb) x /2b 0x0704ce20
+0x704ce20 <printf+80>:	0x68	0x31
+(gdb) x /2b 0x0704ce22
+0x704ce22 <printf+82>:	0x05	0x00
+(gdb) quit
+
+Executing command: /Volumes/data/lldb/llvm/Debug+Asserts/bin/llvm-mc -disassemble -triple=thumb-apple-darwin -debug-only=arm-disassembler disasm-input.txt
+Opcode=2305 Name=tPUSH Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 0: 1: 0: 0| 0: 0: 0: 0| 1: 1: 1: 1|
+-------------------------------------------------------------------------------------------------
+
+	push	{r0, r1, r2, r3}
+Opcode=2305 Name=tPUSH Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 0: 1: 0: 1| 1: 0: 1: 1| 0: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	push	{r4, r5, r7, lr}
+Opcode=2228 Name=tADDrSPi Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 0| 1: 1: 1: 1| 0: 0: 0: 0| 0: 0: 1: 0|
+-------------------------------------------------------------------------------------------------
+
+	add	r7, sp, #8
+Opcode=2328 Name=tSUBspi Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 0: 0: 0: 0| 1: 0: 0: 0| 0: 0: 0: 1|
+-------------------------------------------------------------------------------------------------
+
+	sub	sp, #4
+Opcode=2228 Name=tADDrSPi Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 0| 1: 0: 1: 1| 0: 0: 0: 0| 0: 1: 0: 1|
+-------------------------------------------------------------------------------------------------
+
+	add	r3, sp, #20
+Opcode=1963 Name=t2LDR_POST Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 1: 1: 1: 1| 1: 0: 0: 0| 0: 1: 0: 1| 0: 0: 1: 1| 0: 1: 0: 1| 1: 0: 1: 1| 0: 0: 0: 0| 0: 1: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	ldr	r5, [r3], #4
+Opcode=2324 Name=tSTRspi Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 0: 1| 0: 0: 1: 1| 0: 0: 0: 0| 0: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	str	r3, [sp]
+Opcode=2275 Name=tLDRpci Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 1: 0: 1: 1| 0: 0: 0: 0| 1: 1: 0: 1|
+-------------------------------------------------------------------------------------------------
+
+	ldr.n	r3, #52
+Opcode=2223 Name=tADDhirr Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 0: 0| 0: 1: 1: 1| 1: 0: 1: 1|
+-------------------------------------------------------------------------------------------------
+
+	add	r3, pc
+Opcode=2274 Name=tLDRi Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 1: 0| 1: 0: 0: 0| 0: 0: 0: 1| 1: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	ldr	r0, [r3]
+Opcode=2274 Name=tLDRi Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 1: 0| 1: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	ldr	r4, [r0]
+Opcode=2275 Name=tLDRpci Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 1: 0: 0: 0| 0: 0: 0: 0| 1: 1: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	ldr.n	r0, #48
+Opcode=2223 Name=tADDhirr Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 0: 0| 0: 1: 1: 1| 1: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	add	r0, pc
+Opcode=2274 Name=tLDRi Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 1: 0| 1: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	ldr	r0, [r0]
+Opcode=2274 Name=tLDRi Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 1: 0| 1: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	ldr	r0, [r0]
+Opcode=2243 Name=tBLXi_r9 Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 1: 1: 1: 1| 0: 0: 0: 0| 0: 0: 1: 0| 1: 1: 1: 0| 1: 1: 1: 0| 1: 1: 1: 0| 0: 0: 0: 1| 1: 1: 1: 0|
+-------------------------------------------------------------------------------------------------
+
+	blx	#191548
+Opcode=2255 Name=tCBNZ Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 1: 0: 0: 1| 0: 0: 0: 1| 1: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	cbnz	r0, #6
+Opcode=2275 Name=tLDRpci Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 1: 0: 0: 1| 0: 0: 0: 0| 1: 0: 1: 0|
+-------------------------------------------------------------------------------------------------
+
+	ldr.n	r1, #40
+Opcode=2223 Name=tADDhirr Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 0: 0| 0: 1: 1: 1| 1: 0: 0: 1|
+-------------------------------------------------------------------------------------------------
+
+	add	r1, pc
+Opcode=2274 Name=tLDRi Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 1: 0| 1: 0: 0: 0| 0: 0: 0: 0| 1: 0: 0: 1|
+-------------------------------------------------------------------------------------------------
+
+	ldr	r1, [r1]
+Opcode=2238 Name=tB Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 1: 1: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	b	#0
+Opcode=2294 Name=tMOVr Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 1: 0| 0: 0: 0: 0| 0: 0: 0: 1|
+-------------------------------------------------------------------------------------------------
+
+	mov	r1, r0
+Opcode=2294 Name=tMOVr Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 1: 0| 0: 0: 1: 0| 0: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	mov	r0, r4
+Opcode=2294 Name=tMOVr Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 1: 0| 0: 0: 1: 0| 1: 0: 1: 0|
+-------------------------------------------------------------------------------------------------
+
+	mov	r2, r5
+Opcode=2278 Name=tLDRspi Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 0: 1| 1: 0: 1: 1| 0: 0: 0: 0| 0: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	ldr	r3, [sp]
+Opcode=2246 Name=tBLr9 Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 1: 1: 1: 1| 0: 1: 1: 1| 1: 1: 1: 1| 1: 1: 0: 1| 1: 1: 1: 1| 1: 1: 1: 1| 1: 0: 0: 1| 1: 1: 0: 1|
+-------------------------------------------------------------------------------------------------
+
+	bl	#-8390
+Opcode=2153 Name=t2SUBri Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 1: 1: 1: 1| 0: 0: 0: 1| 1: 0: 1: 0| 0: 1: 1: 1| 0: 0: 0: 0| 1: 1: 0: 1| 0: 0: 0: 0| 1: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	sub.w	sp, r7, #8
+Opcode=1926 Name=t2LDMIA_UPD Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 1: 1: 1: 0| 1: 0: 0: 0| 1: 0: 1: 1| 1: 1: 0: 1| 0: 1: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 0: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	pop.w	{r4, r5, r7, lr}
+Opcode=2230 Name=tADDspi Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	add	sp, #16
+Opcode=2250 Name=tBX_RET Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 0| 0: 1: 1: 1| 0: 1: 1: 1| 0: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	bx	lr
+Opcode=2300 Name=tNOP Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 1: 0: 1: 1| 1: 1: 1: 1| 0: 0: 0: 0| 0: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	nop
+Opcode=2293 Name=tMOVi8 Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 1: 0| 0: 0: 1: 1| 1: 0: 0: 0| 1: 1: 1: 0|
+-------------------------------------------------------------------------------------------------
+
+	movs	r3, #142
+Opcode=2290 Name=tMOVSr Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 1|
+-------------------------------------------------------------------------------------------------
+
+	movs	r5, r0
+Opcode=2225 Name=tADDi8 Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 1: 1| 0: 0: 0: 1| 0: 1: 1: 1| 1: 0: 1: 0|
+-------------------------------------------------------------------------------------------------
+
+	adds	r1, #122
+Opcode=2290 Name=tMOVSr Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 1|
+-------------------------------------------------------------------------------------------------
+
+	movs	r5, r0
+Opcode=2225 Name=tADDi8 Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 1: 1| 0: 0: 0: 1| 0: 1: 1: 0| 1: 0: 0: 0|
+-------------------------------------------------------------------------------------------------
+
+	adds	r1, #104
+Opcode=2290 Name=tMOVSr Format=ARM_FORMAT_THUMBFRM(25)
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 
+-------------------------------------------------------------------------------------------------
+| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 0: 0: 0| 0: 1: 0: 1|
+-------------------------------------------------------------------------------------------------
+
+	movs	r5, r0
+[16:28:00] johnny:/Volumes/data/Radar/9131529 $ 

Added: lldb/trunk/utils/test/README-lldb-disasm
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/test/README-lldb-disasm?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/test/README-lldb-disasm (added)
+++ lldb/trunk/utils/test/README-lldb-disasm Tue Jul 23 10:47:08 2019
@@ -0,0 +1,94 @@
+This README describes a sample invocation of lldb-disasm.py whose purpose is to test
+the lldb 'disassemble' command.
+
+This is for the initial checkin of lldb-disasm.py which only reads an executable image and
+dumps the symbol table from the imgae and its dependent libraries.  The output was cut off
+since it is too large.
+
+da0603a-dhcp191:9131529 johnny$ /Volumes/data/lldb/svn/trunk/utils/test/lldb-disasm.py -C 'platform create remote-ios' -e /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib
+lldb commands: ['platform create remote-ios']
+lldb options: None
+executable: /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib
+sys.path: ['/Volumes/data/lldb/svn/trunk/utils/test', '/Volumes/data/lldb/svn/trunk/build/Debug/LLDB.framework/Resources/Python', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python26.zip', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-mac', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-mac/lib-scriptpackages', '/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-tk', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-old', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-dynload', '/Library/Python/2.6/site-packages', '/AppleInternal/Library/Python/2.6/site-packages', '/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/PyObjC', '/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/wx-2.8-mac-unicode', '/Volumes/data/lldb/svn/trunk/utils/test/../../test/pexpect-2.4', '/Volumes/data/lldb/svn/trunk/test']
+/Volumes/data/lldb/svn/trunk/test/lldbutil.py:80: SyntaxWarning: import * only allowed at module level
+  def int_to_bytearray(val, bytesize):
+/Volumes/data/lldb/svn/trunk/test/lldbutil.py:105: SyntaxWarning: import * only allowed at module level
+  def bytearray_to_int(bytes, bytesize):
+run command: platform create remote-ios
+output: Platform: remote-ios
+Not connected to a remote platform.
+SDKROOT: "/Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3 (8F190)"
+
+run command: file /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib
+output: Current executable set to '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib' (armv7).
+
+run command: image dump symtab
+output: Dumping symbol table for 18 modules.
+Symtab, file = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/usr/lib/libSystem.B.dylib, num_symbols = 851:
+               Debug symbol
+               |Synthetic symbol
+               ||Externally Visible
+               |||
+Index   UserID DSX Type         File Address/Value Load Address       Size               Flags      Name
+------- ------ --- ------------ ------------------ ------------------ ------------------ ---------- ----------------------------------
+[    0]      0     Code         0x0000000000001420                    0x0000000000000000 0x000e0008 libSystem_initializer
+[    1]      1     Code         0x00000000000014c4                    0x0000000000000000 0x001e0008 __keymgr_initializer
+[    2]      2     Code         0x00000000000014fc                    0x0000000000000000 0x000e0008 dwarf2_unwind_dyld_add_image_hook
+[    3]      3     Code         0x0000000000001564                    0x0000000000000000 0x000e0008 get_or_create_key_element
+[    4]      4     Code         0x0000000000001684                    0x0000000000000000 0x000e0008 unlock_node
+[    5]      5     Code         0x0000000000001930                    0x0000000000000000 0x000e0000 RsqrtTable
+[    6]      6     Code         0x0000000000001c30                    0x0000000000000000 0x000e0000 acosf_crossover
+[    7]      7     Code         0x0000000000001c34                    0x0000000000000000 0x000e0000 acosf_mid_poly
+[    8]      8     Code         0x0000000000001c48                    0x0000000000000000 0x000e0000 Pi2_Q30
+[    9]      9     Code         0x0000000000001c4c                    0x0000000000000000 0x000e0000 Pi_Q30
+[   10]     10     Code         0x0000000000001c78                    0x0000000000000000 0x000e0000 acosf_approx
+[   11]     11     Code         0x0000000000001cec                    0x0000000000000000 0x000e0000 acosf_pos_tail_poly
+[   12]     12     Code         0x0000000000001d00                    0x0000000000000000 0x000e0000 acosf_tail
+[   13]     13     Code         0x0000000000001dfc                    0x0000000000000000 0x000e0000 acosf_normalize
+[   14]     14     Code         0x0000000000001e10                    0x0000000000000000 0x000e0000 acosf_round
+[   15]     15     Code         0x0000000000001e28                    0x0000000000000000 0x000e0000 acosf_encode
+[   16]     16     Code         0x0000000000001e30                    0x0000000000000000 0x000e0000 acosf_done
+[   17]     17     Code         0x0000000000001e38                    0x0000000000000000 0x000e0000 acosf_special
+[   18]     18     Code         0x0000000000001e68                    0x0000000000000000 0x000e0000 acosf_small
+[   19]     19     Code         0x0000000000001e9c                    0x0000000000000000 0x000e0000 acosf_very_small
+[   20]     20     Code         0x0000000000001eb8                    0x0000000000000000 0x000e0000 Pif
+[   21]     21     Code         0x000000000000220c                    0x0000000000000000 0x000e0000 RsqrtTable
+[   22]     22     Code         0x000000000000250c                    0x0000000000000000 0x000e0000 asinf_crossover
+[   23]     23     Code         0x0000000000002510                    0x0000000000000000 0x000e0000 asinf_mid_poly
+[   24]     24     Code         0x0000000000002524                    0x0000000000000000 0x000e0000 Pi2_Q30
+[   25]     25     Code         0x0000000000002550                    0x0000000000000000 0x000e0000 asinf_approx
+[   26]     26     Code         0x00000000000025e4                    0x0000000000000000 0x000e0000 asinf_tail_poly
+[   27]     27     Code         0x0000000000002600                    0x0000000000000000 0x000e0000 asinf_tail
+[   28]     28     Code         0x00000000000026e0                    0x0000000000000000 0x000e0000 asinf_normalize
+[   29]     29     Code         0x00000000000026f4                    0x0000000000000000 0x000e0000 asinf_round
+[   30]     30     Code         0x000000000000270c                    0x0000000000000000 0x000e0000 asinf_encode
+[   31]     31     Code         0x0000000000002718                    0x0000000000000000 0x000e0000 asinf_done
+[   32]     32     Code         0x0000000000002720                    0x0000000000000000 0x000e0000 asinf_special
+[   33]     33     Code         0x0000000000002754                    0x0000000000000000 0x000e0000 asinf_small
+[   34]     34     Code         0x0000000000002784                    0x0000000000000000 0x000e0000 Pi2f
+[   35]     35     Code         0x0000000000005774                    0x0000000000000000 0x000e0008 rem_pio2
+[   36]     36     Code         0x00000000000076c4                    0x0000000000000000 0x000e0008 __kernel_rem_pio2
+[   37]     37     Code         0x0000000000008c90                    0x0000000000000000 0x000e0008 __kernel_tan
+[   38]     38     Code         0x0000000000008ef0                    0x0000000000000000 0x000e0008 lgammaApprox
+[   39]     39     Code         0x000000000000b3d4                    0x0000000000000000 0x000e0000 powf_not_special
+[   40]     40     Code         0x000000000000b3dc                    0x0000000000000000 0x000e0000 powf_ylgx
+[   41]     41     Code         0x000000000000b438                    0x0000000000000000 0x000e0000 powf_done
+[   42]     42     Code         0x000000000000b43c                    0x0000000000000000 0x000e0000 powf_special_y
+[   43]     43     Code         0x000000000000b4a8                    0x0000000000000000 0x000e0000 powf_special_x
+[   44]     44     Code         0x000000000000b4cc                    0x0000000000000000 0x000e0000 powf_mzero_minf
+[   45]     45     Code         0x000000000000b54c                    0x0000000000000000 0x000e0000 powf_y_odd
+[   46]     46     Code         0x000000000000b57c                    0x0000000000000000 0x000e0000 powf_y_nonint
+[   47]     47     Code         0x000000000000b588                    0x0000000000000000 0x000e0000 powf_y_even
+[   48]     48     Code         0x000000000000b7a8                    0x0000000000000000 0x000e0000 powf_log2_reduction
+[   49]     49     Code         0x000000000000b7a8                    0x0000000000000000 0x000e0000 powf_log2
+[   50]     50     Code         0x000000000000b814                    0x0000000000000000 0x000e0000 powf_log2_approx
+[   51]     51     Code         0x000000000000b88c                    0x0000000000000000 0x000e0000 powf_log2_synthesis
+[   52]     52     Code         0x000000000000b960                    0x0000000000000000 0x000e0000 powf_log2_exactPowerOfTwo
+[   53]     53     Code         0x000000000000b980                    0x0000000000000000 0x000e0000 powf_log2_near1
+[   54]     54     Code         0x000000000000b9ec                    0x0000000000000000 0x000e0000 powf_log2_synthesis_near1
+[   55]     55     Code         0x000000000000ba04                    0x0000000000000000 0x000e0000 Q32_minimax
+[   56]     56     Code         0x000000000000ba10                    0x0000000000000000 0x000e0000 iexp2_lut
+[   57]     57     Code         0x000000000000ba94                    0x0000000000000000 0x000e0000 powf_exp2
+[   58]     58     Code         0x000000000000bb18                    0x0000000000000000 0x000e0000 powf_exp2_exact_int
+[   59]     59     Code         0x000000000000bb24                    0x0000000000000000 0x000e0000 powf_exp2_big
+[   60]     60     Code         0x000000000000bb74                    0x0000000000000000 0x000e0000 powf_exp2_overflow

Added: lldb/trunk/utils/test/README-run-until-faulted
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/test/README-run-until-faulted?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/test/README-run-until-faulted (added)
+++ lldb/trunk/utils/test/README-run-until-faulted Tue Jul 23 10:47:08 2019
@@ -0,0 +1,18 @@
+A example usage of the Python script run-until-faulted.py:
+
+[18:20:29] johnny:/Volumes/data/lldb/svn/trunk/utils/test $ ./run-until-faulted.py -l /Volumes/data/lldb/svn/trunk/build/Debug/lldb -e './a.out'
+lldb command: /Volumes/data/lldb/svn/trunk/build/Debug/lldb
+executable: ./a.out
+executable options: 
+sending file command....
+sending process launch --  (iteration: 0)
+
+* thread #1: tid = 0x2d03, 0x0000000100000eef a.out`main + 39 at main.c:7, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
+   4   	{
+   5   	    int *null_ptr = 0;
+   6   	    printf("Hello, fault!\n");
+-> 7   	    printf("Now segfault %d\n", *null_ptr);
+   8   	}
+
+(lldb) q
+[18:20:40] johnny:/Volumes/data/lldb/svn/trunk/utils/test $ 

Added: lldb/trunk/utils/test/disasm.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/test/disasm.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/test/disasm.py (added)
+++ lldb/trunk/utils/test/disasm.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,238 @@
+#!/usr/bin/env python
+
+"""
+Run gdb to disassemble a function, feed the bytes to 'llvm-mc -disassemble' command,
+and display the disassembly result.
+
+"""
+
+from __future__ import print_function
+
+import os
+import sys
+from optparse import OptionParser
+
+
+def is_exe(fpath):
+    """Check whether fpath is an executable."""
+    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+
+
+def which(program):
+    """Find the full path to a program, or return None."""
+    fpath, fname = os.path.split(program)
+    if fpath:
+        if is_exe(program):
+            return program
+    else:
+        for path in os.environ["PATH"].split(os.pathsep):
+            exe_file = os.path.join(path, program)
+            if is_exe(exe_file):
+                return exe_file
+    return None
+
+
+def do_llvm_mc_disassembly(
+        gdb_commands,
+        gdb_options,
+        exe,
+        func,
+        mc,
+        mc_options):
+    from io import StringIO
+    import pexpect
+
+    gdb_prompt = "\r\n\(gdb\) "
+    gdb = pexpect.spawn(('gdb %s' % gdb_options) if gdb_options else 'gdb')
+    # Turn on logging for what gdb sends back.
+    gdb.logfile_read = sys.stdout
+    gdb.expect(gdb_prompt)
+
+    # See if there any extra command(s) to execute before we issue the file
+    # command.
+    for cmd in gdb_commands:
+        gdb.sendline(cmd)
+        gdb.expect(gdb_prompt)
+
+    # Now issue the file command.
+    gdb.sendline('file %s' % exe)
+    gdb.expect(gdb_prompt)
+
+    # Send the disassemble command.
+    gdb.sendline('disassemble %s' % func)
+    gdb.expect(gdb_prompt)
+
+    # Get the output from gdb.
+    gdb_output = gdb.before
+
+    # Use StringIO to record the memory dump as well as the gdb assembler code.
+    mc_input = StringIO()
+
+    # These keep track of the states of our simple gdb_output parser.
+    prev_line = None
+    prev_addr = None
+    curr_addr = None
+    addr_diff = 0
+    looking = False
+    for line in gdb_output.split(os.linesep):
+        if line.startswith('Dump of assembler code'):
+            looking = True
+            continue
+
+        if line.startswith('End of assembler dump.'):
+            looking = False
+            prev_addr = curr_addr
+            if mc_options and mc_options.find('arm') != -1:
+                addr_diff = 4
+            if mc_options and mc_options.find('thumb') != -1:
+                # It is obviously wrong to assume the last instruction of the
+                # function has two bytes.
+                # FIXME
+                addr_diff = 2
+
+        if looking and line.startswith('0x'):
+            # It's an assembler code dump.
+            prev_addr = curr_addr
+            curr_addr = line.split(None, 1)[0]
+            if prev_addr and curr_addr:
+                addr_diff = int(curr_addr, 16) - int(prev_addr, 16)
+
+        if prev_addr and addr_diff > 0:
+            # Feed the examining memory command to gdb.
+            gdb.sendline('x /%db %s' % (addr_diff, prev_addr))
+            gdb.expect(gdb_prompt)
+            x_output = gdb.before
+            # Get the last output line from the gdb examine memory command,
+            # split the string into a 3-tuple with separator '>:' to handle
+            # objc method names.
+            memory_dump = x_output.split(
+                os.linesep)[-1].partition('>:')[2].strip()
+            # print "\nbytes:", memory_dump
+            disasm_str = prev_line.partition('>:')[2]
+            print('%s # %s' % (memory_dump, disasm_str), file=mc_input)
+
+        # We're done with the processing.  Assign the current line to be
+        # prev_line.
+        prev_line = line
+
+    # Close the gdb session now that we are done with it.
+    gdb.sendline('quit')
+    gdb.expect(pexpect.EOF)
+    gdb.close()
+
+    # Write the memory dump into a file.
+    with open('disasm-input.txt', 'w') as f:
+        f.write(mc_input.getvalue())
+
+    mc_cmd = '%s -disassemble %s disasm-input.txt' % (mc, mc_options)
+    print("\nExecuting command:", mc_cmd)
+    os.system(mc_cmd)
+
+    # And invoke llvm-mc with the just recorded file.
+    #mc = pexpect.spawn('%s -disassemble %s disasm-input.txt' % (mc, mc_options))
+    #mc.logfile_read = sys.stdout
+    # print "mc:", mc
+    # mc.close()
+
+
+def main():
+    # This is to set up the Python path to include the pexpect-2.4 dir.
+    # Remember to update this when/if things change.
+    scriptPath = sys.path[0]
+    sys.path.append(
+        os.path.join(
+            scriptPath,
+            os.pardir,
+            os.pardir,
+            'test',
+            'pexpect-2.4'))
+
+    parser = OptionParser(usage="""\
+Run gdb to disassemble a function, feed the bytes to 'llvm-mc -disassemble' command,
+and display the disassembly result.
+
+Usage: %prog [options]
+""")
+    parser.add_option(
+        '-C',
+        '--gdb-command',
+        type='string',
+        action='append',
+        metavar='COMMAND',
+        default=[],
+        dest='gdb_commands',
+        help='Command(s) gdb executes after starting up (can be empty)')
+    parser.add_option(
+        '-O',
+        '--gdb-options',
+        type='string',
+        action='store',
+        dest='gdb_options',
+        help="""The options passed to 'gdb' command if specified.""")
+    parser.add_option('-e', '--executable',
+                      type='string', action='store',
+                      dest='executable',
+                      help="""The executable to do disassembly on.""")
+    parser.add_option(
+        '-f',
+        '--function',
+        type='string',
+        action='store',
+        dest='function',
+        help="""The function name (could be an address to gdb) for disassembly.""")
+    parser.add_option('-m', '--llvm-mc',
+                      type='string', action='store',
+                      dest='llvm_mc',
+                      help="""The llvm-mc executable full path, if specified.
+                      Otherwise, it must be present in your PATH environment.""")
+
+    parser.add_option(
+        '-o',
+        '--options',
+        type='string',
+        action='store',
+        dest='llvm_mc_options',
+        help="""The options passed to 'llvm-mc -disassemble' command if specified.""")
+
+    opts, args = parser.parse_args()
+
+    gdb_commands = opts.gdb_commands
+    gdb_options = opts.gdb_options
+
+    if not opts.executable:
+        parser.print_help()
+        sys.exit(1)
+    executable = opts.executable
+
+    if not opts.function:
+        parser.print_help()
+        sys.exit(1)
+    function = opts.function
+
+    llvm_mc = opts.llvm_mc if opts.llvm_mc else which('llvm-mc')
+    if not llvm_mc:
+        parser.print_help()
+        sys.exit(1)
+
+    # This is optional.  For example:
+    # --options='-triple=arm-apple-darwin -debug-only=arm-disassembler'
+    llvm_mc_options = opts.llvm_mc_options
+
+    # We have parsed the options.
+    print("gdb commands:", gdb_commands)
+    print("gdb options:", gdb_options)
+    print("executable:", executable)
+    print("function:", function)
+    print("llvm-mc:", llvm_mc)
+    print("llvm-mc options:", llvm_mc_options)
+
+    do_llvm_mc_disassembly(
+        gdb_commands,
+        gdb_options,
+        executable,
+        function,
+        llvm_mc,
+        llvm_mc_options)
+
+if __name__ == '__main__':
+    main()

Propchange: lldb/trunk/utils/test/disasm.py
------------------------------------------------------------------------------
    svn:executable = *

Added: lldb/trunk/utils/test/lldb-disasm.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/test/lldb-disasm.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/test/lldb-disasm.py (added)
+++ lldb/trunk/utils/test/lldb-disasm.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,294 @@
+#!/usr/bin/env python
+
+"""
+Run lldb to disassemble all the available functions for an executable image.
+
+"""
+
+from __future__ import print_function
+
+import os
+import re
+import sys
+from optparse import OptionParser
+
+
+def setupSysPath():
+    """
+    Add LLDB.framework/Resources/Python and the test dir to the sys.path.
+    """
+    # Get the directory containing the current script.
+    scriptPath = sys.path[0]
+    if not scriptPath.endswith(os.path.join('utils', 'test')):
+        print("This script expects to reside in lldb's utils/test directory.")
+        sys.exit(-1)
+
+    # This is our base name component.
+    base = os.path.abspath(os.path.join(scriptPath, os.pardir, os.pardir))
+
+    # This is for the goodies in the test directory under base.
+    sys.path.append(os.path.join(base, 'test'))
+
+    # These are for xcode build directories.
+    xcode3_build_dir = ['build']
+    xcode4_build_dir = ['build', 'lldb', 'Build', 'Products']
+    dbg = ['Debug']
+    rel = ['Release']
+    bai = ['BuildAndIntegration']
+    python_resource_dir = ['LLDB.framework', 'Resources', 'Python']
+
+    dbgPath = os.path.join(
+        base, *(xcode3_build_dir + dbg + python_resource_dir))
+    dbgPath2 = os.path.join(
+        base, *(xcode4_build_dir + dbg + python_resource_dir))
+    relPath = os.path.join(
+        base, *(xcode3_build_dir + rel + python_resource_dir))
+    relPath2 = os.path.join(
+        base, *(xcode4_build_dir + rel + python_resource_dir))
+    baiPath = os.path.join(
+        base, *(xcode3_build_dir + bai + python_resource_dir))
+    baiPath2 = os.path.join(
+        base, *(xcode4_build_dir + bai + python_resource_dir))
+
+    lldbPath = None
+    if os.path.isfile(os.path.join(dbgPath, 'lldb.py')):
+        lldbPath = dbgPath
+    elif os.path.isfile(os.path.join(dbgPath2, 'lldb.py')):
+        lldbPath = dbgPath2
+    elif os.path.isfile(os.path.join(relPath, 'lldb.py')):
+        lldbPath = relPath
+    elif os.path.isfile(os.path.join(relPath2, 'lldb.py')):
+        lldbPath = relPath2
+    elif os.path.isfile(os.path.join(baiPath, 'lldb.py')):
+        lldbPath = baiPath
+    elif os.path.isfile(os.path.join(baiPath2, 'lldb.py')):
+        lldbPath = baiPath2
+
+    if not lldbPath:
+        print('This script requires lldb.py to be in either ' + dbgPath + ',', end=' ')
+        print(relPath + ', or ' + baiPath)
+        sys.exit(-1)
+
+    # This is to locate the lldb.py module.  Insert it right after sys.path[0].
+    sys.path[1:1] = [lldbPath]
+    # print "sys.path:", sys.path
+
+
+def run_command(ci, cmd, res, echo=True):
+    if echo:
+        print("run command:", cmd)
+    ci.HandleCommand(cmd, res)
+    if res.Succeeded():
+        if echo:
+            print("run_command output:", res.GetOutput())
+    else:
+        if echo:
+            print("run command failed!")
+            print("run_command error:", res.GetError())
+
+
+def do_lldb_disassembly(lldb_commands, exe, disassemble_options, num_symbols,
+                        symbols_to_disassemble,
+                        re_symbol_pattern,
+                        quiet_disassembly):
+    import lldb
+    import atexit
+    import re
+
+    # Create the debugger instance now.
+    dbg = lldb.SBDebugger.Create()
+    if not dbg:
+        raise Exception('Invalid debugger instance')
+
+    # Register an exit callback.
+    atexit.register(lambda: lldb.SBDebugger.Terminate())
+
+    # We want our debugger to be synchronous.
+    dbg.SetAsync(False)
+
+    # Get the command interpreter from the debugger.
+    ci = dbg.GetCommandInterpreter()
+    if not ci:
+        raise Exception('Could not get the command interpreter')
+
+    # And the associated result object.
+    res = lldb.SBCommandReturnObject()
+
+    # See if there any extra command(s) to execute before we issue the file
+    # command.
+    for cmd in lldb_commands:
+        run_command(ci, cmd, res, not quiet_disassembly)
+
+    # Now issue the file command.
+    run_command(ci, 'file %s' % exe, res, not quiet_disassembly)
+
+    # Create a target.
+    #target = dbg.CreateTarget(exe)
+    target = dbg.GetSelectedTarget()
+    stream = lldb.SBStream()
+
+    def IsCodeType(symbol):
+        """Check whether an SBSymbol represents code."""
+        return symbol.GetType() == lldb.eSymbolTypeCode
+
+    # Define a generator for the symbols to disassemble.
+    def symbol_iter(num, symbols, re_symbol_pattern, target, verbose):
+        # If we specify the symbols to disassemble, ignore symbol table dump.
+        if symbols:
+            for i in range(len(symbols)):
+                if verbose:
+                    print("symbol:", symbols[i])
+                yield symbols[i]
+        else:
+            limited = True if num != -1 else False
+            if limited:
+                count = 0
+            if re_symbol_pattern:
+                pattern = re.compile(re_symbol_pattern)
+            stream = lldb.SBStream()
+            for m in target.module_iter():
+                if verbose:
+                    print("module:", m)
+                for s in m:
+                    if limited and count >= num:
+                        return
+                    # If a regexp symbol pattern is supplied, consult it.
+                    if re_symbol_pattern:
+                        # If the pattern does not match, look for the next
+                        # symbol.
+                        if not pattern.match(s.GetName()):
+                            continue
+
+                    # If we come here, we're ready to disassemble the symbol.
+                    if verbose:
+                        print("symbol:", s.GetName())
+                    if IsCodeType(s):
+                        if limited:
+                            count = count + 1
+                            if verbose:
+                                print("returning symbol:", s.GetName())
+                        yield s.GetName()
+                    if verbose:
+                        print("start address:", s.GetStartAddress())
+                        print("end address:", s.GetEndAddress())
+                        s.GetDescription(stream)
+                        print("symbol description:", stream.GetData())
+                        stream.Clear()
+
+    # Disassembly time.
+    for symbol in symbol_iter(
+            num_symbols,
+            symbols_to_disassemble,
+            re_symbol_pattern,
+            target,
+            not quiet_disassembly):
+        cmd = "disassemble %s '%s'" % (disassemble_options, symbol)
+        run_command(ci, cmd, res, not quiet_disassembly)
+
+
+def main():
+    # This is to set up the Python path to include the pexpect-2.4 dir.
+    # Remember to update this when/if things change.
+    scriptPath = sys.path[0]
+    sys.path.append(
+        os.path.join(
+            scriptPath,
+            os.pardir,
+            os.pardir,
+            'test',
+            'pexpect-2.4'))
+
+    parser = OptionParser(usage="""\
+Run lldb to disassemble all the available functions for an executable image.
+
+Usage: %prog [options]
+""")
+    parser.add_option(
+        '-C',
+        '--lldb-command',
+        type='string',
+        action='append',
+        metavar='COMMAND',
+        default=[],
+        dest='lldb_commands',
+        help='Command(s) lldb executes after starting up (can be empty)')
+    parser.add_option(
+        '-e',
+        '--executable',
+        type='string',
+        action='store',
+        dest='executable',
+        help="""Mandatory: the executable to do disassembly on.""")
+    parser.add_option(
+        '-o',
+        '--options',
+        type='string',
+        action='store',
+        dest='disassemble_options',
+        help="""Mandatory: the options passed to lldb's 'disassemble' command.""")
+    parser.add_option(
+        '-q',
+        '--quiet-disassembly',
+        action='store_true',
+        default=False,
+        dest='quiet_disassembly',
+        help="""The symbol(s) to invoke lldb's 'disassemble' command on, if specified.""")
+    parser.add_option(
+        '-n',
+        '--num-symbols',
+        type='int',
+        action='store',
+        default=-1,
+        dest='num_symbols',
+        help="""The number of symbols to disassemble, if specified.""")
+    parser.add_option(
+        '-p',
+        '--symbol_pattern',
+        type='string',
+        action='store',
+        dest='re_symbol_pattern',
+        help="""The regular expression of symbols to invoke lldb's 'disassemble' command.""")
+    parser.add_option(
+        '-s',
+        '--symbol',
+        type='string',
+        action='append',
+        metavar='SYMBOL',
+        default=[],
+        dest='symbols_to_disassemble',
+        help="""The symbol(s) to invoke lldb's 'disassemble' command on, if specified.""")
+
+    opts, args = parser.parse_args()
+
+    lldb_commands = opts.lldb_commands
+
+    if not opts.executable or not opts.disassemble_options:
+        parser.print_help()
+        sys.exit(1)
+
+    executable = opts.executable
+    disassemble_options = opts.disassemble_options
+    quiet_disassembly = opts.quiet_disassembly
+    num_symbols = opts.num_symbols
+    symbols_to_disassemble = opts.symbols_to_disassemble
+    re_symbol_pattern = opts.re_symbol_pattern
+
+    # We have parsed the options.
+    if not quiet_disassembly:
+        print("lldb commands:", lldb_commands)
+        print("executable:", executable)
+        print("disassemble options:", disassemble_options)
+        print("quiet disassembly output:", quiet_disassembly)
+        print("num of symbols to disassemble:", num_symbols)
+        print("symbols to disassemble:", symbols_to_disassemble)
+        print("regular expression of symbols to disassemble:", re_symbol_pattern)
+
+    setupSysPath()
+    do_lldb_disassembly(lldb_commands, executable, disassemble_options,
+                        num_symbols,
+                        symbols_to_disassemble,
+                        re_symbol_pattern,
+                        quiet_disassembly)
+
+if __name__ == '__main__':
+    main()

Propchange: lldb/trunk/utils/test/lldb-disasm.py
------------------------------------------------------------------------------
    svn:executable = *

Added: lldb/trunk/utils/test/llvm-mc-shell.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/test/llvm-mc-shell.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/test/llvm-mc-shell.py (added)
+++ lldb/trunk/utils/test/llvm-mc-shell.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+
+"""
+Run llvm-mc interactively.
+
+"""
+
+from __future__ import print_function
+
+import os
+import sys
+from optparse import OptionParser
+
+
+def is_exe(fpath):
+    """Check whether fpath is an executable."""
+    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+
+
+def which(program):
+    """Find the full path to a program, or return None."""
+    fpath, fname = os.path.split(program)
+    if fpath:
+        if is_exe(program):
+            return program
+    else:
+        for path in os.environ["PATH"].split(os.pathsep):
+            exe_file = os.path.join(path, program)
+            if is_exe(exe_file):
+                return exe_file
+    return None
+
+
+def llvm_mc_loop(mc, mc_options):
+    contents = []
+    fname = 'mc-input.txt'
+    sys.stdout.write(
+        "Enter your input to llvm-mc.  A line starting with 'END' terminates the current batch of input.\n")
+    sys.stdout.write("Enter 'quit' or Ctrl-D to quit the program.\n")
+    while True:
+        sys.stdout.write("> ")
+        next = sys.stdin.readline()
+        # EOF => terminate this llvm-mc shell
+        if not next or next.startswith('quit'):
+            sys.stdout.write('\n')
+            sys.exit(0)
+        # 'END' => send the current batch of input to llvm-mc
+        if next.startswith('END'):
+            # Write contents to our file and clear the contents.
+            with open(fname, 'w') as f:
+                f.writelines(contents)
+                # Clear the list: replace all items with an empty list.
+                contents[:] = []
+
+            # Invoke llvm-mc with our newly created file.
+            mc_cmd = '%s %s %s' % (mc, mc_options, fname)
+            sys.stdout.write("Executing command: %s\n" % mc_cmd)
+            os.system(mc_cmd)
+        else:
+            # Keep accumulating our input.
+            contents.append(next)
+
+
+def main():
+    # This is to set up the Python path to include the pexpect-2.4 dir.
+    # Remember to update this when/if things change.
+    scriptPath = sys.path[0]
+    sys.path.append(
+        os.path.join(
+            scriptPath,
+            os.pardir,
+            os.pardir,
+            'test',
+            'pexpect-2.4'))
+
+    parser = OptionParser(usage="""\
+Do llvm-mc interactively within a shell-like environment.  A batch of input is
+submitted to llvm-mc to execute whenever you terminate the current batch by
+inputing a line which starts with 'END'.  Quit the program by either 'quit' or
+Ctrl-D.
+
+Usage: %prog [options]
+""")
+    parser.add_option('-m', '--llvm-mc',
+                      type='string', action='store',
+                      dest='llvm_mc',
+                      help="""The llvm-mc executable full path, if specified.
+                      Otherwise, it must be present in your PATH environment.""")
+
+    parser.add_option(
+        '-o',
+        '--options',
+        type='string',
+        action='store',
+        dest='llvm_mc_options',
+        help="""The options passed to 'llvm-mc' command if specified.""")
+
+    opts, args = parser.parse_args()
+
+    llvm_mc = opts.llvm_mc if opts.llvm_mc else which('llvm-mc')
+    if not llvm_mc:
+        parser.print_help()
+        sys.exit(1)
+
+    # This is optional.  For example:
+    # --options='-disassemble -triple=arm-apple-darwin -debug-only=arm-disassembler'
+    llvm_mc_options = opts.llvm_mc_options
+
+    # We have parsed the options.
+    print("llvm-mc:", llvm_mc)
+    print("llvm-mc options:", llvm_mc_options)
+
+    llvm_mc_loop(llvm_mc, llvm_mc_options)
+
+if __name__ == '__main__':
+    main()

Propchange: lldb/trunk/utils/test/llvm-mc-shell.py
------------------------------------------------------------------------------
    svn:executable = *

Added: lldb/trunk/utils/test/main.c
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/test/main.c?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/test/main.c (added)
+++ lldb/trunk/utils/test/main.c Tue Jul 23 10:47:08 2019
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, const char *argv[]) {
+  int *null_ptr = 0;
+  printf("Hello, fault!\n");
+  u_int32_t val = (arc4random() & 0x0f);
+  printf("val=%u\n", val);
+  if (val == 0x07) // Lucky 7 :-)
+    printf("Now segfault %d\n", *null_ptr);
+  else
+    printf("Better luck next time!\n");
+}

Added: lldb/trunk/utils/test/ras.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/test/ras.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/test/ras.py (added)
+++ lldb/trunk/utils/test/ras.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,182 @@
+#!/usr/bin/env python
+
+"""
+Run the test suite and send the result as an email message.
+
+The code for sending of the directory is copied from
+http://docs.python.org/library/email-examples.html.
+"""
+
+from __future__ import print_function
+
+import os
+import sys
+import shutil
+import smtplib
+# For guessing MIME type based on file name extension
+import mimetypes
+
+from optparse import OptionParser
+
+from email import encoders
+from email.message import Message
+from email.mime.audio import MIMEAudio
+from email.mime.base import MIMEBase
+from email.mime.image import MIMEImage
+from email.mime.multipart import MIMEMultipart
+from email.mime.text import MIMEText
+
+
+def runTestsuite(testDir, sessDir, envs=None):
+    """Run the testsuite and return a (summary, output) tuple."""
+    os.chdir(testDir)
+
+    for env in envs:
+        list = env.split('=')
+        var = list[0].strip()
+        val = list[1].strip()
+        print(var + "=" + val)
+        os.environ[var] = val
+
+    import shlex
+    import subprocess
+
+    command_line = "./dotest.py -w -s %s" % sessDir
+    # Apply correct tokenization for subprocess.Popen().
+    args = shlex.split(command_line)
+
+    # Use subprocess module to spawn a new process.
+    process = subprocess.Popen(args,
+                               stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    # Wait for subprocess to terminate.
+    stdout, stderr = process.communicate()
+
+    # This will be used as the subject line of our email about this test.
+    cmd = "%s %s" % (' '.join(envs) if envs else "", command_line)
+
+    return (cmd, stderr)
+
+
+COMMASPACE = ', '
+
+
+def main():
+    parser = OptionParser(usage="""\
+Run lldb test suite and send the results as a MIME message.
+
+Usage: %prog [options]
+
+Unless the -o option is given, the email is sent by forwarding to the specified
+SMTP server, which then does the normal delivery process.
+""")
+    parser.add_option('-d', '--directory',
+                      type='string', action='store',
+                      dest='testDir',
+                      help="""The LLDB test directory directly under the top dir.
+                      Otherwise use the current directory.""")
+    #
+    # This is similar to TestBase.getRunSpec(self) from lldbtest.py.
+    #
+    parser.add_option('-e', '--environment',
+                      type='string', action='append', metavar='ENVIRONMENT',
+                      default=[], dest='environments',
+                      help="""The environment setting as prefix to the test driver.
+                      Example: -e 'CC=clang' -e 'ARCH=x86_64'""")
+    parser.add_option('-m', '--mailserver',
+                      type='string', action='store', metavar='MAILSERVER',
+                      dest='mailserver',
+                      help="""The outgoing SMTP server.""")
+    parser.add_option('-o', '--output',
+                      type='string', action='store', metavar='FILE',
+                      help="""Print the composed message to FILE instead of
+                      sending the message to the SMTP server.""")
+    parser.add_option('-s', '--sender',
+                      type='string', action='store', metavar='SENDER',
+                      help='The value of the From: header (required)')
+    parser.add_option('-r', '--recipient',
+                      type='string', action='append', metavar='RECIPIENT',
+                      default=[], dest='recipients',
+                      help='A To: header value (at least one required)')
+    opts, args = parser.parse_args()
+    if not opts.sender or not opts.recipients:
+        parser.print_help()
+        sys.exit(1)
+    testDir = opts.testDir
+    if not testDir:
+        testDir = '.'
+
+    sessDir = 'tmp-lldb-session'
+    if os.path.exists(sessDir):
+        shutil.rmtree(sessDir)
+    # print "environments:", opts.environments
+    summary, output = runTestsuite(testDir, sessDir, opts.environments)
+
+    # Create the enclosing (outer) message
+    outer = MIMEMultipart()
+    outer['Subject'] = summary
+    outer['To'] = COMMASPACE.join(opts.recipients)
+    outer['From'] = opts.sender
+    outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'
+
+    # The sessDir contains all the session logs for failed/errored tests.
+    # Attach them all if it exists!
+
+    if not os.path.exists(sessDir):
+        outer.attach(MIMEText(output, 'plain'))
+    else:
+        outer.attach(
+            MIMEText(
+                "%s\n%s\n\n" %
+                (output, "Session logs of test failures/errors:"), 'plain'))
+
+    for filename in (os.listdir(sessDir) if os.path.exists(sessDir) else []):
+        path = os.path.join(sessDir, filename)
+        if not os.path.isfile(path):
+            continue
+        # Guess the content type based on the file's extension.  Encoding
+        # will be ignored, although we should check for simple things like
+        # gzip'd or compressed files.
+        ctype, encoding = mimetypes.guess_type(path)
+        if ctype is None or encoding is not None:
+            # No guess could be made, or the file is encoded (compressed), so
+            # use a generic bag-of-bits type.
+            ctype = 'application/octet-stream'
+        maintype, subtype = ctype.split('/', 1)
+        if maintype == 'text':
+            fp = open(path)
+            # Note: we should handle calculating the charset
+            msg = MIMEText(fp.read(), _subtype=subtype)
+            fp.close()
+        elif maintype == 'image':
+            fp = open(path, 'rb')
+            msg = MIMEImage(fp.read(), _subtype=subtype)
+            fp.close()
+        elif maintype == 'audio':
+            fp = open(path, 'rb')
+            msg = MIMEAudio(fp.read(), _subtype=subtype)
+            fp.close()
+        else:
+            fp = open(path, 'rb')
+            msg = MIMEBase(maintype, subtype)
+            msg.set_payload(fp.read())
+            fp.close()
+            # Encode the payload using Base64
+            encoders.encode_base64(msg)
+        # Set the filename parameter
+        msg.add_header('Content-Disposition', 'attachment', filename=filename)
+        outer.attach(msg)
+
+    # Now send or store the message
+    composed = outer.as_string()
+    if opts.output:
+        fp = open(opts.output, 'w')
+        fp.write(composed)
+        fp.close()
+    else:
+        s = smtplib.SMTP(opts.mailserver)
+        s.sendmail(opts.sender, opts.recipients, composed)
+        s.quit()
+
+
+if __name__ == '__main__':
+    main()

Propchange: lldb/trunk/utils/test/ras.py
------------------------------------------------------------------------------
    svn:executable = *

Added: lldb/trunk/utils/test/run-dis.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/test/run-dis.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/test/run-dis.py (added)
+++ lldb/trunk/utils/test/run-dis.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,144 @@
+#!/usr/bin/env python
+
+"""
+Run lldb disassembler on all the binaries specified by a combination of root dir
+and path pattern.
+"""
+
+from __future__ import print_function
+
+import os
+import sys
+import subprocess
+import re
+from optparse import OptionParser
+
+# The directory of this Python script as well as the lldb-disasm.py workhorse.
+scriptPath = None
+
+# The root directory for the SDK symbols.
+root_dir = None
+
+# The regular expression pattern to match the desired pathname to the binaries.
+path_pattern = None
+
+# And the re-compiled regular expression object.
+path_regexp = None
+
+# If specified, number of symbols to disassemble for each qualified binary.
+num_symbols = -1
+
+# Command template of the invocation of lldb disassembler.
+template = '%s/lldb-disasm.py -C "platform select remote-ios" -o "-n" -q -e %s -n %s'
+
+# Regular expression for detecting file output for Mach-o binary.
+mach_o = re.compile('\sMach-O.+binary')
+
+
+def isbinary(path):
+    file_output = subprocess.Popen(["file", path],
+                                   stdout=subprocess.PIPE).stdout.read()
+    return (mach_o.search(file_output) is not None)
+
+
+def walk_and_invoke(sdk_root, path_regexp, suffix, num_symbols):
+    """Look for matched file and invoke lldb disassembly on it."""
+    global scriptPath
+
+    for root, dirs, files in os.walk(sdk_root, topdown=False):
+        for name in files:
+            path = os.path.join(root, name)
+
+            # We're not interested in .h file.
+            if name.endswith(".h"):
+                continue
+            # Neither a symbolically linked file.
+            if os.path.islink(path):
+                continue
+
+            # We'll be pattern matching based on the path relative to the SDK
+            # root.
+            replaced_path = path.replace(root_dir, "", 1)
+            # Check regular expression match for the replaced path.
+            if not path_regexp.search(replaced_path):
+                continue
+            # If a suffix is specified, check it, too.
+            if suffix and not name.endswith(suffix):
+                continue
+            if not isbinary(path):
+                continue
+
+            command = template % (
+                scriptPath, path, num_symbols if num_symbols > 0 else 1000)
+            print("Running %s" % (command))
+            os.system(command)
+
+
+def main():
+    """Read the root dir and the path spec, invoke lldb-disasm.py on the file."""
+    global scriptPath
+    global root_dir
+    global path_pattern
+    global path_regexp
+    global num_symbols
+
+    scriptPath = sys.path[0]
+
+    parser = OptionParser(usage="""\
+Run lldb disassembler on all the binaries specified by a combination of root dir
+and path pattern.
+""")
+    parser.add_option(
+        '-r',
+        '--root-dir',
+        type='string',
+        action='store',
+        dest='root_dir',
+        help='Mandatory: the root directory for the SDK symbols.')
+    parser.add_option(
+        '-p',
+        '--path-pattern',
+        type='string',
+        action='store',
+        dest='path_pattern',
+        help='Mandatory: regular expression pattern for the desired binaries.')
+    parser.add_option('-s', '--suffix',
+                      type='string', action='store', default=None,
+                      dest='suffix',
+                      help='Specify the suffix of the binaries to look for.')
+    parser.add_option(
+        '-n',
+        '--num-symbols',
+        type='int',
+        action='store',
+        default=-1,
+        dest='num_symbols',
+        help="""The number of symbols to disassemble, if specified.""")
+
+    opts, args = parser.parse_args()
+    if not opts.root_dir or not opts.path_pattern:
+        parser.print_help()
+        sys.exit(1)
+
+    # Sanity check the root directory.
+    root_dir = opts.root_dir
+    root_dir = os.path.abspath(root_dir)
+    if not os.path.isdir(root_dir):
+        parser.print_help()
+        sys.exit(1)
+
+    path_pattern = opts.path_pattern
+    path_regexp = re.compile(path_pattern)
+    suffix = opts.suffix
+    num_symbols = opts.num_symbols
+
+    print("Root directory for SDK symbols:", root_dir)
+    print("Regular expression for the binaries:", path_pattern)
+    print("Suffix of the binaries to look for:", suffix)
+    print("num of symbols to disassemble:", num_symbols)
+
+    walk_and_invoke(root_dir, path_regexp, suffix, num_symbols)
+
+
+if __name__ == '__main__':
+    main()

Propchange: lldb/trunk/utils/test/run-dis.py
------------------------------------------------------------------------------
    svn:executable = *

Added: lldb/trunk/utils/test/run-until-faulted.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/test/run-until-faulted.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/test/run-until-faulted.py (added)
+++ lldb/trunk/utils/test/run-until-faulted.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+"""
+Run a program via lldb until it fails.
+The lldb executable is located via your PATH env variable, if not specified.
+"""
+
+from __future__ import print_function
+
+import os
+import sys
+from optparse import OptionParser
+
+
+def is_exe(fpath):
+    """Check whether fpath is an executable."""
+    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+
+
+def which(program):
+    """Find the full path to a program, or return None."""
+    fpath, fname = os.path.split(program)
+    if fpath:
+        if is_exe(program):
+            return program
+    else:
+        for path in os.environ["PATH"].split(os.pathsep):
+            exe_file = os.path.join(path, program)
+            if is_exe(exe_file):
+                return exe_file
+    return None
+
+
+def do_lldb_launch_loop(lldb_command, exe, exe_options):
+    import pexpect
+    import time
+
+    prompt = "\(lldb\) "
+    lldb = pexpect.spawn(lldb_command)
+    # Turn on logging for what lldb sends back.
+    lldb.logfile_read = sys.stdout
+    lldb.expect(prompt)
+
+    # Now issue the file command.
+    # print "sending 'file %s' command..." % exe
+    lldb.sendline('file %s' % exe)
+    lldb.expect(prompt)
+
+    # Loop until it faults....
+    count = 0
+    # while True:
+    #    count = count + 1
+    for i in range(100):
+        count = i
+        # print "sending 'process launch -- %s' command... (iteration: %d)" %
+        # (exe_options, count)
+        lldb.sendline('process launch -- %s' % exe_options)
+        index = lldb.expect(['Process .* exited with status',
+                             'Process .* stopped',
+                             pexpect.TIMEOUT])
+        if index == 0:
+            # We'll try again later.
+            time.sleep(3)
+        elif index == 1:
+            # Perfect, our process had stopped; break out of the loop.
+            break
+        elif index == 2:
+            # Something went wrong.
+            print("TIMEOUT occurred:", str(lldb))
+
+    # Give control of lldb shell to the user.
+    lldb.interact()
+
+
+def main():
+    # This is to set up the Python path to include the pexpect-2.4 dir.
+    # Remember to update this when/if things change.
+    scriptPath = sys.path[0]
+    sys.path.append(
+        os.path.join(
+            scriptPath,
+            os.pardir,
+            os.pardir,
+            'test',
+            'pexpect-2.4'))
+
+    parser = OptionParser(usage="""\
+%prog [options]
+Run a program via lldb until it fails.
+The lldb executable is located via your PATH env variable, if not specified.\
+""")
+    parser.add_option('-l', '--lldb-command',
+                      type='string', action='store', metavar='LLDB_COMMAND',
+                      default='lldb', dest='lldb_command',
+                      help='Full path to your lldb command')
+    parser.add_option(
+        '-e',
+        '--executable',
+        type='string',
+        action='store',
+        dest='exe',
+        help="""(Mandatory) The executable to launch via lldb.""")
+    parser.add_option(
+        '-o',
+        '--options',
+        type='string',
+        action='store',
+        default='',
+        dest='exe_options',
+        help="""The args/options passed to the launched program, if specified.""")
+
+    opts, args = parser.parse_args()
+
+    lldb_command = which(opts.lldb_command)
+
+    if not opts.exe:
+        parser.print_help()
+        sys.exit(1)
+    exe = opts.exe
+
+    exe_options = opts.exe_options
+
+    # We have parsed the options.
+    print("lldb command:", lldb_command)
+    print("executable:", exe)
+    print("executable options:", exe_options)
+
+    do_lldb_launch_loop(lldb_command, exe, exe_options)
+
+if __name__ == '__main__':
+    main()

Propchange: lldb/trunk/utils/test/run-until-faulted.py
------------------------------------------------------------------------------
    svn:executable = *

Added: lldb/trunk/utils/vim-lldb/README
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/vim-lldb/README?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/vim-lldb/README (added)
+++ lldb/trunk/utils/vim-lldb/README Tue Jul 23 10:47:08 2019
@@ -0,0 +1,59 @@
+
+=================
+LLDB Vim Frontend
+=================
+
+Prerequisites
+-------------
+
+This plugin is known to work with the following flavours of Vim:
+
+  * Linux (tested on Ubuntu 12.04/12.10):
+      * vim/gvim (from vim-gnome package version 7.3)
+
+  * Mac OS X (tested on Mountain Lion)
+      * Vim command-line (7.3 from Xcode)
+      * MacVim 7.3
+
+To install the plugin, ensure you have
+  * a working version of lldb on your path, or the environment variable LLDB
+    pointing to the lldb binary you would like to use.
+  * a python-enabled vim (check with ":python print 2")
+
+
+Installation
+------------
+
+1) Install the Vim pathogen plugin (it keeps installed plugins organized):
+
+    https://github.com/tpope/vim-pathogen
+
+  Or, for the impatient:
+
+mkdir -p ~/.vim/autoload ~/.vim/bundle; \
+curl -Sso ~/.vim/autoload/pathogen.vim \
+    https://raw.github.com/tpope/vim-pathogen/master/autoload/pathogen.vim
+
+2) Symlink (or copy) ~/.vim/bundle/vim-lldb to this directory:
+
+ln -sf <lldb-dir>/utils/vim-lldb ~/.vim/bundle/vim-lldb
+
+3) Update your help-tags. Start vim, do:
+
+    :Helptags
+
+4) Have fun!
+
+
+Usage/Getting Help
+------------------
+All LLDB commands (with tab-completion) can be accessed in Vim's
+command mode. Try it out by typing:
+
+:L<tab>
+
+There are several sources of help available:
+
+:help lldb                  -- Documentation for this plugin
+:Lhelp                      -- LLDB's built-in help system (i.e lldb 'help' command)
+:Lscript help (lldb)        -- Complete LLDB Python API reference

Added: lldb/trunk/utils/vim-lldb/doc/lldb.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/vim-lldb/doc/lldb.txt?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/vim-lldb/doc/lldb.txt (added)
+++ lldb/trunk/utils/vim-lldb/doc/lldb.txt Tue Jul 23 10:47:08 2019
@@ -0,0 +1,115 @@
+*lldb.txt* A plugin that enables debugging from your favourite editor
+
+Author:   Daniel Malea <daniel.malea at intel.com>
+License:  Same terms as Vim itself (see |license|)
+
+INTRODUCTION                                    *lldb*
+
+Installing this plugin enables a set of commands in Vim to control the
+LLDB (http://lldb.llvm.org) debugger.
+
+COMMANDS                                        *lldb-commands*
+
+The LLDB command interpreter is exposed to Vim's command mode using the
+':L' prefix. Tab-completion is available and will cycle through commands.
+Some commands have modified behaviour in Vim; for example, :Lbreakpoint
+with no arguments will set a breakpoint at the current cursor, rather than
+printing the standard help information for the LLDB command 'breakpoint'.
+
+                                                *lldb-windows*
+
+In addition to the standard commands available under the LLDB interpreter,
+there are also commands to display or hide informational debugger panes.
+
+Windows can be shown or hidden using the ':Lhide <name>' or ':Lshow <name>'
+commands.
+                                                *lldb-:Lhide*
+:Lhide [windowname]     Hide informational debugger pane named 'windowname'. 
+
+                                                *lldb-:Lshow*
+:Lshow [windowname]     Show informational debugger pane named 'windowname'. 
+
+Possible window name arguments to the Lhide and Lshow commands include:
+
+    * backtrace
+    * breakpoints
+    * disassembly
+    * locals
+    * registers
+    * threads
+                                                *lldb-:Lattach*
+:Lattach <process-name> Attach to a process by name.
+
+                                                *lldb-:Ldetach*
+:Ldetach                Detach from the current process.
+
+                                                *lldb-:Ltarget*
+:Ltarget [[create] executable]
+                        Create a target with the specified executable. If
+                        run with a single argument, that argument is assumed
+                        to be a path to the executable to be debugged.
+                        Otherwise, all arguments are passed into LLDB's command
+                        interpreter.
+
+                                                *lldb-:Lstart*
+:Lstart                 Create a process by executing the current target
+                        and wait for LLDB to attach.
+
+                                                *lldb-:Lrun*
+:Lrun                   Create a process by executing the current target
+                        without waiting for LLDB to attach.
+
+                                                *lldb-:Lcontinue*
+:Lcontinue              Continue execution of the process until the next
+                        breakpoint is hit or the process exits.
+
+                                                *lldb-:Lthread*
+:Lthread <args>         Passes through to LLDB. See :Lhelp thread. 
+
+                                                *lldb-:Lstep*
+:Lstep                  Step into the current function call.
+
+                                                *lldb-:Lstepin*
+:Lstepin                Step into the current function call.
+
+                                                *lldb-:Lstepinst*
+:Lstepinst              Step one instruction.
+
+                                                *lldb-:Lstepinstover*
+:Lstepinstover          Step one instruction, but skip over jump or call
+                        instructions.
+
+                                                *lldb-:Lnext*
+:Lnext                  Step to the next line.
+
+                                                *lldb-:Lfinish*
+:Lfinish                Step out of the current function.
+
+                                                *lldb-:Lbreakpoint*
+:Lbreakpoint [args]     When arguments are provided, the lldb breakpoint
+                        command is invoked. If no arguments are provided,
+                        a breakpoint at the location under the cursor.
+
+                                                *lldb-:Lprint*
+                                                *lldb-:Lpo*
+                                                *lldb-:LpO*
+:Lprint <expr>          Aliases to the lldb print and po commands. Cursor
+:Lpo <expr>             word (cursor WORD for LpO) will be used when 
+:LpO <expr>             expression omitted.
+
+MAPPINGS                                        *lldb-mappings*
+
+On Mac OS X (under MacVim) , the following key mappings are available:
+
+<Command-B>             Insert a breakpoint at the line under cursor
+
+
+ABOUT                                           *lldb-about*
+
+Grab the latest version of this plugin (and LLDB sources) with:
+  git clone https://github.com/llvm/llvm-project.git
+
+File any bugs at:
+  http://llvm.org/bugs/enter_bug.cgi?product=lldb
+
+ vim:tw=78:et:ft=help:norl:

Added: lldb/trunk/utils/vim-lldb/plugin/lldb.vim
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/vim-lldb/plugin/lldb.vim?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/vim-lldb/plugin/lldb.vim (added)
+++ lldb/trunk/utils/vim-lldb/plugin/lldb.vim Tue Jul 23 10:47:08 2019
@@ -0,0 +1,151 @@
+
+" Vim script glue code for LLDB integration
+
+function! s:FindPythonScriptDir()
+  for dir in pathogen#split(&runtimepath)
+    let searchstr = "python-vim-lldb"
+    let candidates = pathogen#glob_directories(dir . "/" . searchstr)
+    if len(candidates) > 0
+      return candidates[0]
+    endif
+  endfor
+  return
+endfunction()
+
+function! s:InitLldbPlugin()
+  if has('python') == 0
+    call confirm('ERROR: This Vim installation does not have python support. lldb.vim will not work.')
+    return
+  endif
+  
+  " Key-Bindings
+  " FIXME: choose sensible keybindings for:
+  " - process: start, interrupt, continue, continue-to-cursor
+  " - step: instruction, in, over, out
+  "
+  if has('gui_macvim')
+    " Apple-B toggles breakpoint on cursor
+    map <D-B>     :Lbreakpoint<CR>
+  endif
+
+  "
+  " Setup the python interpreter path
+  "
+  let vim_lldb_pydir = s:FindPythonScriptDir()
+  execute 'python import sys; sys.path.append("' . vim_lldb_pydir . '")'
+
+  "
+  " Register :L<Command>
+  " The LLDB CommandInterpreter provides tab-completion in Vim's command mode.
+  " FIXME: this list of commands, at least partially should be auto-generated
+  "
+
+  " Window show/hide commands
+  command -complete=custom,s:CompleteWindow -nargs=1 Lhide               python ctrl.doHide('<args>')
+  command -complete=custom,s:CompleteWindow -nargs=0 Lshow               python ctrl.doShow('<args>')
+ 
+  " Launching convenience commands (no autocompletion)
+  command -nargs=* Lstart                                                python ctrl.doLaunch(True,  '<args>')
+  command -nargs=* Lrun                                                  python ctrl.doLaunch(False, '<args>')
+  command -nargs=1 Lattach                                               python ctrl.doAttach('<args>')
+  command -nargs=0 Ldetach                                               python ctrl.doDetach()
+
+  " Regexp-commands: because vim's command mode does not support '_' or '-'
+  " characters in command names, we omit them when creating the :L<cmd>
+  " equivalents.
+  command -complete=custom,s:CompleteCommand -nargs=* Lregexpattach      python ctrl.doCommand('_regexp-attach', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lregexpbreak       python ctrl.doCommand('_regexp-break', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lregexpbt          python ctrl.doCommand('_regexp-bt', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lregexpdown        python ctrl.doCommand('_regexp-down', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lregexptbreak      python ctrl.doCommand('_regexp-tbreak', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lregexpdisplay     python ctrl.doCommand('_regexp-display', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lregexpundisplay   python ctrl.doCommand('_regexp-undisplay', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lregexpup          python ctrl.doCommand('_regexp-up', '<args>')
+
+  command -complete=custom,s:CompleteCommand -nargs=* Lapropos           python ctrl.doCommand('apropos', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lbacktrace         python ctrl.doCommand('bt', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lbreakpoint        python ctrl.doBreakpoint('<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lcommand           python ctrl.doCommand('command', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Ldisassemble       python ctrl.doCommand('disassemble', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lexpression        python ctrl.doCommand('expression', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lhelp              python ctrl.doCommand('help', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Llog               python ctrl.doCommand('log', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lplatform          python ctrl.doCommand('platform','<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lplugin            python ctrl.doCommand('plugin', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lprocess           python ctrl.doProcess('<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lregister          python ctrl.doCommand('register', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lscript            python ctrl.doCommand('script', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lsettings          python ctrl.doCommand('settings','<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lsource            python ctrl.doCommand('source', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Ltype              python ctrl.doCommand('type', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lversion           python ctrl.doCommand('version', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=* Lwatchpoint        python ctrl.doCommand('watchpoint', '<args>')
+ 
+  " Convenience (shortcut) LLDB commands
+  command -complete=custom,s:CompleteCommand -nargs=* Lprint             python ctrl.doCommand('print', vim.eval("s:CursorWord('<args>')"))
+  command -complete=custom,s:CompleteCommand -nargs=* Lpo                python ctrl.doCommand('po', vim.eval("s:CursorWord('<args>')"))
+  command -complete=custom,s:CompleteCommand -nargs=* LpO                python ctrl.doCommand('po', vim.eval("s:CursorWORD('<args>')"))
+  command -complete=custom,s:CompleteCommand -nargs=* Lbt                python ctrl.doCommand('bt', '<args>')
+
+  " Frame/Thread-Selection (commands that also do an Uupdate but do not
+  " generate events in LLDB)
+  command -complete=custom,s:CompleteCommand -nargs=* Lframe             python ctrl.doSelect('frame', '<args>')
+  command -complete=custom,s:CompleteCommand -nargs=? Lup                python ctrl.doCommand('up', '<args>',     print_on_success=False, goto_file=True)
+  command -complete=custom,s:CompleteCommand -nargs=? Ldown              python ctrl.doCommand('down', '<args>', print_on_success=False, goto_file=True)
+  command -complete=custom,s:CompleteCommand -nargs=* Lthread            python ctrl.doSelect('thread', '<args>')
+
+  command -complete=custom,s:CompleteCommand -nargs=* Ltarget            python ctrl.doTarget('<args>')
+
+  " Continue
+  command -complete=custom,s:CompleteCommand -nargs=* Lcontinue          python ctrl.doContinue()
+
+  " Thread-Stepping (no autocompletion)
+  command -nargs=0 Lstepinst                                             python ctrl.doStep(StepType.INSTRUCTION)
+  command -nargs=0 Lstepinstover                                         python ctrl.doStep(StepType.INSTRUCTION_OVER)
+  command -nargs=0 Lstepin                                               python ctrl.doStep(StepType.INTO)
+  command -nargs=0 Lstep                                                 python ctrl.doStep(StepType.INTO)
+  command -nargs=0 Lnext                                                 python ctrl.doStep(StepType.OVER)
+  command -nargs=0 Lfinish                                               python ctrl.doStep(StepType.OUT)
+
+  " hack: service the LLDB event-queue when the cursor moves
+  " FIXME: some threaded solution would be better...but it
+  "        would have to be designed carefully because Vim's APIs are non threadsafe;
+  "        use of the vim module **MUST** be restricted to the main thread.
+  command -nargs=0 Lrefresh python ctrl.doRefresh()
+  autocmd CursorMoved * :Lrefresh
+  autocmd CursorHold  * :Lrefresh
+  autocmd VimLeavePre * python ctrl.doExit()
+
+  execute 'pyfile ' . vim_lldb_pydir . '/plugin.py'
+endfunction()
+
+function! s:CompleteCommand(A, L, P)
+  python << EOF
+a = vim.eval("a:A")
+l = vim.eval("a:L")
+p = vim.eval("a:P")
+returnCompleteCommand(a, l, p)
+EOF
+endfunction()
+
+function! s:CompleteWindow(A, L, P)
+  python << EOF
+a = vim.eval("a:A")
+l = vim.eval("a:L")
+p = vim.eval("a:P")
+returnCompleteWindow(a, l, p)
+EOF
+endfunction()
+
+" Returns cword if search term is empty
+function! s:CursorWord(term) 
+  return empty(a:term) ? expand('<cword>') : a:term 
+endfunction()
+
+" Returns cleaned cWORD if search term is empty
+function! s:CursorWORD(term) 
+  " Will strip all non-alphabetic characters from both sides
+  return empty(a:term) ?  substitute(expand('<cWORD>'), '^\A*\(.\{-}\)\A*$', '\1', '') : a:term 
+endfunction()
+
+call s:InitLldbPlugin()

Added: lldb/trunk/utils/vim-lldb/python-vim-lldb/import_lldb.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/vim-lldb/python-vim-lldb/import_lldb.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/vim-lldb/python-vim-lldb/import_lldb.py (added)
+++ lldb/trunk/utils/vim-lldb/python-vim-lldb/import_lldb.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,71 @@
+
+# Locate and load the lldb python module
+
+import os
+import sys
+
+
+def import_lldb():
+    """ Find and import the lldb modules. This function tries to find the lldb module by:
+        1. Simply by doing "import lldb" in case the system python installation is aware of lldb. If that fails,
+        2. Executes the lldb executable pointed to by the LLDB environment variable (or if unset, the first lldb
+           on PATH") with the -P flag to determine the PYTHONPATH to set. If the lldb executable returns a valid
+           path, it is added to sys.path and the import is attempted again. If that fails, 3. On Mac OS X the
+           default Xcode 4.5 installation path.
+    """
+
+    # Try simple 'import lldb', in case of a system-wide install or a
+    # pre-configured PYTHONPATH
+    try:
+        import lldb
+        return True
+    except ImportError:
+        pass
+
+    # Allow overriding default path to lldb executable with the LLDB
+    # environment variable
+    lldb_executable = 'lldb'
+    if 'LLDB' in os.environ and os.path.exists(os.environ['LLDB']):
+        lldb_executable = os.environ['LLDB']
+
+    # Try using builtin module location support ('lldb -P')
+    from subprocess import check_output, CalledProcessError
+    try:
+        with open(os.devnull, 'w') as fnull:
+            lldb_minus_p_path = check_output(
+                "%s -P" %
+                lldb_executable,
+                shell=True,
+                stderr=fnull).strip()
+        if not os.path.exists(lldb_minus_p_path):
+            # lldb -P returned invalid path, probably too old
+            pass
+        else:
+            sys.path.append(lldb_minus_p_path)
+            import lldb
+            return True
+    except CalledProcessError:
+        # Cannot run 'lldb -P' to determine location of lldb python module
+        pass
+    except ImportError:
+        # Unable to import lldb module from path returned by `lldb -P`
+        pass
+
+    # On Mac OS X, use the try the default path to XCode lldb module
+    if "darwin" in sys.platform:
+        xcode_python_path = "/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/Current/Resources/Python/"
+        sys.path.append(xcode_python_path)
+        try:
+            import lldb
+            return True
+        except ImportError:
+            # Unable to import lldb module from default Xcode python path
+            pass
+
+    return False
+
+if not import_lldb():
+    import vim
+    vim.command(
+        'redraw | echo "%s"' %
+        " Error loading lldb module; vim-lldb will be disabled. Check LLDB installation or set LLDB environment variable.")

Added: lldb/trunk/utils/vim-lldb/python-vim-lldb/lldb_controller.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/vim-lldb/python-vim-lldb/lldb_controller.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/vim-lldb/python-vim-lldb/lldb_controller.py (added)
+++ lldb/trunk/utils/vim-lldb/python-vim-lldb/lldb_controller.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,415 @@
+
+#
+# This file defines the layer that talks to lldb
+#
+
+from __future__ import print_function
+
+import os
+import re
+import sys
+import lldb
+import vim
+from vim_ui import UI
+
+# =================================================
+# Convert some enum value to its string counterpart
+# =================================================
+
+# Shamelessly copy/pasted from lldbutil.py in the test suite
+
+
+def state_type_to_str(enum):
+    """Returns the stateType string given an enum."""
+    if enum == lldb.eStateInvalid:
+        return "invalid"
+    elif enum == lldb.eStateUnloaded:
+        return "unloaded"
+    elif enum == lldb.eStateConnected:
+        return "connected"
+    elif enum == lldb.eStateAttaching:
+        return "attaching"
+    elif enum == lldb.eStateLaunching:
+        return "launching"
+    elif enum == lldb.eStateStopped:
+        return "stopped"
+    elif enum == lldb.eStateRunning:
+        return "running"
+    elif enum == lldb.eStateStepping:
+        return "stepping"
+    elif enum == lldb.eStateCrashed:
+        return "crashed"
+    elif enum == lldb.eStateDetached:
+        return "detached"
+    elif enum == lldb.eStateExited:
+        return "exited"
+    elif enum == lldb.eStateSuspended:
+        return "suspended"
+    else:
+        raise Exception("Unknown StateType enum")
+
+
+class StepType:
+    INSTRUCTION = 1
+    INSTRUCTION_OVER = 2
+    INTO = 3
+    OVER = 4
+    OUT = 5
+
+
+class LLDBController(object):
+    """ Handles Vim and LLDB events such as commands and lldb events. """
+
+    # Timeouts (sec) for waiting on new events. Because vim is not multi-threaded, we are restricted to
+    # servicing LLDB events from the main UI thread. Usually, we only process events that are already
+    # sitting on the queue. But in some situations (when we are expecting an event as a result of some
+    # user interaction) we want to wait for it. The constants below set these wait period in which the
+    # Vim UI is "blocked". Lower numbers will make Vim more responsive, but LLDB will be delayed and higher
+    # numbers will mean that LLDB events are processed faster, but the Vim UI may appear less responsive at
+    # times.
+    eventDelayStep = 2
+    eventDelayLaunch = 1
+    eventDelayContinue = 1
+
+    def __init__(self):
+        """ Creates the LLDB SBDebugger object and initializes the UI class. """
+        self.target = None
+        self.process = None
+        self.load_dependent_modules = True
+
+        self.dbg = lldb.SBDebugger.Create()
+        self.commandInterpreter = self.dbg.GetCommandInterpreter()
+
+        self.ui = UI()
+
+    def completeCommand(self, a, l, p):
+        """ Returns a list of viable completions for command a with length l and cursor at p  """
+
+        assert l[0] == 'L'
+        # Remove first 'L' character that all commands start with
+        l = l[1:]
+
+        # Adjust length as string has 1 less character
+        p = int(p) - 1
+
+        result = lldb.SBStringList()
+        num = self.commandInterpreter.HandleCompletion(l, p, 1, -1, result)
+
+        if num == -1:
+            # FIXME: insert completion character... what's a completion
+            # character?
+            pass
+        elif num == -2:
+            # FIXME: replace line with result.GetStringAtIndex(0)
+            pass
+
+        if result.GetSize() > 0:
+            results = [_f for _f in [result.GetStringAtIndex(x)
+                                    for x in range(result.GetSize())] if _f]
+            return results
+        else:
+            return []
+
+    def doStep(self, stepType):
+        """ Perform a step command and block the UI for eventDelayStep seconds in order to process
+            events on lldb's event queue.
+            FIXME: if the step does not complete in eventDelayStep seconds, we relinquish control to
+                   the main thread to avoid the appearance of a "hang". If this happens, the UI will
+                   update whenever; usually when the user moves the cursor. This is somewhat annoying.
+        """
+        if not self.process:
+            sys.stderr.write("No process to step")
+            return
+
+        t = self.process.GetSelectedThread()
+        if stepType == StepType.INSTRUCTION:
+            t.StepInstruction(False)
+        if stepType == StepType.INSTRUCTION_OVER:
+            t.StepInstruction(True)
+        elif stepType == StepType.INTO:
+            t.StepInto()
+        elif stepType == StepType.OVER:
+            t.StepOver()
+        elif stepType == StepType.OUT:
+            t.StepOut()
+
+        self.processPendingEvents(self.eventDelayStep, True)
+
+    def doSelect(self, command, args):
+        """ Like doCommand, but suppress output when "select" is the first argument."""
+        a = args.split(' ')
+        return self.doCommand(command, args, "select" != a[0], True)
+
+    def doProcess(self, args):
+        """ Handle 'process' command. If 'launch' is requested, use doLaunch() instead
+            of the command interpreter to start the inferior process.
+        """
+        a = args.split(' ')
+        if len(args) == 0 or (len(a) > 0 and a[0] != 'launch'):
+            self.doCommand("process", args)
+            #self.ui.update(self.target, "", self)
+        else:
+            self.doLaunch('-s' not in args, "")
+
+    def doAttach(self, process_name):
+        """ Handle process attach.  """
+        error = lldb.SBError()
+
+        self.processListener = lldb.SBListener("process_event_listener")
+        self.target = self.dbg.CreateTarget('')
+        self.process = self.target.AttachToProcessWithName(
+            self.processListener, process_name, False, error)
+        if not error.Success():
+            sys.stderr.write("Error during attach: " + str(error))
+            return
+
+        self.ui.activate()
+        self.pid = self.process.GetProcessID()
+
+        print("Attached to %s (pid=%d)" % (process_name, self.pid))
+
+    def doDetach(self):
+        if self.process is not None and self.process.IsValid():
+            pid = self.process.GetProcessID()
+            state = state_type_to_str(self.process.GetState())
+            self.process.Detach()
+            self.processPendingEvents(self.eventDelayLaunch)
+
+    def doLaunch(self, stop_at_entry, args):
+        """ Handle process launch.  """
+        error = lldb.SBError()
+
+        fs = self.target.GetExecutable()
+        exe = os.path.join(fs.GetDirectory(), fs.GetFilename())
+        if self.process is not None and self.process.IsValid():
+            pid = self.process.GetProcessID()
+            state = state_type_to_str(self.process.GetState())
+            self.process.Destroy()
+
+        launchInfo = lldb.SBLaunchInfo(args.split(' '))
+        self.process = self.target.Launch(launchInfo, error)
+        if not error.Success():
+            sys.stderr.write("Error during launch: " + str(error))
+            return
+
+        # launch succeeded, store pid and add some event listeners
+        self.pid = self.process.GetProcessID()
+        self.processListener = lldb.SBListener("process_event_listener")
+        self.process.GetBroadcaster().AddListener(
+            self.processListener, lldb.SBProcess.eBroadcastBitStateChanged)
+
+        print("Launched %s %s (pid=%d)" % (exe, args, self.pid))
+
+        if not stop_at_entry:
+            self.doContinue()
+        else:
+            self.processPendingEvents(self.eventDelayLaunch)
+
+    def doTarget(self, args):
+        """ Pass target command to interpreter, except if argument is not one of the valid options, or
+            is create, in which case try to create a target with the argument as the executable. For example:
+              target list        ==> handled by interpreter
+              target create blah ==> custom creation of target 'blah'
+              target blah        ==> also creates target blah
+        """
+        target_args = [  # "create",
+            "delete",
+            "list",
+            "modules",
+            "select",
+            "stop-hook",
+            "symbols",
+            "variable"]
+
+        a = args.split(' ')
+        if len(args) == 0 or (len(a) > 0 and a[0] in target_args):
+            self.doCommand("target", args)
+            return
+        elif len(a) > 1 and a[0] == "create":
+            exe = a[1]
+        elif len(a) == 1 and a[0] not in target_args:
+            exe = a[0]
+
+        err = lldb.SBError()
+        self.target = self.dbg.CreateTarget(
+            exe, None, None, self.load_dependent_modules, err)
+        if not self.target:
+            sys.stderr.write(
+                "Error creating target %s. %s" %
+                (str(exe), str(err)))
+            return
+
+        self.ui.activate()
+        self.ui.update(self.target, "created target %s" % str(exe), self)
+
+    def doContinue(self):
+        """ Handle 'contiue' command.
+            FIXME: switch to doCommand("continue", ...) to handle -i ignore-count param.
+        """
+        if not self.process or not self.process.IsValid():
+            sys.stderr.write("No process to continue")
+            return
+
+        self.process.Continue()
+        self.processPendingEvents(self.eventDelayContinue)
+
+    def doBreakpoint(self, args):
+        """ Handle breakpoint command with command interpreter, except if the user calls
+            "breakpoint" with no other args, in which case add a breakpoint at the line
+            under the cursor.
+        """
+        a = args.split(' ')
+        if len(args) == 0:
+            show_output = False
+
+            # User called us with no args, so toggle the bp under cursor
+            cw = vim.current.window
+            cb = vim.current.buffer
+            name = cb.name
+            line = cw.cursor[0]
+
+            # Since the UI is responsbile for placing signs at bp locations, we have to
+            # ask it if there already is one or more breakpoints at (file,
+            # line)...
+            if self.ui.haveBreakpoint(name, line):
+                bps = self.ui.getBreakpoints(name, line)
+                args = "delete %s" % " ".join([str(b.GetID()) for b in bps])
+                self.ui.deleteBreakpoints(name, line)
+            else:
+                args = "set -f %s -l %d" % (name, line)
+        else:
+            show_output = True
+
+        self.doCommand("breakpoint", args, show_output)
+        return
+
+    def doRefresh(self):
+        """ process pending events and update UI on request """
+        status = self.processPendingEvents()
+
+    def doShow(self, name):
+        """ handle :Lshow <name> """
+        if not name:
+            self.ui.activate()
+            return
+
+        if self.ui.showWindow(name):
+            self.ui.update(self.target, "", self)
+
+    def doHide(self, name):
+        """ handle :Lhide <name> """
+        if self.ui.hideWindow(name):
+            self.ui.update(self.target, "", self)
+
+    def doExit(self):
+        self.dbg.Terminate()
+        self.dbg = None
+
+    def getCommandResult(self, command, command_args):
+        """ Run cmd in the command interpreter and returns (success, output) """
+        result = lldb.SBCommandReturnObject()
+        cmd = "%s %s" % (command, command_args)
+
+        self.commandInterpreter.HandleCommand(cmd, result)
+        return (result.Succeeded(), result.GetOutput()
+                if result.Succeeded() else result.GetError())
+
+    def doCommand(
+            self,
+            command,
+            command_args,
+            print_on_success=True,
+            goto_file=False):
+        """ Run cmd in interpreter and print result (success or failure) on the vim status line. """
+        (success, output) = self.getCommandResult(command, command_args)
+        if success:
+            self.ui.update(self.target, "", self, goto_file)
+            if len(output) > 0 and print_on_success:
+                print(output)
+        else:
+            sys.stderr.write(output)
+
+    def getCommandOutput(self, command, command_args=""):
+        """ runs cmd in the command interpreter andreturns (status, result) """
+        result = lldb.SBCommandReturnObject()
+        cmd = "%s %s" % (command, command_args)
+        self.commandInterpreter.HandleCommand(cmd, result)
+        return (result.Succeeded(), result.GetOutput()
+                if result.Succeeded() else result.GetError())
+
+    def processPendingEvents(self, wait_seconds=0, goto_file=True):
+        """ Handle any events that are queued from the inferior.
+            Blocks for at most wait_seconds, or if wait_seconds == 0,
+            process only events that are already queued.
+        """
+
+        status = None
+        num_events_handled = 0
+
+        if self.process is not None:
+            event = lldb.SBEvent()
+            old_state = self.process.GetState()
+            new_state = None
+            done = False
+            if old_state == lldb.eStateInvalid or old_state == lldb.eStateExited:
+                # Early-exit if we are in 'boring' states
+                pass
+            else:
+                while not done and self.processListener is not None:
+                    if not self.processListener.PeekAtNextEvent(event):
+                        if wait_seconds > 0:
+                            # No events on the queue, but we are allowed to wait for wait_seconds
+                            # for any events to show up.
+                            self.processListener.WaitForEvent(
+                                wait_seconds, event)
+                            new_state = lldb.SBProcess.GetStateFromEvent(event)
+
+                            num_events_handled += 1
+
+                        done = not self.processListener.PeekAtNextEvent(event)
+                    else:
+                        # An event is on the queue, process it here.
+                        self.processListener.GetNextEvent(event)
+                        new_state = lldb.SBProcess.GetStateFromEvent(event)
+
+                        # continue if stopped after attaching
+                        if old_state == lldb.eStateAttaching and new_state == lldb.eStateStopped:
+                            self.process.Continue()
+
+                        # If needed, perform any event-specific behaviour here
+                        num_events_handled += 1
+
+        if num_events_handled == 0:
+            pass
+        else:
+            if old_state == new_state:
+                status = ""
+            self.ui.update(self.target, status, self, goto_file)
+
+
+def returnCompleteCommand(a, l, p):
+    """ Returns a "\n"-separated string with possible completion results
+        for command a with length l and cursor at p.
+    """
+    separator = "\n"
+    results = ctrl.completeCommand(a, l, p)
+    vim.command('return "%s%s"' % (separator.join(results), separator))
+
+
+def returnCompleteWindow(a, l, p):
+    """ Returns a "\n"-separated string with possible completion results
+        for commands that expect a window name parameter (like hide/show).
+        FIXME: connect to ctrl.ui instead of hardcoding the list here
+    """
+    separator = "\n"
+    results = [
+        'breakpoints',
+        'backtrace',
+        'disassembly',
+        'locals',
+        'threads',
+        'registers']
+    vim.command('return "%s%s"' % (separator.join(results), separator))
+
+global ctrl
+ctrl = LLDBController()

Added: lldb/trunk/utils/vim-lldb/python-vim-lldb/plugin.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/vim-lldb/python-vim-lldb/plugin.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/vim-lldb/python-vim-lldb/plugin.py (added)
+++ lldb/trunk/utils/vim-lldb/python-vim-lldb/plugin.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,16 @@
+
+# Try to import all dependencies, catch and handle the error gracefully if
+# it fails.
+
+import import_lldb
+
+try:
+    import lldb
+    import vim
+except ImportError:
+    sys.stderr.write(
+        "Unable to load vim/lldb module. Check lldb is on the path is available (or LLDB is set) and that script is invoked inside Vim with :pyfile")
+    pass
+else:
+    # Everthing went well, so use import to start the plugin controller
+    from lldb_controller import *

Added: lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_panes.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_panes.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_panes.py (added)
+++ lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_panes.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,669 @@
+#
+# This file contains implementations of the LLDB display panes in VIM
+#
+# The most generic way to define a new window is to inherit from VimPane
+# and to implement:
+# - get_content() - returns a string with the pane contents
+#
+# Optionally, to highlight text, implement:
+# - get_highlights() - returns a map
+#
+# And call:
+# - define_highlight(unique_name, colour)
+# at some point in the constructor.
+#
+#
+# If the pane shows some key-value data that is in the context of a
+# single frame, inherit from FrameKeyValuePane and implement:
+# - get_frame_content(self, SBFrame frame)
+#
+#
+# If the pane presents some information that can be retrieved with
+# a simple LLDB command while the subprocess is stopped, inherit
+# from StoppedCommandPane and call:
+# - self.setCommand(command, command_args)
+# at some point in the constructor.
+#
+# Optionally, you can implement:
+# - get_selected_line()
+# to highlight a selected line and place the cursor there.
+#
+#
+# FIXME: implement WatchlistPane to displayed watched expressions
+# FIXME: define interface for interactive panes, like catching enter
+#        presses to change selected frame/thread...
+#
+
+import lldb
+import vim
+
+import sys
+
+# ==============================================================
+# Get the description of an lldb object or None if not available
+# ==============================================================
+
+# Shamelessly copy/pasted from lldbutil.py in the test suite
+
+
+def get_description(obj, option=None):
+    """Calls lldb_obj.GetDescription() and returns a string, or None.
+
+    For SBTarget, SBBreakpointLocation, and SBWatchpoint lldb objects, an extra
+    option can be passed in to describe the detailed level of description
+    desired:
+        o lldb.eDescriptionLevelBrief
+        o lldb.eDescriptionLevelFull
+        o lldb.eDescriptionLevelVerbose
+    """
+    method = getattr(obj, 'GetDescription')
+    if not method:
+        return None
+    tuple = (lldb.SBTarget, lldb.SBBreakpointLocation, lldb.SBWatchpoint)
+    if isinstance(obj, tuple):
+        if option is None:
+            option = lldb.eDescriptionLevelBrief
+
+    stream = lldb.SBStream()
+    if option is None:
+        success = method(stream)
+    else:
+        success = method(stream, option)
+    if not success:
+        return None
+    return stream.GetData()
+
+
+def get_selected_thread(target):
+    """ Returns a tuple with (thread, error) where thread == None if error occurs """
+    process = target.GetProcess()
+    if process is None or not process.IsValid():
+        return (None, VimPane.MSG_NO_PROCESS)
+
+    thread = process.GetSelectedThread()
+    if thread is None or not thread.IsValid():
+        return (None, VimPane.MSG_NO_THREADS)
+    return (thread, "")
+
+
+def get_selected_frame(target):
+    """ Returns a tuple with (frame, error) where frame == None if error occurs """
+    (thread, error) = get_selected_thread(target)
+    if thread is None:
+        return (None, error)
+
+    frame = thread.GetSelectedFrame()
+    if frame is None or not frame.IsValid():
+        return (None, VimPane.MSG_NO_FRAME)
+    return (frame, "")
+
+
+def _cmd(cmd):
+    vim.command("call confirm('%s')" % cmd)
+    vim.command(cmd)
+
+
+def move_cursor(line, col=0):
+    """ moves cursor to specified line and col """
+    cw = vim.current.window
+    if cw.cursor[0] != line:
+        vim.command("execute \"normal %dgg\"" % line)
+
+
+def winnr():
+    """ Returns currently selected window number """
+    return int(vim.eval("winnr()"))
+
+
+def bufwinnr(name):
+    """ Returns window number corresponding with buffer name """
+    return int(vim.eval("bufwinnr('%s')" % name))
+
+
+def goto_window(nr):
+    """ go to window number nr"""
+    if nr != winnr():
+        vim.command(str(nr) + ' wincmd w')
+
+
+def goto_next_window():
+    """ go to next window. """
+    vim.command('wincmd w')
+    return (winnr(), vim.current.buffer.name)
+
+
+def goto_previous_window():
+    """ go to previously selected window """
+    vim.command("execute \"normal \\<c-w>p\"")
+
+
+def have_gui():
+    """ Returns True if vim is in a gui (Gvim/MacVim), False otherwise. """
+    return int(vim.eval("has('gui_running')")) == 1
+
+
+class PaneLayout(object):
+    """ A container for a (vertical) group layout of VimPanes """
+
+    def __init__(self):
+        self.panes = {}
+
+    def havePane(self, name):
+        """ Returns true if name is a registered pane, False otherwise """
+        return name in self.panes
+
+    def prepare(self, panes=[]):
+        """ Draw panes on screen. If empty list is provided, show all. """
+
+        # If we can't select a window contained in the layout, we are doing a
+        # first draw
+        first_draw = not self.selectWindow(True)
+        did_first_draw = False
+
+        # Prepare each registered pane
+        for name in self.panes:
+            if name in panes or len(panes) == 0:
+                if first_draw:
+                    # First window in layout will be created with :vsp, and
+                    # closed later
+                    vim.command(":vsp")
+                    first_draw = False
+                    did_first_draw = True
+                self.panes[name].prepare()
+
+        if did_first_draw:
+            # Close the split window
+            vim.command(":q")
+
+        self.selectWindow(False)
+
+    def contains(self, bufferName=None):
+        """ Returns True if window with name bufferName is contained in the layout, False otherwise.
+            If bufferName is None, the currently selected window is checked.
+        """
+        if not bufferName:
+            bufferName = vim.current.buffer.name
+
+        for p in self.panes:
+            if bufferName is not None and bufferName.endswith(p):
+                return True
+        return False
+
+    def selectWindow(self, select_contained=True):
+        """ Selects a window contained in the layout (if select_contained = True) and returns True.
+            If select_contained = False, a window that is not contained is selected. Returns False
+            if no group windows can be selected.
+        """
+        if select_contained == self.contains():
+            # Simple case: we are already selected
+            return True
+
+        # Otherwise, switch to next window until we find a contained window, or
+        # reach the first window again.
+        first = winnr()
+        (curnum, curname) = goto_next_window()
+
+        while not select_contained == self.contains(
+                curname) and curnum != first:
+            (curnum, curname) = goto_next_window()
+
+        return self.contains(curname) == select_contained
+
+    def hide(self, panes=[]):
+        """ Hide panes specified. If empty list provided, hide all. """
+        for name in self.panes:
+            if name in panes or len(panes) == 0:
+                self.panes[name].destroy()
+
+    def registerForUpdates(self, p):
+        self.panes[p.name] = p
+
+    def update(self, target, controller):
+        for name in self.panes:
+            self.panes[name].update(target, controller)
+
+
+class VimPane(object):
+    """ A generic base class for a pane that displays stuff """
+    CHANGED_VALUE_HIGHLIGHT_NAME_GUI = 'ColorColumn'
+    CHANGED_VALUE_HIGHLIGHT_NAME_TERM = 'lldb_changed'
+    CHANGED_VALUE_HIGHLIGHT_COLOUR_TERM = 'darkred'
+
+    SELECTED_HIGHLIGHT_NAME_GUI = 'Cursor'
+    SELECTED_HIGHLIGHT_NAME_TERM = 'lldb_selected'
+    SELECTED_HIGHLIGHT_COLOUR_TERM = 'darkblue'
+
+    MSG_NO_TARGET = "Target does not exist."
+    MSG_NO_PROCESS = "Process does not exist."
+    MSG_NO_THREADS = "No valid threads."
+    MSG_NO_FRAME = "No valid frame."
+
+    # list of defined highlights, so we avoid re-defining them
+    highlightTypes = []
+
+    def __init__(self, owner, name, open_below=False, height=3):
+        self.owner = owner
+        self.name = name
+        self.buffer = None
+        self.maxHeight = 20
+        self.openBelow = open_below
+        self.height = height
+        self.owner.registerForUpdates(self)
+
+    def isPrepared(self):
+        """ check window is OK """
+        if self.buffer is None or len(
+                dir(self.buffer)) == 0 or bufwinnr(self.name) == -1:
+            return False
+        return True
+
+    def prepare(self, method='new'):
+        """ check window is OK, if not then create """
+        if not self.isPrepared():
+            self.create(method)
+
+    def on_create(self):
+        pass
+
+    def destroy(self):
+        """ destroy window """
+        if self.buffer is None or len(dir(self.buffer)) == 0:
+            return
+        vim.command('bdelete ' + self.name)
+
+    def create(self, method):
+        """ create window """
+
+        if method != 'edit':
+            belowcmd = "below" if self.openBelow else ""
+            vim.command('silent %s %s %s' % (belowcmd, method, self.name))
+        else:
+            vim.command('silent %s %s' % (method, self.name))
+
+        self.window = vim.current.window
+
+        # Set LLDB pane options
+        vim.command("setlocal buftype=nofile")  # Don't try to open a file
+        vim.command("setlocal noswapfile")     # Don't use a swap file
+        vim.command("set nonumber")            # Don't display line numbers
+        # vim.command("set nowrap")              # Don't wrap text
+
+        # Save some parameters and reference to buffer
+        self.buffer = vim.current.buffer
+        self.width = int(vim.eval("winwidth(0)"))
+        self.height = int(vim.eval("winheight(0)"))
+
+        self.on_create()
+        goto_previous_window()
+
+    def update(self, target, controller):
+        """ updates buffer contents """
+        self.target = target
+        if not self.isPrepared():
+            # Window is hidden, or otherwise not ready for an update
+            return
+
+        original_cursor = self.window.cursor
+
+        # Select pane
+        goto_window(bufwinnr(self.name))
+
+        # Clean and update content, and apply any highlights.
+        self.clean()
+
+        if self.write(self.get_content(target, controller)):
+            self.apply_highlights()
+
+            cursor = self.get_selected_line()
+            if cursor is None:
+                # Place the cursor at its original position in the window
+                cursor_line = min(original_cursor[0], len(self.buffer))
+                cursor_col = min(
+                    original_cursor[1], len(
+                        self.buffer[
+                            cursor_line - 1]))
+            else:
+                # Place the cursor at the location requested by a VimPane
+                # implementation
+                cursor_line = min(cursor, len(self.buffer))
+                cursor_col = self.window.cursor[1]
+
+            self.window.cursor = (cursor_line, cursor_col)
+
+        goto_previous_window()
+
+    def get_selected_line(self):
+        """ Returns the line number to move the cursor to, or None to leave
+            it where the user last left it.
+            Subclasses implement this to define custom behaviour.
+        """
+        return None
+
+    def apply_highlights(self):
+        """ Highlights each set of lines in  each highlight group """
+        highlights = self.get_highlights()
+        for highlightType in highlights:
+            lines = highlights[highlightType]
+            if len(lines) == 0:
+                continue
+
+            cmd = 'match %s /' % highlightType
+            lines = ['\%' + '%d' % line + 'l' for line in lines]
+            cmd += '\\|'.join(lines)
+            cmd += '/'
+            vim.command(cmd)
+
+    def define_highlight(self, name, colour):
+        """ Defines highlihght """
+        if name in VimPane.highlightTypes:
+            # highlight already defined
+            return
+
+        vim.command(
+            "highlight %s ctermbg=%s guibg=%s" %
+            (name, colour, colour))
+        VimPane.highlightTypes.append(name)
+
+    def write(self, msg):
+        """ replace buffer with msg"""
+        self.prepare()
+
+        msg = str(msg.encode("utf-8", "replace")).split('\n')
+        try:
+            self.buffer.append(msg)
+            vim.command("execute \"normal ggdd\"")
+        except vim.error:
+            # cannot update window; happens when vim is exiting.
+            return False
+
+        move_cursor(1, 0)
+        return True
+
+    def clean(self):
+        """ clean all datas in buffer """
+        self.prepare()
+        vim.command(':%d')
+        #self.buffer[:] = None
+
+    def get_content(self, target, controller):
+        """ subclasses implement this to provide pane content """
+        assert(0 and "pane subclass must implement this")
+        pass
+
+    def get_highlights(self):
+        """ Subclasses implement this to provide pane highlights.
+            This function is expected to return a map of:
+              { highlight_name ==> [line_number, ...], ... }
+        """
+        return {}
+
+
+class FrameKeyValuePane(VimPane):
+
+    def __init__(self, owner, name, open_below):
+        """ Initialize parent, define member variables, choose which highlight
+            to use based on whether or not we have a gui (MacVim/Gvim).
+        """
+
+        VimPane.__init__(self, owner, name, open_below)
+
+        # Map-of-maps key/value history { frame --> { variable_name,
+        # variable_value } }
+        self.frameValues = {}
+
+        if have_gui():
+            self.changedHighlight = VimPane.CHANGED_VALUE_HIGHLIGHT_NAME_GUI
+        else:
+            self.changedHighlight = VimPane.CHANGED_VALUE_HIGHLIGHT_NAME_TERM
+            self.define_highlight(VimPane.CHANGED_VALUE_HIGHLIGHT_NAME_TERM,
+                                  VimPane.CHANGED_VALUE_HIGHLIGHT_COLOUR_TERM)
+
+    def format_pair(self, key, value, changed=False):
+        """ Formats a key/value pair. Appends a '*' if changed == True """
+        marker = '*' if changed else ' '
+        return "%s %s = %s\n" % (marker, key, value)
+
+    def get_content(self, target, controller):
+        """ Get content for a frame-aware pane. Also builds the list of lines that
+            need highlighting (i.e. changed values.)
+        """
+        if target is None or not target.IsValid():
+            return VimPane.MSG_NO_TARGET
+
+        self.changedLines = []
+
+        (frame, err) = get_selected_frame(target)
+        if frame is None:
+            return err
+
+        output = get_description(frame)
+        lineNum = 1
+
+        # Retrieve the last values displayed for this frame
+        frameId = get_description(frame.GetBlock())
+        if frameId in self.frameValues:
+            frameOldValues = self.frameValues[frameId]
+        else:
+            frameOldValues = {}
+
+        # Read the frame variables
+        vals = self.get_frame_content(frame)
+        for (key, value) in vals:
+            lineNum += 1
+            if len(frameOldValues) == 0 or (
+                    key in frameOldValues and frameOldValues[key] == value):
+                output += self.format_pair(key, value)
+            else:
+                output += self.format_pair(key, value, True)
+                self.changedLines.append(lineNum)
+
+        # Save values as oldValues
+        newValues = {}
+        for (key, value) in vals:
+            newValues[key] = value
+        self.frameValues[frameId] = newValues
+
+        return output
+
+    def get_highlights(self):
+        ret = {}
+        ret[self.changedHighlight] = self.changedLines
+        return ret
+
+
+class LocalsPane(FrameKeyValuePane):
+    """ Pane that displays local variables """
+
+    def __init__(self, owner, name='locals'):
+        FrameKeyValuePane.__init__(self, owner, name, open_below=True)
+
+        # FIXME: allow users to customize display of args/locals/statics/scope
+        self.arguments = True
+        self.show_locals = True
+        self.show_statics = True
+        self.show_in_scope_only = True
+
+    def format_variable(self, var):
+        """ Returns a Tuple of strings "(Type) Name", "Value" for SBValue var """
+        val = var.GetValue()
+        if val is None:
+            # If the value is too big, SBValue.GetValue() returns None; replace
+            # with ...
+            val = "..."
+
+        return ("(%s) %s" % (var.GetTypeName(), var.GetName()), "%s" % val)
+
+    def get_frame_content(self, frame):
+        """ Returns list of key-value pairs of local variables in frame """
+        vals = frame.GetVariables(self.arguments,
+                                  self.show_locals,
+                                  self.show_statics,
+                                  self.show_in_scope_only)
+        return [self.format_variable(x) for x in vals]
+
+
+class RegistersPane(FrameKeyValuePane):
+    """ Pane that displays the contents of registers """
+
+    def __init__(self, owner, name='registers'):
+        FrameKeyValuePane.__init__(self, owner, name, open_below=True)
+
+    def format_register(self, reg):
+        """ Returns a tuple of strings ("name", "value") for SBRegister reg. """
+        name = reg.GetName()
+        val = reg.GetValue()
+        if val is None:
+            val = "..."
+        return (name, val.strip())
+
+    def get_frame_content(self, frame):
+        """ Returns a list of key-value pairs ("name", "value") of registers in frame """
+
+        result = []
+        for register_sets in frame.GetRegisters():
+            # hack the register group name into the list of registers...
+            result.append((" = = %s =" % register_sets.GetName(), ""))
+
+            for reg in register_sets:
+                result.append(self.format_register(reg))
+        return result
+
+
+class CommandPane(VimPane):
+    """ Pane that displays the output of an LLDB command """
+
+    def __init__(self, owner, name, open_below, process_required=True):
+        VimPane.__init__(self, owner, name, open_below)
+        self.process_required = process_required
+
+    def setCommand(self, command, args=""):
+        self.command = command
+        self.args = args
+
+    def get_content(self, target, controller):
+        output = ""
+        if not target:
+            output = VimPane.MSG_NO_TARGET
+        elif self.process_required and not target.GetProcess():
+            output = VimPane.MSG_NO_PROCESS
+        else:
+            (success, output) = controller.getCommandOutput(
+                self.command, self.args)
+        return output
+
+
+class StoppedCommandPane(CommandPane):
+    """ Pane that displays the output of an LLDB command when the process is
+        stopped; otherwise displays process status. This class also implements
+        highlighting for a single line (to show a single-line selected entity.)
+    """
+
+    def __init__(self, owner, name, open_below):
+        """ Initialize parent and define highlight to use for selected line. """
+        CommandPane.__init__(self, owner, name, open_below)
+        if have_gui():
+            self.selectedHighlight = VimPane.SELECTED_HIGHLIGHT_NAME_GUI
+        else:
+            self.selectedHighlight = VimPane.SELECTED_HIGHLIGHT_NAME_TERM
+            self.define_highlight(VimPane.SELECTED_HIGHLIGHT_NAME_TERM,
+                                  VimPane.SELECTED_HIGHLIGHT_COLOUR_TERM)
+
+    def get_content(self, target, controller):
+        """ Returns the output of a command that relies on the process being stopped.
+            If the process is not in 'stopped' state, the process status is returned.
+        """
+        output = ""
+        if not target or not target.IsValid():
+            output = VimPane.MSG_NO_TARGET
+        elif not target.GetProcess() or not target.GetProcess().IsValid():
+            output = VimPane.MSG_NO_PROCESS
+        elif target.GetProcess().GetState() == lldb.eStateStopped:
+            (success, output) = controller.getCommandOutput(
+                self.command, self.args)
+        else:
+            (success, output) = controller.getCommandOutput("process", "status")
+        return output
+
+    def get_highlights(self):
+        """ Highlight the line under the cursor. Users moving the cursor has
+            no effect on the selected line.
+        """
+        ret = {}
+        line = self.get_selected_line()
+        if line is not None:
+            ret[self.selectedHighlight] = [line]
+            return ret
+        return ret
+
+    def get_selected_line(self):
+        """ Subclasses implement this to control where the cursor (and selected highlight)
+            is placed.
+        """
+        return None
+
+
+class DisassemblyPane(CommandPane):
+    """ Pane that displays disassembly around PC """
+
+    def __init__(self, owner, name='disassembly'):
+        CommandPane.__init__(self, owner, name, open_below=True)
+
+        # FIXME: let users customize the number of instructions to disassemble
+        self.setCommand("disassemble", "-c %d -p" % self.maxHeight)
+
+
+class ThreadPane(StoppedCommandPane):
+    """ Pane that displays threads list """
+
+    def __init__(self, owner, name='threads'):
+        StoppedCommandPane.__init__(self, owner, name, open_below=False)
+        self.setCommand("thread", "list")
+
+# FIXME: the function below assumes threads are listed in sequential order,
+#        which turns out to not be the case. Highlighting of selected thread
+#        will be disabled until this can be fixed. LLDB prints a '*' anyways
+#        beside the selected thread, so this is not too big of a problem.
+#  def get_selected_line(self):
+#    """ Place the cursor on the line with the selected entity.
+#        Subclasses should override this to customize selection.
+#        Formula: selected_line = selected_thread_id + 1
+#    """
+#    (thread, err) = get_selected_thread(self.target)
+#    if thread is None:
+#      return None
+#    else:
+#      return thread.GetIndexID() + 1
+
+
+class BacktracePane(StoppedCommandPane):
+    """ Pane that displays backtrace """
+
+    def __init__(self, owner, name='backtrace'):
+        StoppedCommandPane.__init__(self, owner, name, open_below=False)
+        self.setCommand("bt", "")
+
+    def get_selected_line(self):
+        """ Returns the line number in the buffer with the selected frame.
+            Formula: selected_line = selected_frame_id + 2
+            FIXME: the above formula hack does not work when the function return
+                   value is printed in the bt window; the wrong line is highlighted.
+        """
+
+        (frame, err) = get_selected_frame(self.target)
+        if frame is None:
+            return None
+        else:
+            return frame.GetFrameID() + 2
+
+
+class BreakpointsPane(CommandPane):
+
+    def __init__(self, owner, name='breakpoints'):
+        super(
+            BreakpointsPane,
+            self).__init__(
+            owner,
+            name,
+            open_below=False,
+            process_required=False)
+        self.setCommand("breakpoint", "list")

Added: lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_signs.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_signs.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_signs.py (added)
+++ lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_signs.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,81 @@
+
+# Classes responsible for drawing signs in the Vim user interface.
+
+import vim
+
+
+class VimSign(object):
+    SIGN_TEXT_BREAKPOINT_RESOLVED = "B>"
+    SIGN_TEXT_BREAKPOINT_UNRESOLVED = "b>"
+    SIGN_TEXT_PC = "->"
+    SIGN_HIGHLIGHT_COLOUR_PC = 'darkblue'
+
+    # unique sign id (for ':[sign/highlight] define)
+    sign_id = 1
+
+    # unique name id (for ':sign place')
+    name_id = 1
+
+    # Map of {(sign_text, highlight_colour) --> sign_name}
+    defined_signs = {}
+
+    def __init__(self, sign_text, buffer, line_number, highlight_colour=None):
+        """ Define the sign and highlight (if applicable) and show the sign. """
+
+        # Get the sign name, either by defining it, or looking it up in the map
+        # of defined signs
+        key = (sign_text, highlight_colour)
+        if key not in VimSign.defined_signs:
+            name = self.define(sign_text, highlight_colour)
+        else:
+            name = VimSign.defined_signs[key]
+
+        self.show(name, buffer.number, line_number)
+        pass
+
+    def define(self, sign_text, highlight_colour):
+        """ Defines sign and highlight (if highlight_colour is not None). """
+        sign_name = "sign%d" % VimSign.name_id
+        if highlight_colour is None:
+            vim.command("sign define %s text=%s" % (sign_name, sign_text))
+        else:
+            self.highlight_name = "highlight%d" % VimSign.name_id
+            vim.command(
+                "highlight %s ctermbg=%s guibg=%s" %
+                (self.highlight_name, highlight_colour, highlight_colour))
+            vim.command(
+                "sign define %s text=%s linehl=%s texthl=%s" %
+                (sign_name, sign_text, self.highlight_name, self.highlight_name))
+        VimSign.defined_signs[(sign_text, highlight_colour)] = sign_name
+        VimSign.name_id += 1
+        return sign_name
+
+    def show(self, name, buffer_number, line_number):
+        self.id = VimSign.sign_id
+        VimSign.sign_id += 1
+        vim.command("sign place %d name=%s line=%d buffer=%s" %
+                    (self.id, name, line_number, buffer_number))
+        pass
+
+    def hide(self):
+        vim.command("sign unplace %d" % self.id)
+        pass
+
+
+class BreakpointSign(VimSign):
+
+    def __init__(self, buffer, line_number, is_resolved):
+        txt = VimSign.SIGN_TEXT_BREAKPOINT_RESOLVED if is_resolved else VimSign.SIGN_TEXT_BREAKPOINT_UNRESOLVED
+        super(BreakpointSign, self).__init__(txt, buffer, line_number)
+
+
+class PCSign(VimSign):
+
+    def __init__(self, buffer, line_number, is_selected_thread):
+        super(
+            PCSign,
+            self).__init__(
+            VimSign.SIGN_TEXT_PC,
+            buffer,
+            line_number,
+            VimSign.SIGN_HIGHLIGHT_COLOUR_PC if is_selected_thread else None)

Added: lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_ui.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_ui.py?rev=366830&view=auto
==============================================================================
--- lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_ui.py (added)
+++ lldb/trunk/utils/vim-lldb/python-vim-lldb/vim_ui.py Tue Jul 23 10:47:08 2019
@@ -0,0 +1,257 @@
+
+# LLDB UI state in the Vim user interface.
+
+from __future__ import print_function
+
+import os
+import re
+import sys
+import lldb
+import vim
+from vim_panes import *
+from vim_signs import *
+
+
+def is_same_file(a, b):
+    """ returns true if paths a and b are the same file """
+    a = os.path.realpath(a)
+    b = os.path.realpath(b)
+    return a in b or b in a
+
+
+class UI:
+
+    def __init__(self):
+        """ Declare UI state variables """
+
+        # Default panes to display
+        self.defaultPanes = [
+            'breakpoints',
+            'backtrace',
+            'locals',
+            'threads',
+            'registers',
+            'disassembly']
+
+        # map of tuples (filename, line) --> SBBreakpoint
+        self.markedBreakpoints = {}
+
+        # Currently shown signs
+        self.breakpointSigns = {}
+        self.pcSigns = []
+
+        # Container for panes
+        self.paneCol = PaneLayout()
+
+        # All possible LLDB panes
+        self.backtracePane = BacktracePane(self.paneCol)
+        self.threadPane = ThreadPane(self.paneCol)
+        self.disassemblyPane = DisassemblyPane(self.paneCol)
+        self.localsPane = LocalsPane(self.paneCol)
+        self.registersPane = RegistersPane(self.paneCol)
+        self.breakPane = BreakpointsPane(self.paneCol)
+
+    def activate(self):
+        """ Activate UI: display default set of panes """
+        self.paneCol.prepare(self.defaultPanes)
+
+    def get_user_buffers(self, filter_name=None):
+        """ Returns a list of buffers that are not a part of the LLDB UI. That is, they
+            are not contained in the PaneLayout object self.paneCol.
+        """
+        ret = []
+        for w in vim.windows:
+            b = w.buffer
+            if not self.paneCol.contains(b.name):
+                if filter_name is None or filter_name in b.name:
+                    ret.append(b)
+        return ret
+
+    def update_pc(self, process, buffers, goto_file):
+        """ Place the PC sign on the PC location of each thread's selected frame """
+
+        def GetPCSourceLocation(thread):
+            """ Returns a tuple (thread_index, file, line, column) that represents where
+                the PC sign should be placed for a thread.
+            """
+
+            frame = thread.GetSelectedFrame()
+            frame_num = frame.GetFrameID()
+            le = frame.GetLineEntry()
+            while not le.IsValid() and frame_num < thread.GetNumFrames():
+                frame_num += 1
+                le = thread.GetFrameAtIndex(frame_num).GetLineEntry()
+
+            if le.IsValid():
+                path = os.path.join(
+                    le.GetFileSpec().GetDirectory(),
+                    le.GetFileSpec().GetFilename())
+                return (
+                    thread.GetIndexID(),
+                    path,
+                    le.GetLine(),
+                    le.GetColumn())
+            return None
+
+        # Clear all existing PC signs
+        del_list = []
+        for sign in self.pcSigns:
+            sign.hide()
+            del_list.append(sign)
+        for sign in del_list:
+            self.pcSigns.remove(sign)
+            del sign
+
+        # Select a user (non-lldb) window
+        if not self.paneCol.selectWindow(False):
+            # No user window found; avoid clobbering by splitting
+            vim.command(":vsp")
+
+        # Show a PC marker for each thread
+        for thread in process:
+            loc = GetPCSourceLocation(thread)
+            if not loc:
+                # no valid source locations for PCs. hide all existing PC
+                # markers
+                continue
+
+            buf = None
+            (tid, fname, line, col) = loc
+            buffers = self.get_user_buffers(fname)
+            is_selected = thread.GetIndexID() == process.GetSelectedThread().GetIndexID()
+            if len(buffers) == 1:
+                buf = buffers[0]
+                if buf != vim.current.buffer:
+                    # Vim has an open buffer to the required file: select it
+                    vim.command('execute ":%db"' % buf.number)
+            elif is_selected and vim.current.buffer.name not in fname and os.path.exists(fname) and goto_file:
+                # FIXME: If current buffer is modified, vim will complain when we try to switch away.
+                # Find a way to detect if the current buffer is modified,
+                # and...warn instead?
+                vim.command('execute ":e %s"' % fname)
+                buf = vim.current.buffer
+            elif len(buffers) > 1 and goto_file:
+                # FIXME: multiple open buffers match PC location
+                continue
+            else:
+                continue
+
+            self.pcSigns.append(PCSign(buf, line, is_selected))
+
+            if is_selected and goto_file:
+                # if the selected file has a PC marker, move the cursor there
+                # too
+                curname = vim.current.buffer.name
+                if curname is not None and is_same_file(curname, fname):
+                    move_cursor(line, 0)
+                elif move_cursor:
+                    print("FIXME: not sure where to move cursor because %s != %s " % (vim.current.buffer.name, fname))
+
+    def update_breakpoints(self, target, buffers):
+        """ Decorates buffer with signs corresponding to breakpoints in target. """
+
+        def GetBreakpointLocations(bp):
+            """ Returns a list of tuples (resolved, filename, line) where a breakpoint was resolved. """
+            if not bp.IsValid():
+                sys.stderr.write("breakpoint is invalid, no locations")
+                return []
+
+            ret = []
+            numLocs = bp.GetNumLocations()
+            for i in range(numLocs):
+                loc = bp.GetLocationAtIndex(i)
+                desc = get_description(loc, lldb.eDescriptionLevelFull)
+                match = re.search('at\ ([^:]+):([\d]+)', desc)
+                try:
+                    lineNum = int(match.group(2).strip())
+                    ret.append((loc.IsResolved(), match.group(1), lineNum))
+                except ValueError as e:
+                    sys.stderr.write(
+                        "unable to parse breakpoint location line number: '%s'" %
+                        match.group(2))
+                    sys.stderr.write(str(e))
+
+            return ret
+
+        if target is None or not target.IsValid():
+            return
+
+        needed_bps = {}
+        for bp_index in range(target.GetNumBreakpoints()):
+            bp = target.GetBreakpointAtIndex(bp_index)
+            locations = GetBreakpointLocations(bp)
+            for (is_resolved, file, line) in GetBreakpointLocations(bp):
+                for buf in buffers:
+                    if file in buf.name:
+                        needed_bps[(buf, line, is_resolved)] = bp
+
+        # Hide any signs that correspond with disabled breakpoints
+        del_list = []
+        for (b, l, r) in self.breakpointSigns:
+            if (b, l, r) not in needed_bps:
+                self.breakpointSigns[(b, l, r)].hide()
+                del_list.append((b, l, r))
+        for d in del_list:
+            del self.breakpointSigns[d]
+
+        # Show any signs for new breakpoints
+        for (b, l, r) in needed_bps:
+            bp = needed_bps[(b, l, r)]
+            if self.haveBreakpoint(b.name, l):
+                self.markedBreakpoints[(b.name, l)].append(bp)
+            else:
+                self.markedBreakpoints[(b.name, l)] = [bp]
+
+            if (b, l, r) not in self.breakpointSigns:
+                s = BreakpointSign(b, l, r)
+                self.breakpointSigns[(b, l, r)] = s
+
+    def update(self, target, status, controller, goto_file=False):
+        """ Updates debugger info panels and breakpoint/pc marks and prints
+            status to the vim status line. If goto_file is True, the user's
+            cursor is moved to the source PC location in the selected frame.
+        """
+
+        self.paneCol.update(target, controller)
+        self.update_breakpoints(target, self.get_user_buffers())
+
+        if target is not None and target.IsValid():
+            process = target.GetProcess()
+            if process is not None and process.IsValid():
+                self.update_pc(process, self.get_user_buffers, goto_file)
+
+        if status is not None and len(status) > 0:
+            print(status)
+
+    def haveBreakpoint(self, file, line):
+        """ Returns True if we have a breakpoint at file:line, False otherwise  """
+        return (file, line) in self.markedBreakpoints
+
+    def getBreakpoints(self, fname, line):
+        """ Returns the LLDB SBBreakpoint object at fname:line """
+        if self.haveBreakpoint(fname, line):
+            return self.markedBreakpoints[(fname, line)]
+        else:
+            return None
+
+    def deleteBreakpoints(self, name, line):
+        del self.markedBreakpoints[(name, line)]
+
+    def showWindow(self, name):
+        """ Shows (un-hides) window pane specified by name """
+        if not self.paneCol.havePane(name):
+            sys.stderr.write("unknown window: %s" % name)
+            return False
+        self.paneCol.prepare([name])
+        return True
+
+    def hideWindow(self, name):
+        """ Hides window pane specified by name """
+        if not self.paneCol.havePane(name):
+            sys.stderr.write("unknown window: %s" % name)
+            return False
+        self.paneCol.hide([name])
+        return True
+
+global ui
+ui = UI()




More information about the lldb-commits mailing list