mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 22:06:06 +00:00
[Utils] Remove legacy scripts
As pointed out by Nathan in D65155, these scrips don't seem to serve any real need anymore. llvm-svn: 366827
This commit is contained in:
parent
6f13637a3e
commit
6491076ec6
@ -1,14 +0,0 @@
|
||||
# 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()
|
@ -1,153 +0,0 @@
|
||||
//===- 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);
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
//===- 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)
|
@ -1,34 +0,0 @@
|
||||
//===- 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
|
@ -1,73 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Convert the raw message sources from git patch emails to git-am friendly files.
|
||||
|
||||
Usage:
|
||||
|
||||
1. Mail.app -> Save As -> api.eml (Raw Message Source)
|
||||
2. .../convert.py api.eml
|
||||
3. git am [--signoff] < api.eml
|
||||
4. git svn dcommit [--commit-url https://id@llvm.org/svn/llvm-project/lldb/trunk]
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import io
|
||||
|
||||
|
||||
def usage(problem_file=None):
|
||||
if problem_file:
|
||||
print("%s is not a file" % problem_file)
|
||||
print("Usage: convert.py raw-message-source [raw-message-source2 ...]")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def do_convert(file):
|
||||
"""Skip all preceding mail message headers until 'From: ' is encountered.
|
||||
Then for each line ('From: ' header included), replace the dos style CRLF
|
||||
end-of-line with unix style LF end-of-line.
|
||||
"""
|
||||
print("converting %s ..." % file)
|
||||
|
||||
with open(file, 'r') as f_in:
|
||||
content = f_in.read()
|
||||
|
||||
# The new content to be written back to the same file.
|
||||
new_content = io.StringIO()
|
||||
|
||||
# Boolean flag controls whether to start printing lines.
|
||||
from_header_seen = False
|
||||
|
||||
# By default, splitlines() don't include line breaks. CRLF should be gone.
|
||||
for line in content.splitlines():
|
||||
# Wait till we scan the 'From: ' header before start printing the
|
||||
# lines.
|
||||
if not from_header_seen:
|
||||
if not line.startswith('From: '):
|
||||
continue
|
||||
else:
|
||||
from_header_seen = True
|
||||
|
||||
print(line, file=new_content)
|
||||
|
||||
with open(file, 'w') as f_out:
|
||||
f_out.write(new_content.getvalue())
|
||||
|
||||
print("done")
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 1:
|
||||
usage()
|
||||
# Convert the raw message source one by one.
|
||||
for file in sys.argv[1:]:
|
||||
if not os.path.isfile(file):
|
||||
usage(file)
|
||||
do_convert(file)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,6 +0,0 @@
|
||||
add_llvm_utility(lit-cpuid
|
||||
lit-cpuid.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(lit-cpuid PRIVATE LLVMSupport)
|
||||
set_target_properties(lit-cpuid PROPERTIES FOLDER "lldb utils")
|
@ -1,37 +0,0 @@
|
||||
//===- 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;
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
# 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()
|
@ -1,17 +0,0 @@
|
||||
#!/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))
|
@ -1,36 +0,0 @@
|
||||
|
||||
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
|
@ -1,93 +0,0 @@
|
||||
##===-- 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)
|
@ -1,130 +0,0 @@
|
||||
##===-- 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()
|
@ -1,338 +0,0 @@
|
||||
##===-- 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()
|
@ -1,142 +0,0 @@
|
||||
##===-- 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
|
@ -1,26 +0,0 @@
|
||||
##===-- 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
|
File diff suppressed because it is too large
Load Diff
@ -1,152 +0,0 @@
|
||||
#!/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()
|
@ -1,77 +0,0 @@
|
||||
#!/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()
|
@ -1,238 +0,0 @@
|
||||
##===-- 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()
|
@ -1,41 +0,0 @@
|
||||
##===-- 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
|
@ -1,97 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Greps and returns the first svn log entry containing a line matching the regular
|
||||
expression pattern passed as the only arg.
|
||||
|
||||
Example:
|
||||
|
||||
svn log -v | grep-svn-log.py '^ D.+why_are_you_missing.h$'
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import fileinput
|
||||
import re
|
||||
import sys
|
||||
import io
|
||||
|
||||
# Separator string for "svn log -v" output.
|
||||
separator = '-' * 72
|
||||
|
||||
usage = """Usage: grep-svn-log.py line-pattern
|
||||
Example:
|
||||
svn log -v | grep-svn-log.py '^ D.+why_are_you_missing.h'"""
|
||||
|
||||
|
||||
class Log(io.StringIO):
|
||||
"""Simple facade to keep track of the log content."""
|
||||
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def add_line(self, a_line):
|
||||
"""Add a line to the content, if there is a previous line, commit it."""
|
||||
global separator
|
||||
if self.prev_line is not None:
|
||||
print(self.prev_line, file=self)
|
||||
self.prev_line = a_line
|
||||
self.separator_added = (a_line == separator)
|
||||
|
||||
def del_line(self):
|
||||
"""Forget about the previous line, do not commit it."""
|
||||
self.prev_line = None
|
||||
|
||||
def reset(self):
|
||||
"""Forget about the previous lines entered."""
|
||||
io.StringIO.__init__(self)
|
||||
self.prev_line = None
|
||||
|
||||
def finish(self):
|
||||
"""Call this when you're finished with populating content."""
|
||||
if self.prev_line is not None:
|
||||
print(self.prev_line, file=self)
|
||||
self.prev_line = None
|
||||
|
||||
|
||||
def grep(regexp):
|
||||
# The log content to be written out once a match is found.
|
||||
log = Log()
|
||||
|
||||
LOOKING_FOR_MATCH = 0
|
||||
FOUND_LINE_MATCH = 1
|
||||
state = LOOKING_FOR_MATCH
|
||||
|
||||
while True:
|
||||
line = sys.stdin.readline()
|
||||
if not line:
|
||||
return
|
||||
line = line.splitlines()[0]
|
||||
if state == FOUND_LINE_MATCH:
|
||||
# At this state, we keep on accumulating lines until the separator
|
||||
# is encountered. At which point, we can return the log content.
|
||||
if line == separator:
|
||||
log.finish()
|
||||
print(log.getvalue())
|
||||
return
|
||||
log.add_line(line)
|
||||
|
||||
elif state == LOOKING_FOR_MATCH:
|
||||
if line == separator:
|
||||
log.reset()
|
||||
log.add_line(line)
|
||||
# Update next state if necessary.
|
||||
if regexp.search(line):
|
||||
state = FOUND_LINE_MATCH
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print(usage)
|
||||
sys.exit(0)
|
||||
|
||||
regexp = re.compile(sys.argv[1])
|
||||
grep(regexp)
|
||||
sys.stdin.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,293 +0,0 @@
|
||||
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@gmail.com).
|
@ -1,8 +0,0 @@
|
||||
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"
|
@ -1,61 +0,0 @@
|
||||
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
|
@ -1,12 +0,0 @@
|
||||
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)
|
@ -1,2 +0,0 @@
|
||||
[Master]
|
||||
init-hook='import os; import sys; sys.path.append(os.path.join(os.getcwd(), "lib")); print("hello from {}".format(os.getcwd()))'
|
@ -1,270 +0,0 @@
|
||||
#!/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()
|
@ -1,406 +0,0 @@
|
||||
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 $
|
@ -1,94 +0,0 @@
|
||||
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
|
@ -1,18 +0,0 @@
|
||||
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 $
|
@ -1,238 +0,0 @@
|
||||
#!/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()
|
@ -1,294 +0,0 @@
|
||||
#!/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()
|
@ -1,116 +0,0 @@
|
||||
#!/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()
|
@ -1,13 +0,0 @@
|
||||
#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");
|
||||
}
|
@ -1,182 +0,0 @@
|
||||
#!/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()
|
@ -1,144 +0,0 @@
|
||||
#!/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()
|
@ -1,131 +0,0 @@
|
||||
#!/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()
|
@ -1,59 +0,0 @@
|
||||
|
||||
=================
|
||||
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
|
@ -1,115 +0,0 @@
|
||||
*lldb.txt* A plugin that enables debugging from your favourite editor
|
||||
|
||||
Author: Daniel Malea <daniel.malea@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:
|
@ -1,151 +0,0 @@
|
||||
|
||||
" 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()
|
@ -1,71 +0,0 @@
|
||||
|
||||
# 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.")
|
@ -1,415 +0,0 @@
|
||||
|
||||
#
|
||||
# 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()
|
@ -1,16 +0,0 @@
|
||||
|
||||
# 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 *
|
@ -1,669 +0,0 @@
|
||||
#
|
||||
# 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")
|
@ -1,81 +0,0 @@
|
||||
|
||||
# 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)
|
@ -1,257 +0,0 @@
|
||||
|
||||
# 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()
|
Loading…
x
Reference in New Issue
Block a user