Index: vendor/lldb/dist/include/lldb/Utility/CompletionRequest.h =================================================================== --- vendor/lldb/dist/include/lldb/Utility/CompletionRequest.h (revision 337146) +++ vendor/lldb/dist/include/lldb/Utility/CompletionRequest.h (revision 337147) @@ -1,120 +1,149 @@ //===-- CompletionRequest.h -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_UTILITY_COMPLETIONREQUEST_H #define LLDB_UTILITY_COMPLETIONREQUEST_H #include "lldb/Utility/Args.h" #include "lldb/Utility/StringList.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" namespace lldb_private { //---------------------------------------------------------------------- /// @class CompletionRequest CompletionRequest.h /// "lldb/Utility/ArgCompletionRequest.h" /// /// Contains all information necessary to complete an incomplete command /// for the user. Will be filled with the generated completions by the different /// completions functions. /// //---------------------------------------------------------------------- class CompletionRequest { public: //---------------------------------------------------------- /// Constructs a completion request. /// /// @param [in] command_line /// The command line the user has typed at this point. /// /// @param [in] raw_cursor_pos /// The position of the cursor in the command line string. Index 0 means /// the cursor is at the start of the line. The completion starts from /// this cursor position. /// /// @param [in] match_start_point /// @param [in] max_return_elements /// If there is a match that is expensive to compute, these are here to /// allow you to compute the completions in batches. Start the /// completion from match_start_point, and return match_return_elements /// elements. /// /// @param [out] matches /// A list of matches that will be filled by the different completion /// handlers. //---------------------------------------------------------- CompletionRequest(llvm::StringRef command_line, unsigned raw_cursor_pos, int match_start_point, int max_return_elements, StringList &matches); llvm::StringRef GetRawLine() const { return m_command; } unsigned GetRawCursorPos() const { return m_raw_cursor_pos; } const Args &GetParsedLine() const { return m_parsed_line; } Args &GetParsedLine() { return m_parsed_line; } const Args &GetPartialParsedLine() const { return m_partial_parsed_line; } void SetCursorIndex(int i) { m_cursor_index = i; } int GetCursorIndex() const { return m_cursor_index; } void SetCursorCharPosition(int pos) { m_cursor_char_position = pos; } int GetCursorCharPosition() const { return m_cursor_char_position; } int GetMatchStartPoint() const { return m_match_start_point; } int GetMaxReturnElements() const { return m_max_return_elements; } bool GetWordComplete() { return m_word_complete; } void SetWordComplete(bool v) { m_word_complete = v; } - /// The array of matches returned. - StringList &GetMatches() { return *m_matches; } + /// Adds a possible completion string. If the completion was already + /// suggested before, it will not be added to the list of results. A copy of + /// the suggested completion is stored, so the given string can be free'd + /// afterwards. + /// + /// @param match The suggested completion. + void AddCompletion(llvm::StringRef completion) { + // Add the completion if we haven't seen the same value before. + if (m_match_set.insert(completion).second) + m_matches->AppendString(completion); + } + /// Adds multiple possible completion strings. + /// + /// \param completions The list of completions. + /// + /// @see AddCompletion + void AddCompletions(const StringList &completions) { + for (std::size_t i = 0; i < completions.GetSize(); ++i) + AddCompletion(completions.GetStringAtIndex(i)); + } + + std::size_t GetNumberOfMatches() const { return m_matches->GetSize(); } + llvm::StringRef GetCursorArgument() const { return GetParsedLine().GetArgumentAtIndex(GetCursorIndex()); } llvm::StringRef GetCursorArgumentPrefix() const { return GetCursorArgument().substr(0, GetCursorCharPosition()); } private: /// The raw command line we are supposed to complete. llvm::StringRef m_command; /// The cursor position in m_command. unsigned m_raw_cursor_pos; /// The command line parsed as arguments. Args m_parsed_line; /// The command line until the cursor position parsed as arguments. Args m_partial_parsed_line; /// The index of the argument in which the completion cursor is. int m_cursor_index; /// The cursor position in the argument indexed by m_cursor_index. int m_cursor_char_position; /// If there is a match that is expensive /// to compute, these are here to allow you to compute the completions in /// batches. Start the completion from \amatch_start_point, and return /// \amatch_return_elements elements. // FIXME: These two values are not implemented. int m_match_start_point; int m_max_return_elements; /// \btrue if this is a complete option value (a space will be inserted /// after the completion.) \bfalse otherwise. bool m_word_complete = false; - // We don't own the list. + + // Note: This list is kept private. This is by design to prevent that any + // completion depends on any already computed completion from another backend. + // Note: We don't own the list. It's owned by the creator of the + // CompletionRequest object. StringList *m_matches; + + /// List of added completions so far. Used to filter out duplicates. + llvm::StringSet<> m_match_set; }; } // namespace lldb_private #endif // LLDB_UTILITY_COMPLETIONREQUEST_H Index: vendor/lldb/dist/include/lldb/Utility/Stream.h =================================================================== --- vendor/lldb/dist/include/lldb/Utility/Stream.h (revision 337146) +++ vendor/lldb/dist/include/lldb/Utility/Stream.h (revision 337147) @@ -1,544 +1,542 @@ //===-- Stream.h ------------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_Stream_h_ #define liblldb_Stream_h_ #include "lldb/Utility/Flags.h" #include "lldb/lldb-defines.h" #include "lldb/lldb-enumerations.h" // for ByteOrder::eByteOrderInvalid #include "llvm/ADT/StringRef.h" // for StringRef #include "llvm/Support/FormatVariadic.h" #include #include // for size_t #include // for uint32_t, uint64_t, uint8_t #include // for forward namespace lldb_private { //---------------------------------------------------------------------- /// @class Stream Stream.h "lldb/Utility/Stream.h" /// A stream class that can stream formatted output to a file. //---------------------------------------------------------------------- class Stream { public: //------------------------------------------------------------------ /// \a m_flags bit values. //------------------------------------------------------------------ enum { eBinary = (1 << 0) ///< Get and put data as binary instead of as the default /// string mode. }; //------------------------------------------------------------------ /// Construct with flags and address size and byte order. /// /// Construct with dump flags \a flags and the default address size. \a /// flags can be any of the above enumeration logical OR'ed together. //------------------------------------------------------------------ Stream(uint32_t flags, uint32_t addr_size, lldb::ByteOrder byte_order); //------------------------------------------------------------------ /// Construct a default Stream, not binary, host byte order and host addr /// size. /// //------------------------------------------------------------------ Stream(); //------------------------------------------------------------------ /// Destructor //------------------------------------------------------------------ virtual ~Stream(); //------------------------------------------------------------------ // Subclasses must override these methods //------------------------------------------------------------------ //------------------------------------------------------------------ /// Flush the stream. /// /// Subclasses should flush the stream to make any output appear if the /// stream has any buffering. //------------------------------------------------------------------ virtual void Flush() = 0; //------------------------------------------------------------------ /// Output character bytes to the stream. /// /// Appends \a src_len characters from the buffer \a src to the stream. /// /// @param[in] src /// A buffer containing at least \a src_len bytes of data. /// /// @param[in] src_len /// A number of bytes to append to the stream. /// /// @return /// The number of bytes that were appended to the stream. //------------------------------------------------------------------ virtual size_t Write(const void *src, size_t src_len) = 0; //------------------------------------------------------------------ // Member functions //------------------------------------------------------------------ size_t PutChar(char ch); //------------------------------------------------------------------ /// Set the byte_order value. /// /// Sets the byte order of the data to extract. Extracted values will be /// swapped if necessary when decoding. /// /// @param[in] byte_order /// The byte order value to use when extracting data. /// /// @return /// The old byte order value. //------------------------------------------------------------------ lldb::ByteOrder SetByteOrder(lldb::ByteOrder byte_order); //------------------------------------------------------------------ /// Format a C string from a printf style format and variable arguments and /// encode and append the resulting C string as hex bytes. /// /// @param[in] format /// A printf style format string. /// /// @param[in] ... /// Any additional arguments needed for the printf format string. /// /// @return /// The number of bytes that were appended to the stream. //------------------------------------------------------------------ size_t PrintfAsRawHex8(const char *format, ...) __attribute__((__format__(__printf__, 2, 3))); //------------------------------------------------------------------ /// Format a C string from a printf style format and variable arguments and /// encode and append the resulting C string as hex bytes. /// /// @param[in] format /// A printf style format string. /// /// @param[in] ... /// Any additional arguments needed for the printf format string. /// /// @return /// The number of bytes that were appended to the stream. //------------------------------------------------------------------ size_t PutHex8(uint8_t uvalue); size_t PutNHex8(size_t n, uint8_t uvalue); size_t PutHex16(uint16_t uvalue, lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); size_t PutHex32(uint32_t uvalue, lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); size_t PutHex64(uint64_t uvalue, lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); size_t PutMaxHex64(uint64_t uvalue, size_t byte_size, lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); size_t PutFloat(float f, lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); size_t PutDouble(double d, lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); size_t PutLongDouble(long double ld, lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); size_t PutPointer(void *ptr); // Append \a src_len bytes from \a src to the stream as hex characters (two // ascii characters per byte of input data) size_t PutBytesAsRawHex8(const void *src, size_t src_len, lldb::ByteOrder src_byte_order = lldb::eByteOrderInvalid, lldb::ByteOrder dst_byte_order = lldb::eByteOrderInvalid); // Append \a src_len bytes from \a s to the stream as binary data. size_t PutRawBytes(const void *s, size_t src_len, lldb::ByteOrder src_byte_order = lldb::eByteOrderInvalid, lldb::ByteOrder dst_byte_order = lldb::eByteOrderInvalid); size_t PutCStringAsRawHex8(const char *s); //------------------------------------------------------------------ /// Output a NULL terminated C string \a cstr to the stream \a s. /// /// @param[in] cstr /// A NULL terminated C string. /// /// @return /// A reference to this class so multiple things can be streamed /// in one statement. //------------------------------------------------------------------ Stream &operator<<(const char *cstr); Stream &operator<<(llvm::StringRef str); //------------------------------------------------------------------ /// Output a pointer value \a p to the stream \a s. /// /// @param[in] p /// A void pointer. /// /// @return /// A reference to this class so multiple things can be streamed /// in one statement. //------------------------------------------------------------------ Stream &operator<<(const void *p); //------------------------------------------------------------------ /// Output a character \a ch to the stream \a s. /// /// @param[in] ch /// A printable character value. /// /// @return /// A reference to this class so multiple things can be streamed /// in one statement. //------------------------------------------------------------------ Stream &operator<<(char ch); //------------------------------------------------------------------ /// Output a uint8_t \a uval to the stream \a s. /// /// @param[in] uval /// A uint8_t value. /// /// @return /// A reference to this class so multiple things can be streamed /// in one statement. //------------------------------------------------------------------ Stream &operator<<(uint8_t uval); //------------------------------------------------------------------ /// Output a uint16_t \a uval to the stream \a s. /// /// @param[in] uval /// A uint16_t value. /// /// @return /// A reference to this class so multiple things can be streamed /// in one statement. //------------------------------------------------------------------ Stream &operator<<(uint16_t uval); //------------------------------------------------------------------ /// Output a uint32_t \a uval to the stream \a s. /// /// @param[in] uval /// A uint32_t value. /// /// @return /// A reference to this class so multiple things can be streamed /// in one statement. //------------------------------------------------------------------ Stream &operator<<(uint32_t uval); //------------------------------------------------------------------ /// Output a uint64_t \a uval to the stream \a s. /// /// @param[in] uval /// A uint64_t value. /// /// @return /// A reference to this class so multiple things can be streamed /// in one statement. //------------------------------------------------------------------ Stream &operator<<(uint64_t uval); //------------------------------------------------------------------ /// Output a int8_t \a sval to the stream \a s. /// /// @param[in] sval /// A int8_t value. /// /// @return /// A reference to this class so multiple things can be streamed /// in one statement. //------------------------------------------------------------------ Stream &operator<<(int8_t sval); //------------------------------------------------------------------ /// Output a int16_t \a sval to the stream \a s. /// /// @param[in] sval /// A int16_t value. /// /// @return /// A reference to this class so multiple things can be streamed /// in one statement. //------------------------------------------------------------------ Stream &operator<<(int16_t sval); //------------------------------------------------------------------ /// Output a int32_t \a sval to the stream \a s. /// /// @param[in] sval /// A int32_t value. /// /// @return /// A reference to this class so multiple things can be streamed /// in one statement. //------------------------------------------------------------------ Stream &operator<<(int32_t sval); //------------------------------------------------------------------ /// Output a int64_t \a sval to the stream \a s. /// /// @param[in] sval /// A int64_t value. /// /// @return /// A reference to this class so multiple things can be streamed /// in one statement. //------------------------------------------------------------------ Stream &operator<<(int64_t sval); //------------------------------------------------------------------ /// Output an address value to this stream. /// /// Put an address \a addr out to the stream with optional \a prefix and \a /// suffix strings. /// /// @param[in] addr /// An address value. /// /// @param[in] addr_size /// Size in bytes of the address, used for formatting. /// /// @param[in] prefix /// A prefix C string. If nullptr, no prefix will be output. /// /// @param[in] suffix /// A suffix C string. If nullptr, no suffix will be output. //------------------------------------------------------------------ void Address(uint64_t addr, uint32_t addr_size, const char *prefix = nullptr, const char *suffix = nullptr); //------------------------------------------------------------------ /// Output an address range to this stream. /// /// Put an address range \a lo_addr - \a hi_addr out to the stream with /// optional \a prefix and \a suffix strings. /// /// @param[in] lo_addr /// The start address of the address range. /// /// @param[in] hi_addr /// The end address of the address range. /// /// @param[in] addr_size /// Size in bytes of the address, used for formatting. /// /// @param[in] prefix /// A prefix C string. If nullptr, no prefix will be output. /// /// @param[in] suffix /// A suffix C string. If nullptr, no suffix will be output. //------------------------------------------------------------------ void AddressRange(uint64_t lo_addr, uint64_t hi_addr, uint32_t addr_size, const char *prefix = nullptr, const char *suffix = nullptr); //------------------------------------------------------------------ /// Output a C string to the stream. /// /// Print a C string \a cstr to the stream. /// /// @param[in] cstr /// The string to be output to the stream. //------------------------------------------------------------------ size_t PutCString(llvm::StringRef cstr); //------------------------------------------------------------------ /// Output and End of Line character to the stream. //------------------------------------------------------------------ size_t EOL(); //------------------------------------------------------------------ /// Get the address size in bytes. /// /// @return /// The size of an address in bytes that is used when outputting /// address and pointer values to the stream. //------------------------------------------------------------------ uint32_t GetAddressByteSize() const; //------------------------------------------------------------------ /// The flags accessor. /// /// @return /// A reference to the Flags member variable. //------------------------------------------------------------------ Flags &GetFlags(); //------------------------------------------------------------------ /// The flags const accessor. /// /// @return /// A const reference to the Flags member variable. //------------------------------------------------------------------ const Flags &GetFlags() const; //------------------------------------------------------------------ //// The byte order accessor. //// //// @return //// The byte order. //------------------------------------------------------------------ lldb::ByteOrder GetByteOrder() const; //------------------------------------------------------------------ /// Get the current indentation level. /// /// @return /// The current indentation level as an integer. //------------------------------------------------------------------ int GetIndentLevel() const; //------------------------------------------------------------------ /// Indent the current line in the stream. /// /// Indent the current line using the current indentation level and print an /// optional string following the indentation spaces. /// /// @param[in] s /// A C string to print following the indentation. If nullptr, just /// output the indentation characters. //------------------------------------------------------------------ size_t Indent(const char *s = nullptr); size_t Indent(llvm::StringRef s); //------------------------------------------------------------------ /// Decrement the current indentation level. //------------------------------------------------------------------ void IndentLess(int amount = 2); //------------------------------------------------------------------ /// Increment the current indentation level. //------------------------------------------------------------------ void IndentMore(int amount = 2); //------------------------------------------------------------------ /// Output an offset value. /// /// Put an offset \a uval out to the stream using the printf format in \a /// format. /// /// @param[in] offset /// The offset value. /// /// @param[in] format /// The printf style format to use when outputting the offset. //------------------------------------------------------------------ void Offset(uint32_t offset, const char *format = "0x%8.8x: "); //------------------------------------------------------------------ /// Output printf formatted output to the stream. /// /// Print some formatted output to the stream. /// /// @param[in] format /// A printf style format string. /// /// @param[in] ... /// Variable arguments that are needed for the printf style /// format string \a format. //------------------------------------------------------------------ size_t Printf(const char *format, ...) __attribute__((format(printf, 2, 3))); size_t PrintfVarArg(const char *format, va_list args); template void Format(const char *format, Args &&... args) { PutCString(llvm::formatv(format, std::forward(args)...).str()); } //------------------------------------------------------------------ /// Output a quoted C string value to the stream. /// /// Print a double quoted NULL terminated C string to the stream using the /// printf format in \a format. /// /// @param[in] cstr /// A NULL terminated C string value. /// /// @param[in] format /// The optional C string format that can be overridden. //------------------------------------------------------------------ void QuotedCString(const char *cstr, const char *format = "\"%s\""); //------------------------------------------------------------------ /// Set the address size in bytes. /// /// @param[in] addr_size /// The new size in bytes of an address to use when outputting /// address and pointer values. //------------------------------------------------------------------ void SetAddressByteSize(uint32_t addr_size); //------------------------------------------------------------------ /// Set the current indentation level. /// /// @param[in] level /// The new indentation level. //------------------------------------------------------------------ void SetIndentLevel(int level); //------------------------------------------------------------------ /// Output a SLEB128 number to the stream. /// /// Put an SLEB128 \a uval out to the stream using the printf format in \a /// format. /// /// @param[in] uval /// A uint64_t value that was extracted as a SLEB128 value. /// /// @param[in] format /// The optional printf format that can be overridden. //------------------------------------------------------------------ size_t PutSLEB128(int64_t uval); //------------------------------------------------------------------ /// Output a ULEB128 number to the stream. /// /// Put an ULEB128 \a uval out to the stream using the printf format in \a /// format. /// /// @param[in] uval /// A uint64_t value that was extracted as a ULEB128 value. /// /// @param[in] format /// The optional printf format that can be overridden. //------------------------------------------------------------------ size_t PutULEB128(uint64_t uval); - static void UnitTest(Stream *s); - protected: //------------------------------------------------------------------ // Member variables //------------------------------------------------------------------ Flags m_flags; ///< Dump flags. uint32_t m_addr_size; ///< Size of an address in bytes. lldb::ByteOrder m_byte_order; ///< Byte order to use when encoding scalar types. int m_indent_level; ///< Indention level. size_t _PutHex8(uint8_t uvalue, bool add_prefix); }; } // namespace lldb_private #endif // liblldb_Stream_h_ Index: vendor/lldb/dist/packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/api/multiple-debuggers/TestMultipleDebuggers.py (revision 337147) @@ -1,46 +1,46 @@ """Test the lldb public C++ api when doing multiple debug sessions simultaneously.""" from __future__ import print_function import os import re import subprocess import sys import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestMultipleSimultaneousDebuggers(TestBase): mydir = TestBase.compute_mydir(__file__) - # This test case fails non-deterministically. + # This test case fails non-deterministically. @skipIfNoSBHeaders @expectedFailureAll(bugnumber="llvm.org/pr20282") def test_multiple_debuggers(self): env = {self.dylibPath: self.getLLDBLibraryEnvVal()} self.driver_exe = self.getBuildArtifact("multi-process-driver") self.buildDriver('multi-process-driver.cpp', self.driver_exe) self.addTearDownHook(lambda: os.remove(self.driver_exe)) self.signBinary(self.driver_exe) self.inferior_exe = self.getBuildArtifact("testprog") self.buildDriver('testprog.cpp', self.inferior_exe) self.addTearDownHook(lambda: os.remove(self.inferior_exe)) # check_call will raise a CalledProcessError if multi-process-driver doesn't return # exit code 0 to indicate success. We can let this exception go - the test harness # will recognize it as a test failure. if self.TraceOn(): print("Running test %s" % self.driver_exe) check_call([self.driver_exe, self.inferior_exe], env=env) else: with open(os.devnull, 'w') as fnull: check_call([self.driver_exe, self.inferior_exe], env=env, stdout=fnull, stderr=fnull) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/api/multithreaded/TestMultithreaded.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/api/multithreaded/TestMultithreaded.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/api/multithreaded/TestMultithreaded.py (revision 337147) @@ -1,114 +1,114 @@ """Test the lldb public C++ api breakpoint callbacks.""" from __future__ import print_function # __package__ = "lldbsuite.test" import os import re from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil import subprocess class SBBreakpointCallbackCase(TestBase): NO_DEBUG_INFO_TESTCASE = True - + def setUp(self): TestBase.setUp(self) self.generateSource('driver.cpp') self.generateSource('listener_test.cpp') self.generateSource('test_breakpoint_callback.cpp') self.generateSource('test_listener_event_description.cpp') self.generateSource('test_listener_event_process_state.cpp') self.generateSource('test_listener_resume.cpp') mydir = TestBase.compute_mydir(__file__) @skipIfRemote @skipIfNoSBHeaders # clang-cl does not support throw or catch (llvm.org/pr24538) @skipIfWindows def test_breakpoint_callback(self): """Test the that SBBreakpoint callback is invoked when a breakpoint is hit. """ self.build_and_test('driver.cpp test_breakpoint_callback.cpp', 'test_breakpoint_callback') @skipIfRemote @skipIfNoSBHeaders # clang-cl does not support throw or catch (llvm.org/pr24538) @skipIfWindows @expectedFlakeyFreeBSD def test_sb_api_listener_event_description(self): """ Test the description of an SBListener breakpoint event is valid.""" self.build_and_test( 'driver.cpp listener_test.cpp test_listener_event_description.cpp', 'test_listener_event_description') pass @skipIfRemote @skipIfNoSBHeaders # clang-cl does not support throw or catch (llvm.org/pr24538) @skipIfWindows @expectedFlakeyFreeBSD def test_sb_api_listener_event_process_state(self): """ Test that a registered SBListener receives events when a process changes state. """ self.build_and_test( 'driver.cpp listener_test.cpp test_listener_event_process_state.cpp', 'test_listener_event_process_state') pass @skipIfRemote @skipIfNoSBHeaders # clang-cl does not support throw or catch (llvm.org/pr24538) @skipIfWindows @expectedFlakeyFreeBSD @expectedFailureAll(oslist=["linux"]) def test_sb_api_listener_resume(self): """ Test that a process can be resumed from a non-main thread. """ self.build_and_test( 'driver.cpp listener_test.cpp test_listener_resume.cpp', 'test_listener_resume') pass def build_and_test(self, sources, test_name, args=None): """ Build LLDB test from sources, and run expecting 0 exit code """ # These tests link against host lldb API. # Compiler's target triple must match liblldb triple # because remote is disabled, we can assume that the os is the same # still need to check architecture if self.getLldbArchitecture() != self.getArchitecture(): self.skipTest( "This test is only run if the target arch is the same as the lldb binary arch") self.inferior = 'inferior_program' self.buildProgram('inferior.cpp', self.inferior) self.addTearDownHook(lambda: os.remove(self.getBuildArtifact(self.inferior))) self.buildDriver(sources, test_name) self.addTearDownHook(lambda: os.remove(self.getBuildArtifact(test_name))) test_exe = self.getBuildArtifact(test_name) self.signBinary(test_exe) exe = [test_exe, self.getBuildArtifact(self.inferior)] env = {self.dylibPath: self.getLLDBLibraryEnvVal()} if 'LLDB_DEBUGSERVER_PATH' in os.environ: env['LLDB_DEBUGSERVER_PATH'] = os.environ['LLDB_DEBUGSERVER_PATH'] if self.TraceOn(): print("Running test %s" % " ".join(exe)) check_call(exe, env=env) else: with open(os.devnull, 'w') as fnull: check_call(exe, env=env, stdout=fnull, stderr=fnull) def build_program(self, sources, program): return self.buildDriver(sources, program) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/decorators.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/decorators.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/decorators.py (revision 337147) @@ -1,812 +1,812 @@ from __future__ import absolute_import from __future__ import print_function # System modules from distutils.version import LooseVersion, StrictVersion from functools import wraps import inspect import os import platform import re import sys import tempfile import subprocess # Third-party modules import six import unittest2 # LLDB modules import use_lldb_suite import lldb from . import configuration from . import test_categories from . import lldbtest_config from lldbsuite.test_event.event_builder import EventBuilder from lldbsuite.support import funcutils from lldbsuite.test import lldbplatform from lldbsuite.test import lldbplatformutil class DecorateMode: Skip, Xfail = range(2) # You can use no_match to reverse the test of the conditional that is used to match keyword # arguments in the skip / xfail decorators. If oslist=["windows", "linux"] skips windows # and linux, oslist=no_match(["windows", "linux"]) skips *unless* windows # or linux. class no_match: def __init__(self, item): self.item = item def _check_expected_version(comparison, expected, actual): def fn_leq(x, y): return x <= y def fn_less(x, y): return x < y def fn_geq(x, y): return x >= y def fn_greater(x, y): return x > y def fn_eq(x, y): return x == y def fn_neq(x, y): return x != y op_lookup = { "==": fn_eq, "=": fn_eq, "!=": fn_neq, "<>": fn_neq, ">": fn_greater, "<": fn_less, ">=": fn_geq, "<=": fn_leq } expected_str = '.'.join([str(x) for x in expected]) actual_str = '.'.join([str(x) for x in actual]) return op_lookup[comparison]( LooseVersion(actual_str), LooseVersion(expected_str)) def _match_decorator_property(expected, actual): if actual is None or expected is None: return True if isinstance(expected, no_match): return not _match_decorator_property(expected.item, actual) elif isinstance(expected, (re._pattern_type,) + six.string_types): return re.search(expected, actual) is not None elif hasattr(expected, "__iter__"): return any([x is not None and _match_decorator_property(x, actual) for x in expected]) else: return expected == actual def expectedFailure(expected_fn, bugnumber=None): def expectedFailure_impl(func): if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception( "Decorator can only be used to decorate a test method") @wraps(func) def wrapper(*args, **kwargs): self = args[0] if funcutils.requires_self(expected_fn): xfail_reason = expected_fn(self) else: xfail_reason = expected_fn() if xfail_reason is not None: if configuration.results_formatter_object is not None: # Mark this test as expected to fail. configuration.results_formatter_object.handle_event( EventBuilder.event_for_mark_test_expected_failure(self)) xfail_func = unittest2.expectedFailure(func) xfail_func(*args, **kwargs) else: func(*args, **kwargs) return wrapper # Some decorators can be called both with no arguments (e.g. @expectedFailureWindows) # or with arguments (e.g. @expectedFailureWindows(compilers=['gcc'])). When called # the first way, the first argument will be the actual function because decorators are # weird like that. So this is basically a check that says "which syntax was the original # function decorated with?" if six.callable(bugnumber): return expectedFailure_impl(bugnumber) else: return expectedFailure_impl def skipTestIfFn(expected_fn, bugnumber=None): def skipTestIfFn_impl(func): if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception( "@skipTestIfFn can only be used to decorate a test method") @wraps(func) def wrapper(*args, **kwargs): self = args[0] if funcutils.requires_self(expected_fn): reason = expected_fn(self) else: reason = expected_fn() if reason is not None: self.skipTest(reason) else: func(*args, **kwargs) return wrapper # Some decorators can be called both with no arguments (e.g. @expectedFailureWindows) # or with arguments (e.g. @expectedFailureWindows(compilers=['gcc'])). When called # the first way, the first argument will be the actual function because decorators are # weird like that. So this is basically a check that says "how was the # decorator used" if six.callable(bugnumber): return skipTestIfFn_impl(bugnumber) else: return skipTestIfFn_impl def _decorateTest(mode, bugnumber=None, oslist=None, hostoslist=None, compiler=None, compiler_version=None, archs=None, triple=None, debug_info=None, swig_version=None, py_version=None, macos_version=None, remote=None): def fn(self): skip_for_os = _match_decorator_property( lldbplatform.translate(oslist), self.getPlatform()) skip_for_hostos = _match_decorator_property( lldbplatform.translate(hostoslist), lldbplatformutil.getHostPlatform()) skip_for_compiler = _match_decorator_property( compiler, self.getCompiler()) and self.expectedCompilerVersion(compiler_version) skip_for_arch = _match_decorator_property( archs, self.getArchitecture()) skip_for_debug_info = _match_decorator_property( debug_info, self.getDebugInfo()) skip_for_triple = _match_decorator_property( triple, lldb.DBG.GetSelectedPlatform().GetTriple()) skip_for_remote = _match_decorator_property( remote, lldb.remote_platform is not None) skip_for_swig_version = ( swig_version is None) or ( not hasattr( lldb, 'swig_version')) or ( _check_expected_version( swig_version[0], swig_version[1], lldb.swig_version)) skip_for_py_version = ( py_version is None) or _check_expected_version( py_version[0], py_version[1], sys.version_info) skip_for_macos_version = (macos_version is None) or ( _check_expected_version( macos_version[0], macos_version[1], platform.mac_ver()[0])) # For the test to be skipped, all specified (e.g. not None) parameters must be True. # An unspecified parameter means "any", so those are marked skip by default. And we skip # the final test if all conditions are True. conditions = [(oslist, skip_for_os, "target o/s"), (hostoslist, skip_for_hostos, "host o/s"), (compiler, skip_for_compiler, "compiler or version"), (archs, skip_for_arch, "architecture"), (debug_info, skip_for_debug_info, "debug info format"), (triple, skip_for_triple, "target triple"), (swig_version, skip_for_swig_version, "swig version"), (py_version, skip_for_py_version, "python version"), (macos_version, skip_for_macos_version, "macOS version"), (remote, skip_for_remote, "platform locality (remote/local)")] reasons = [] final_skip_result = True for this_condition in conditions: final_skip_result = final_skip_result and this_condition[1] if this_condition[0] is not None and this_condition[1]: reasons.append(this_condition[2]) reason_str = None if final_skip_result: mode_str = { DecorateMode.Skip: "skipping", DecorateMode.Xfail: "xfailing"}[mode] if len(reasons) > 0: reason_str = ",".join(reasons) reason_str = "{} due to the following parameter(s): {}".format( mode_str, reason_str) else: reason_str = "{} unconditionally" if bugnumber is not None and not six.callable(bugnumber): reason_str = reason_str + " [" + str(bugnumber) + "]" return reason_str if mode == DecorateMode.Skip: return skipTestIfFn(fn, bugnumber) elif mode == DecorateMode.Xfail: return expectedFailure(fn, bugnumber) else: return None # provide a function to xfail on defined oslist, compiler version, and archs # if none is specified for any argument, that argument won't be checked and thus means for all # for example, # @expectedFailureAll, xfail for all platform/compiler/arch, # @expectedFailureAll(compiler='gcc'), xfail for gcc on all platform/architecture # @expectedFailureAll(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), xfail for gcc>=4.9 on linux with i386 def expectedFailureAll(bugnumber=None, oslist=None, hostoslist=None, compiler=None, compiler_version=None, archs=None, triple=None, debug_info=None, swig_version=None, py_version=None, macos_version=None, remote=None): return _decorateTest(DecorateMode.Xfail, bugnumber=bugnumber, oslist=oslist, hostoslist=hostoslist, compiler=compiler, compiler_version=compiler_version, archs=archs, triple=triple, debug_info=debug_info, swig_version=swig_version, py_version=py_version, macos_version=None, remote=remote) # provide a function to skip on defined oslist, compiler version, and archs # if none is specified for any argument, that argument won't be checked and thus means for all # for example, # @skipIf, skip for all platform/compiler/arch, # @skipIf(compiler='gcc'), skip for gcc on all platform/architecture # @skipIf(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), skip for gcc>=4.9 on linux with i386 def skipIf(bugnumber=None, oslist=None, hostoslist=None, compiler=None, compiler_version=None, archs=None, triple=None, debug_info=None, swig_version=None, py_version=None, macos_version=None, remote=None): return _decorateTest(DecorateMode.Skip, bugnumber=bugnumber, oslist=oslist, hostoslist=hostoslist, compiler=compiler, compiler_version=compiler_version, archs=archs, triple=triple, debug_info=debug_info, swig_version=swig_version, py_version=py_version, macos_version=macos_version, remote=remote) def _skip_for_android(reason, api_levels, archs): def impl(obj): result = lldbplatformutil.match_android_device( obj.getArchitecture(), valid_archs=archs, valid_api_levels=api_levels) return reason if result else None return impl def add_test_categories(cat): """Add test categories to a TestCase method""" cat = test_categories.validate(cat, True) def impl(func): if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception( "@add_test_categories can only be used to decorate a test method") try: if hasattr(func, "categories"): cat.extend(func.categories) setattr(func, "categories", cat) except AttributeError: raise Exception('Cannot assign categories to inline tests.') return func return impl def benchmarks_test(func): """Decorate the item as a benchmarks test.""" def should_skip_benchmarks_test(): return "benchmarks test" # Mark this function as such to separate them from the regular tests. result = skipTestIfFn(should_skip_benchmarks_test)(func) result.__benchmarks_test__ = True return result def no_debug_info_test(func): """Decorate the item as a test what don't use any debug info. If this annotation is specified then the test runner won't generate a separate test for each debug info format. """ if isinstance(func, type) and issubclass(func, unittest2.TestCase): raise Exception( "@no_debug_info_test can only be used to decorate a test method") @wraps(func) def wrapper(self, *args, **kwargs): return func(self, *args, **kwargs) # Mark this function as such to separate them from the regular tests. wrapper.__no_debug_info_test__ = True return wrapper def apple_simulator_test(platform): """ Decorate the test as a test requiring a simulator for a specific platform. Consider that a simulator is available if you have the corresponding SDK installed. The SDK identifiers for simulators are iphonesimulator, appletvsimulator, watchsimulator """ def should_skip_simulator_test(): if lldbplatformutil.getHostPlatform() != 'darwin': return "simulator tests are run only on darwin hosts" try: DEVNULL = open(os.devnull, 'w') output = subprocess.check_output(["xcodebuild", "-showsdks"], stderr=DEVNULL) if re.search('%ssimulator' % platform, output): return None else: return "%s simulator is not supported on this system." % platform except subprocess.CalledProcessError: return "%s is not supported on this system (xcodebuild failed)." % feature return skipTestIfFn(should_skip_simulator_test) def debugserver_test(func): """Decorate the item as a debugserver test.""" def should_skip_debugserver_test(): return "debugserver tests" if configuration.dont_do_debugserver_test else None return skipTestIfFn(should_skip_debugserver_test)(func) def llgs_test(func): """Decorate the item as a lldb-server test.""" def should_skip_llgs_tests(): return "llgs tests" if configuration.dont_do_llgs_test else None return skipTestIfFn(should_skip_llgs_tests)(func) def not_remote_testsuite_ready(func): """Decorate the item as a test which is not ready yet for remote testsuite.""" def is_remote(): return "Not ready for remote testsuite" if lldb.remote_platform else None return skipTestIfFn(is_remote)(func) def expectedFailureOS( oslist, bugnumber=None, compilers=None, debug_info=None, archs=None): return expectedFailureAll( oslist=oslist, bugnumber=bugnumber, compiler=compilers, archs=archs, debug_info=debug_info) def expectedFailureDarwin(bugnumber=None, compilers=None, debug_info=None): # For legacy reasons, we support both "darwin" and "macosx" as OS X # triples. return expectedFailureOS( lldbplatform.darwin_all, bugnumber, compilers, debug_info=debug_info) def expectedFailureAndroid(bugnumber=None, api_levels=None, archs=None): """ Mark a test as xfail for Android. Arguments: bugnumber - The LLVM pr associated with the problem. api_levels - A sequence of numbers specifying the Android API levels for which a test is expected to fail. None means all API level. arch - A sequence of architecture names specifying the architectures for which a test is expected to fail. None means all architectures. """ return expectedFailure( _skip_for_android( "xfailing on android", api_levels, archs), bugnumber) # Flakey tests get two chances to run. If they fail the first time round, the result formatter # makes sure it is run one more time. def expectedFlakey(expected_fn, bugnumber=None): def expectedFailure_impl(func): @wraps(func) def wrapper(*args, **kwargs): self = args[0] if expected_fn(self): # Send event marking test as explicitly eligible for rerunning. if configuration.results_formatter_object is not None: # Mark this test as rerunnable. configuration.results_formatter_object.handle_event( EventBuilder.event_for_mark_test_rerun_eligible(self)) func(*args, **kwargs) return wrapper # Some decorators can be called both with no arguments (e.g. @expectedFailureWindows) # or with arguments (e.g. @expectedFailureWindows(compilers=['gcc'])). When called # the first way, the first argument will be the actual function because decorators are # weird like that. So this is basically a check that says "which syntax was the original # function decorated with?" if six.callable(bugnumber): return expectedFailure_impl(bugnumber) else: return expectedFailure_impl def expectedFlakeyDsym(bugnumber=None): def fn(self): return self.getDebugInfo() == "dwarf" return expectedFlakey(fn, bugnumber) def expectedFlakeyOS(oslist, bugnumber=None, compilers=None): def fn(self): return (self.getPlatform() in oslist and self.expectedCompiler(compilers)) return expectedFlakey(fn, bugnumber) def expectedFlakeyDarwin(bugnumber=None, compilers=None): # For legacy reasons, we support both "darwin" and "macosx" as OS X # triples. return expectedFlakeyOS( lldbplatformutil.getDarwinOSTriples(), bugnumber, compilers) def expectedFlakeyFreeBSD(bugnumber=None, compilers=None): return expectedFlakeyOS(['freebsd'], bugnumber, compilers) def expectedFlakeyLinux(bugnumber=None, compilers=None): return expectedFlakeyOS(['linux'], bugnumber, compilers) def expectedFlakeyNetBSD(bugnumber=None, compilers=None): return expectedFlakeyOS(['netbsd'], bugnumber, compilers) def expectedFlakeyAndroid(bugnumber=None, api_levels=None, archs=None): return expectedFlakey( _skip_for_android( "flakey on android", api_levels, archs), bugnumber) def skipIfOutOfTreeDebugserver(func): """Decorate the item to skip tests if using an out-of-tree debugserver.""" def is_out_of_tree_debugserver(): return "out-of-tree debugserver" if lldbtest_config.out_of_tree_debugserver else None return skipTestIfFn(is_out_of_tree_debugserver)(func) def skipIfRemote(func): """Decorate the item to skip tests if testing remotely.""" def is_remote(): return "skip on remote platform" if lldb.remote_platform else None return skipTestIfFn(is_remote)(func) def skipIfNoSBHeaders(func): """Decorate the item to mark tests that should be skipped when LLDB is built with no SB API headers.""" def are_sb_headers_missing(): if lldbplatformutil.getHostPlatform() == 'darwin': header = os.path.join( os.environ["LLDB_LIB_DIR"], 'LLDB.framework', 'Versions', 'Current', 'Headers', 'LLDB.h') if os.path.exists(header): return None header = os.path.join( os.environ["LLDB_SRC"], "include", "lldb", "API", "LLDB.h") if not os.path.exists(header): return "skip because LLDB.h header not found" return None return skipTestIfFn(are_sb_headers_missing)(func) def skipIfiOSSimulator(func): """Decorate the item to skip tests that should be skipped on the iOS Simulator.""" def is_ios_simulator(): return "skip on the iOS Simulator" if configuration.lldb_platform_name == 'ios-simulator' else None return skipTestIfFn(is_ios_simulator)(func) def skipIfiOS(func): return skipIfPlatform(["ios"])(func) def skipIftvOS(func): return skipIfPlatform(["tvos"])(func) def skipIfwatchOS(func): return skipIfPlatform(["watchos"])(func) def skipIfbridgeOS(func): return skipIfPlatform(["bridgeos"])(func) def skipIfDarwinEmbedded(func): """Decorate the item to skip tests that should be skipped on Darwin armv7/arm64 targets.""" return skipIfPlatform( lldbplatform.translate( lldbplatform.darwin_embedded))(func) def skipIfFreeBSD(func): """Decorate the item to skip tests that should be skipped on FreeBSD.""" return skipIfPlatform(["freebsd"])(func) def skipIfNetBSD(func): """Decorate the item to skip tests that should be skipped on NetBSD.""" return skipIfPlatform(["netbsd"])(func) def skipIfDarwin(func): """Decorate the item to skip tests that should be skipped on Darwin.""" return skipIfPlatform( lldbplatform.translate( lldbplatform.darwin_all))(func) def skipIfLinux(func): """Decorate the item to skip tests that should be skipped on Linux.""" return skipIfPlatform(["linux"])(func) def skipIfWindows(func): """Decorate the item to skip tests that should be skipped on Windows.""" return skipIfPlatform(["windows"])(func) def skipUnlessWindows(func): """Decorate the item to skip tests that should be skipped on any non-Windows platform.""" return skipUnlessPlatform(["windows"])(func) def skipUnlessDarwin(func): """Decorate the item to skip tests that should be skipped on any non Darwin platform.""" return skipUnlessPlatform(lldbplatformutil.getDarwinOSTriples())(func) def skipUnlessGoInstalled(func): """Decorate the item to skip tests when no Go compiler is available.""" def is_go_missing(self): compiler = self.getGoCompilerVersion() if not compiler: return "skipping because go compiler not found" match_version = re.search(r"(\d+\.\d+(\.\d+)?)", compiler) if not match_version: # Couldn't determine version. return "skipping because go version could not be parsed out of {}".format( compiler) else: min_strict_version = StrictVersion("1.4.0") compiler_strict_version = StrictVersion(match_version.group(1)) if compiler_strict_version < min_strict_version: return "skipping because available version ({}) does not meet minimum required version ({})".format( compiler_strict_version, min_strict_version) return None return skipTestIfFn(is_go_missing)(func) def skipIfHostIncompatibleWithRemote(func): """Decorate the item to skip tests if binaries built on this host are incompatible.""" def is_host_incompatible_with_remote(self): host_arch = self.getLldbArchitecture() host_platform = lldbplatformutil.getHostPlatform() target_arch = self.getArchitecture() target_platform = 'darwin' if self.platformIsDarwin() else self.getPlatform() if not (target_arch == 'x86_64' and host_arch == 'i386') and host_arch != target_arch: return "skipping because target %s is not compatible with host architecture %s" % ( target_arch, host_arch) if target_platform != host_platform: return "skipping because target is %s but host is %s" % ( target_platform, host_platform) if lldbplatformutil.match_android_device(target_arch): return "skipping because target is android" return None return skipTestIfFn(is_host_incompatible_with_remote)(func) def skipIfPlatform(oslist): """Decorate the item to skip tests if running on one of the listed platforms.""" # This decorator cannot be ported to `skipIf` yet because it is used on entire # classes, which `skipIf` explicitly forbids. return unittest2.skipIf(lldbplatformutil.getPlatform() in oslist, "skip on %s" % (", ".join(oslist))) def skipUnlessPlatform(oslist): """Decorate the item to skip tests unless running on one of the listed platforms.""" # This decorator cannot be ported to `skipIf` yet because it is used on entire # classes, which `skipIf` explicitly forbids. return unittest2.skipUnless(lldbplatformutil.getPlatform() in oslist, "requires one of %s" % (", ".join(oslist))) def skipIfTargetAndroid(api_levels=None, archs=None): """Decorator to skip tests when the target is Android. Arguments: api_levels - The API levels for which the test should be skipped. If it is None, then the test will be skipped for all API levels. arch - A sequence of architecture names specifying the architectures for which a test is skipped. None means all architectures. """ return skipTestIfFn( _skip_for_android( "skipping for android", api_levels, archs)) def skipUnlessSupportedTypeAttribute(attr): """Decorate the item to skip test unless Clang supports type __attribute__(attr).""" def compiler_doesnt_support_struct_attribute(self): compiler_path = self.getCompiler() f = tempfile.NamedTemporaryFile() cmd = [self.getCompiler(), "-x", "c++", "-c", "-o", f.name, "-"] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) stdout, stderr = p.communicate('struct __attribute__((%s)) Test {};'%attr) if attr in stderr: return "Compiler does not support attribute %s"%(attr) return None return skipTestIfFn(compiler_doesnt_support_struct_attribute) def skipUnlessThreadSanitizer(func): """Decorate the item to skip test unless Clang -fsanitize=thread is supported.""" def is_compiler_clang_with_thread_sanitizer(self): compiler_path = self.getCompiler() compiler = os.path.basename(compiler_path) if not compiler.startswith("clang"): return "Test requires clang as compiler" if lldbplatformutil.getPlatform() == 'windows': return "TSAN tests not compatible with 'windows'" # rdar://28659145 - TSAN tests don't look like they're supported on i386 if self.getArchitecture() == 'i386' and platform.system() == 'Darwin': return "TSAN tests not compatible with i386 targets" f = tempfile.NamedTemporaryFile() cmd = "echo 'int main() {}' | %s -x c -o %s -" % (compiler_path, f.name) if os.popen(cmd).close() is not None: return None # The compiler cannot compile at all, let's *not* skip the test cmd = "echo 'int main() {}' | %s -fsanitize=thread -x c -o %s -" % (compiler_path, f.name) if os.popen(cmd).close() is not None: return "Compiler cannot compile with -fsanitize=thread" return None return skipTestIfFn(is_compiler_clang_with_thread_sanitizer)(func) def skipUnlessUndefinedBehaviorSanitizer(func): """Decorate the item to skip test unless -fsanitize=undefined is supported.""" def is_compiler_clang_with_ubsan(self): # Write out a temp file which exhibits UB. inputf = tempfile.NamedTemporaryFile(suffix='.c', mode='w') inputf.write('int main() { int x = 0; return x / x; }\n') inputf.flush() # We need to write out the object into a named temp file for inspection. outputf = tempfile.NamedTemporaryFile() # Try to compile with ubsan turned on. cmd = '%s -fsanitize=undefined %s -o %s' % (self.getCompiler(), inputf.name, outputf.name) if os.popen(cmd).close() is not None: return "Compiler cannot compile with -fsanitize=undefined" # Check that we actually see ubsan instrumentation in the binary. cmd = 'nm %s' % outputf.name with os.popen(cmd) as nm_output: if '___ubsan_handle_divrem_overflow' not in nm_output.read(): return "Division by zero instrumentation is missing" # Find the ubsan dylib. # FIXME: This check should go away once compiler-rt gains support for __ubsan_on_report. cmd = '%s -fsanitize=undefined -x c - -o - -### 2>&1' % self.getCompiler() with os.popen(cmd) as cc_output: driver_jobs = cc_output.read() m = re.search(r'"([^"]+libclang_rt.ubsan_osx_dynamic.dylib)"', driver_jobs) if not m: return "Could not find the ubsan dylib used by the driver" ubsan_dylib = m.group(1) # Check that the ubsan dylib has special monitor hooks. cmd = 'nm -gU %s' % ubsan_dylib with os.popen(cmd) as nm_output: syms = nm_output.read() if '___ubsan_on_report' not in syms: return "Missing ___ubsan_on_report" if '___ubsan_get_current_report_data' not in syms: return "Missing ___ubsan_get_current_report_data" # OK, this dylib + compiler works for us. return None return skipTestIfFn(is_compiler_clang_with_ubsan)(func) def skipUnlessAddressSanitizer(func): """Decorate the item to skip test unless Clang -fsanitize=thread is supported.""" def is_compiler_with_address_sanitizer(self): compiler_path = self.getCompiler() compiler = os.path.basename(compiler_path) f = tempfile.NamedTemporaryFile() if lldbplatformutil.getPlatform() == 'windows': return "ASAN tests not compatible with 'windows'" cmd = "echo 'int main() {}' | %s -x c -o %s -" % (compiler_path, f.name) if os.popen(cmd).close() is not None: return None # The compiler cannot compile at all, let's *not* skip the test cmd = "echo 'int main() {}' | %s -fsanitize=address -x c -o %s -" % (compiler_path, f.name) if os.popen(cmd).close() is not None: return "Compiler cannot compile with -fsanitize=address" return None return skipTestIfFn(is_compiler_with_address_sanitizer)(func) def skipIfXmlSupportMissing(func): config = lldb.SBDebugger.GetBuildConfiguration() xml = config.GetValueForKey("xml") fail_value = True # More likely to notice if something goes wrong have_xml = xml.GetValueForKey("value").GetBooleanValue(fail_value) return unittest2.skipIf(not have_xml, "requires xml support")(func) def skipIfLLVMTargetMissing(target): config = lldb.SBDebugger.GetBuildConfiguration() targets = config.GetValueForKey("targets").GetValueForKey("value") found = False for i in range(targets.GetSize()): if targets.GetItemAtIndex(i).GetStringValue(99) == target: found = True break - + return unittest2.skipIf(not found, "requires " + target) # Call sysctl on darwin to see if a specified hardware feature is available on this machine. def skipUnlessFeature(feature): def is_feature_enabled(self): if platform.system() == 'Darwin': try: DEVNULL = open(os.devnull, 'w') output = subprocess.check_output(["/usr/sbin/sysctl", feature], stderr=DEVNULL) # If 'feature: 1' was output, then this feature is available and # the test should not be skipped. if re.match('%s: 1\s*' % feature, output): return None else: return "%s is not supported on this system." % feature except subprocess.CalledProcessError: return "%s is not supported on this system." % feature return skipTestIfFn(is_feature_enabled) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/call-function/TestCallStdStringFunction.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/call-function/TestCallStdStringFunction.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/call-function/TestCallStdStringFunction.py (revision 337147) @@ -1,57 +1,57 @@ """ Test calling std::String member functions. """ from __future__ import print_function import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ExprCommandCallFunctionTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Find the line number to break for main.c. self.line = line_number( 'main.cpp', '// Please test these expressions while stopped at this line:') @expectedFailureAll( compiler="icc", bugnumber="llvm.org/pr14437, fails with ICC 13.1") @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr21765") def test_with(self): """Test calling std::String member function.""" self.build() self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) # Some versions of GCC encode two locations for the 'return' statement # in main.cpp lldbutil.run_break_set_by_file_and_line( self, "main.cpp", self.line, num_expected_locations=-1, loc_exact=True) self.runCmd("run", RUN_SUCCEEDED) self.expect("print str", substrs=['Hello world']) # Calling this function now succeeds, but we follow the typedef return type through to # const char *, and thus don't invoke the Summary formatter. - # clang's libstdc++ on ios arm64 inlines std::string::c_str() always; + # clang's libstdc++ on ios arm64 inlines std::string::c_str() always; # skip this part of the test. triple = self.dbg.GetSelectedPlatform().GetTriple() do_cstr_test = True if triple == "arm64-apple-ios" or triple == "arm64-apple-tvos" or triple == "armv7k-apple-watchos" or triple == "arm64-apple-bridgeos": do_cstr_test = False if do_cstr_test: self.expect("print str.c_str()", substrs=['Hello world']) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/call-restarts/TestCallThatRestarts.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/call-restarts/TestCallThatRestarts.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/call-restarts/TestCallThatRestarts.py (revision 337147) @@ -1,166 +1,166 @@ """ Test calling a function that hits a signal set to auto-restart, make sure the call completes. """ from __future__ import print_function import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ExprCommandThatRestartsTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True def setUp(self): # Call super's setUp(). TestBase.setUp(self) self.main_source = "lotta-signals.c" self.main_source_spec = lldb.SBFileSpec(self.main_source) @skipIfFreeBSD # llvm.org/pr19246: intermittent failure @skipIfDarwin # llvm.org/pr19246: intermittent failure @skipIfWindows # Test relies on signals, unsupported on Windows @expectedFlakeyAndroid(bugnumber="llvm.org/pr19246") def test(self): """Test calling function that hits a signal and restarts.""" self.build() self.call_function() def check_after_call(self, num_sigchld): after_call = self.sigchld_no.GetValueAsSigned(-1) self.assertTrue( after_call - self.start_sigchld_no == num_sigchld, "Really got %d SIGCHLD signals through the call." % (num_sigchld)) self.start_sigchld_no = after_call # Check that we are back where we were before: frame = self.thread.GetFrameAtIndex(0) self.assertTrue( self.orig_frame_pc == frame.GetPC(), "Restored the zeroth frame correctly") def call_function(self): - (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self, 'Stop here in main.', self.main_source_spec) # Make sure the SIGCHLD behavior is pass/no-stop/no-notify: return_obj = lldb.SBCommandReturnObject() self.dbg.GetCommandInterpreter().HandleCommand( "process handle SIGCHLD -s 0 -p 1 -n 0", return_obj) self.assertTrue(return_obj.Succeeded(), "Set SIGCHLD to pass, no-stop") # The sigchld_no variable should be 0 at this point. self.sigchld_no = target.FindFirstGlobalVariable("sigchld_no") self.assertTrue( self.sigchld_no.IsValid(), "Got a value for sigchld_no") self.start_sigchld_no = self.sigchld_no.GetValueAsSigned(-1) self.assertTrue( self.start_sigchld_no != -1, "Got an actual value for sigchld_no") options = lldb.SBExpressionOptions() # processing 30 signals takes a while, increase the expression timeout # a bit options.SetTimeoutInMicroSeconds(3000000) # 3s options.SetUnwindOnError(True) frame = self.thread.GetFrameAtIndex(0) # Store away the PC to check that the functions unwind to the right # place after calls self.orig_frame_pc = frame.GetPC() num_sigchld = 30 value = frame.EvaluateExpression( "call_me (%d)" % (num_sigchld), options) self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertTrue(value.GetValueAsSigned(-1) == num_sigchld) self.check_after_call(num_sigchld) # Okay, now try with a breakpoint in the called code in the case where # we are ignoring breakpoint hits. handler_bkpt = target.BreakpointCreateBySourceRegex( "Got sigchld %d.", self.main_source_spec) self.assertTrue(handler_bkpt.GetNumLocations() > 0) options.SetIgnoreBreakpoints(True) options.SetUnwindOnError(True) value = frame.EvaluateExpression( "call_me (%d)" % (num_sigchld), options) self.assertTrue(value.IsValid() and value.GetError().Success()) self.assertTrue(value.GetValueAsSigned(-1) == num_sigchld) self.check_after_call(num_sigchld) # Now set the signal to print but not stop and make sure that calling # still works: self.dbg.GetCommandInterpreter().HandleCommand( "process handle SIGCHLD -s 0 -p 1 -n 1", return_obj) self.assertTrue( return_obj.Succeeded(), "Set SIGCHLD to pass, no-stop, notify") value = frame.EvaluateExpression( "call_me (%d)" % (num_sigchld), options) self.assertTrue(value.IsValid() and value.GetError().Success()) self.assertTrue(value.GetValueAsSigned(-1) == num_sigchld) self.check_after_call(num_sigchld) # Now set this unwind on error to false, and make sure that we still # complete the call: options.SetUnwindOnError(False) value = frame.EvaluateExpression( "call_me (%d)" % (num_sigchld), options) self.assertTrue(value.IsValid() and value.GetError().Success()) self.assertTrue(value.GetValueAsSigned(-1) == num_sigchld) self.check_after_call(num_sigchld) # Okay, now set UnwindOnError to true, and then make the signal behavior to stop # and see that now we do stop at the signal point: self.dbg.GetCommandInterpreter().HandleCommand( "process handle SIGCHLD -s 1 -p 1 -n 1", return_obj) self.assertTrue( return_obj.Succeeded(), "Set SIGCHLD to pass, stop, notify") value = frame.EvaluateExpression( "call_me (%d)" % (num_sigchld), options) self.assertTrue( value.IsValid() and value.GetError().Success() == False) # Set signal handling back to no-stop, and continue and we should end # up back in out starting frame: self.dbg.GetCommandInterpreter().HandleCommand( "process handle SIGCHLD -s 0 -p 1 -n 1", return_obj) self.assertTrue( return_obj.Succeeded(), "Set SIGCHLD to pass, no-stop, notify") error = process.Continue() self.assertTrue( error.Success(), "Continuing after stopping for signal succeeds.") frame = self.thread.GetFrameAtIndex(0) self.assertTrue( frame.GetPC() == self.orig_frame_pc, "Continuing returned to the place we started.") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/call-throws/TestCallThatThrows.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/call-throws/TestCallThatThrows.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/call-throws/TestCallThatThrows.py (revision 337147) @@ -1,104 +1,104 @@ """ Test calling a function that throws an ObjC exception, make sure that it doesn't propagate the exception. """ from __future__ import print_function import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ExprCommandWithThrowTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) self.main_source = "call-throws.m" self.main_source_spec = lldb.SBFileSpec(self.main_source) @skipUnlessDarwin def test(self): """Test calling a function that throws and ObjC exception.""" self.build() self.call_function() def check_after_call(self): # Check that we are back where we were before: frame = self.thread.GetFrameAtIndex(0) self.assertTrue( self.orig_frame_pc == frame.GetPC(), "Restored the zeroth frame correctly") def call_function(self): """Test calling function that throws.""" - (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self, 'I am about to throw.', self.main_source_spec) options = lldb.SBExpressionOptions() options.SetUnwindOnError(True) frame = self.thread.GetFrameAtIndex(0) # Store away the PC to check that the functions unwind to the right # place after calls self.orig_frame_pc = frame.GetPC() value = frame.EvaluateExpression("[my_class callMeIThrow]", options) self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success() == False) self.check_after_call() # Okay, now try with a breakpoint in the called code in the case where # we are ignoring breakpoint hits. handler_bkpt = target.BreakpointCreateBySourceRegex( "I felt like it", self.main_source_spec) self.assertTrue(handler_bkpt.GetNumLocations() > 0) options.SetIgnoreBreakpoints(True) options.SetUnwindOnError(True) value = frame.EvaluateExpression("[my_class callMeIThrow]", options) self.assertTrue( value.IsValid() and value.GetError().Success() == False) self.check_after_call() # Now set the ObjC language breakpoint and make sure that doesn't # interfere with the call: exception_bkpt = target.BreakpointCreateForException( lldb.eLanguageTypeObjC, False, True) self.assertTrue(exception_bkpt.GetNumLocations() > 0) options.SetIgnoreBreakpoints(True) options.SetUnwindOnError(True) value = frame.EvaluateExpression("[my_class callMeIThrow]", options) self.assertTrue( value.IsValid() and value.GetError().Success() == False) self.check_after_call() # Now turn off exception trapping, and call a function that catches the exceptions, # and make sure the function actually completes, and we get the right # value: options.SetTrapExceptions(False) value = frame.EvaluateExpression("[my_class iCatchMyself]", options) self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertTrue(value.GetValueAsUnsigned() == 57) self.check_after_call() options.SetTrapExceptions(True) # Now set this unwind on error to false, and make sure that we stop # where the exception was thrown options.SetUnwindOnError(False) value = frame.EvaluateExpression("[my_class callMeIThrow]", options) self.assertTrue( value.IsValid() and value.GetError().Success() == False) self.check_after_call() Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/char/TestExprsChar.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/char/TestExprsChar.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/char/TestExprsChar.py (revision 337147) @@ -1,70 +1,70 @@ from __future__ import print_function import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ExprCharTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) self.main_source = "main.cpp" self.main_source_spec = lldb.SBFileSpec(self.main_source) def do_test(self, dictionary=None): """These basic expression commands should work as expected.""" self.build(dictionary=dictionary) - (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, '// Break here', self.main_source_spec) frame = thread.GetFrameAtIndex(0) value = frame.EvaluateExpression("foo(c)") self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertEqual(value.GetValueAsSigned(0), 1) value = frame.EvaluateExpression("foo(sc)") self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertEqual(value.GetValueAsSigned(0), 2) value = frame.EvaluateExpression("foo(uc)") self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertEqual(value.GetValueAsSigned(0), 3) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr21765") def test_default_char(self): self.do_test() @expectedFailureAll( archs=[ "arm", "aarch64", "powerpc64le", "s390x"], bugnumber="llvm.org/pr23069") @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr21765") def test_signed_char(self): self.do_test(dictionary={'CFLAGS_EXTRAS': '-fsigned-char'}) @expectedFailureAll( archs=[ "i[3-6]86", "x86_64", "arm64", 'armv7', 'armv7k'], bugnumber="llvm.org/pr23069, ") @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr21765") @expectedFailureAll(triple='mips*', bugnumber="llvm.org/pr23069") def test_unsigned_char(self): self.do_test(dictionary={'CFLAGS_EXTRAS': '-funsigned-char'}) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/fixits/TestFixIts.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/fixits/TestFixIts.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/fixits/TestFixIts.py (revision 337147) @@ -1,72 +1,72 @@ """ Test calling an expression with errors that a FixIt can fix. """ from __future__ import print_function import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ExprCommandWithFixits(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) self.main_source = "main.cpp" self.main_source_spec = lldb.SBFileSpec(self.main_source) @skipUnlessDarwin def test_with_target(self): """Test calling expressions with errors that can be fixed by the FixIts.""" self.build() self.try_expressions() def test_with_dummy_target(self): """Test calling expressions in the dummy target with errors that can be fixed by the FixIts.""" ret_val = lldb.SBCommandReturnObject() result = self.dbg.GetCommandInterpreter().HandleCommand("expression ((1 << 16) - 1))", ret_val) self.assertEqual(result, lldb.eReturnStatusSuccessFinishResult, "The expression was successful.") self.assertTrue("Fix-it applied" in ret_val.GetError(), "Found the applied FixIt.") def try_expressions(self): """Test calling expressions with errors that can be fixed by the FixIts.""" - (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self, 'Stop here to evaluate expressions', self.main_source_spec) options = lldb.SBExpressionOptions() options.SetAutoApplyFixIts(True) frame = self.thread.GetFrameAtIndex(0) # Try with one error: value = frame.EvaluateExpression("my_pointer.first", options) self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertTrue(value.GetValueAsUnsigned() == 10) # Try with two errors: two_error_expression = "my_pointer.second->a" value = frame.EvaluateExpression(two_error_expression, options) self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertTrue(value.GetValueAsUnsigned() == 20) # Now turn off the fixits, and the expression should fail: options.SetAutoApplyFixIts(False) value = frame.EvaluateExpression(two_error_expression, options) self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Fail()) error_string = value.GetError().GetCString() self.assertTrue( error_string.find("fixed expression suggested:") != -1, "Fix was suggested") self.assertTrue( error_string.find("my_pointer->second.a") != -1, "Fix was right") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/issue_11588/Test11588.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/issue_11588/Test11588.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/issue_11588/Test11588.py (revision 337147) @@ -1,69 +1,69 @@ """ Test the solution to issue 11581. valobj.AddressOf() returns None when an address is expected in a SyntheticChildrenProvider """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class Issue11581TestCase(TestBase): mydir = TestBase.compute_mydir(__file__) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778") def test_11581_commands(self): # This is the function to remove the custom commands in order to have a # clean slate for the next test case. def cleanup(): self.runCmd('type synthetic clear', check=False) # Execute the cleanup function during test case tear down. self.addTearDownHook(cleanup) """valobj.AddressOf() should return correct values.""" self.build() - (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, - 'Set breakpoint here.', + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + 'Set breakpoint here.', lldb.SBFileSpec("main.cpp", False)) self.runCmd("command script import --allow-reload s11588.py") self.runCmd( "type synthetic add --python-class s11588.Issue11581SyntheticProvider StgClosure") self.expect("expr --show-types -- *((StgClosure*)(r14-1))", substrs=["(StgClosure) $", "(StgClosure *) &$", "0x", "addr = ", "load_address = "]) # register r14 is an x86_64 extension let's skip this part of the test # if we are on a different architecture if self.getArchitecture() == 'x86_64': target = lldb.debugger.GetSelectedTarget() process = target.GetProcess() frame = process.GetSelectedThread().GetSelectedFrame() pointer = frame.FindVariable("r14") addr = pointer.GetValueAsUnsigned(0) self.assertTrue(addr != 0, "could not read pointer to StgClosure") addr = addr - 1 self.runCmd("register write r14 %d" % addr) self.expect( "register read r14", substrs=[ "0x", hex(addr)[ 2:].rstrip("L")]) # Remove trailing 'L' if it exists self.expect("expr --show-types -- *(StgClosure*)$r14", substrs=["(StgClosure) $", "(StgClosure *) &$", "0x", "addr = ", "load_address = ", hex(addr)[2:].rstrip("L"), str(addr)]) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/pr35310/TestExprsBug35310.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/pr35310/TestExprsBug35310.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/pr35310/TestExprsBug35310.py (revision 337147) @@ -1,40 +1,40 @@ from __future__ import print_function import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ExprBug35310(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) self.main_source = "main.cpp" self.main_source_spec = lldb.SBFileSpec(self.main_source) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr21765") def test_issue35310(self): """Test invoking functions with non-standard linkage names. The GNU abi_tag extension used by libstdc++ is a common source of these, but they could originate from other reasons as well. """ self.build() - (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, '// Break here', self.main_source_spec) frame = thread.GetFrameAtIndex(0) value = frame.EvaluateExpression("a.test_abi_tag()") self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertEqual(value.GetValueAsSigned(0), 1) value = frame.EvaluateExpression("a.test_asm_name()") self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertEqual(value.GetValueAsSigned(0), 2) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/save_jit_objects/TestSaveJITObjects.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/save_jit_objects/TestSaveJITObjects.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/save_jit_objects/TestSaveJITObjects.py (revision 337147) @@ -1,53 +1,53 @@ """ Test that LLDB can emit JIT objects when the appropriate setting is enabled """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class SaveJITObjectsTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def enumerateJITFiles(self): return [f for f in os.listdir(self.getBuildDir()) if f.startswith("jit")] - + def countJITFiles(self): return len(self.enumerateJITFiles()) def cleanJITFiles(self): for j in self.enumerateJITFiles(): os.remove(j) return @expectedFailureAll(oslist=["windows"]) def test_save_jit_objects(self): self.build() os.chdir(self.getBuildDir()) src_file = "main.c" src_file_spec = lldb.SBFileSpec(src_file) - + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( self, "break", src_file_spec) frame = thread.frames[0] self.cleanJITFiles() frame.EvaluateExpression("(void*)malloc(0x1)") self.assertTrue(self.countJITFiles() == 0, "No files emitted with save-jit-objects=false") self.runCmd("settings set target.save-jit-objects true") frame.EvaluateExpression("(void*)malloc(0x1)") jit_files_count = self.countJITFiles() self.cleanJITFiles() self.assertTrue(jit_files_count != 0, "At least one file emitted with save-jit-objects=true") process.Kill() os.chdir(self.getSourceDir()) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/unwind_expression/TestUnwindExpression.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/unwind_expression/TestUnwindExpression.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/unwind_expression/TestUnwindExpression.py (revision 337147) @@ -1,103 +1,103 @@ """ Test stopping at a breakpoint in an expression, and unwinding from there. """ from __future__ import print_function import unittest2 import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class UnwindFromExpressionTest(TestBase): mydir = TestBase.compute_mydir(__file__) main_spec = lldb.SBFileSpec("main.cpp", False) def build_and_run_to_bkpt(self): self.build() - (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + (target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self, "// Set a breakpoint here to get started", self.main_spec) # Next set a breakpoint in this function, set up Expression options to stop on # breakpoint hits, and call the function. self.fun_bkpt = self.target().BreakpointCreateBySourceRegex( "// Stop inside the function here.", self.main_spec) self.assertTrue(self.fun_bkpt, VALID_BREAKPOINT) @no_debug_info_test @expectedFailureAll(bugnumber="llvm.org/pr33164") def test_conditional_bktp(self): """ Test conditional breakpoint handling in the IgnoreBreakpoints = False case """ self.build_and_run_to_bkpt() self.fun_bkpt.SetCondition("0") # Should not get hit options = lldb.SBExpressionOptions() options.SetIgnoreBreakpoints(False) options.SetUnwindOnError(False) main_frame = self.thread.GetFrameAtIndex(0) val = main_frame.EvaluateExpression("second_function(47)", options) self.assertTrue( val.GetError().Success(), "We did complete the execution.") self.assertEquals(47, val.GetValueAsSigned()) @add_test_categories(['pyapi']) @expectedFailureAll(oslist=["windows"]) def test_unwind_expression(self): """Test unwinding from an expression.""" self.build_and_run_to_bkpt() # Run test with varying one thread timeouts to also test the halting # logic in the IgnoreBreakpoints = False case self.do_unwind_test(self.thread, self.fun_bkpt, 1000) self.do_unwind_test(self.thread, self.fun_bkpt, 100000) def do_unwind_test(self, thread, bkpt, timeout): # # Use Python API to evaluate expressions while stopped in a stack frame. # main_frame = thread.GetFrameAtIndex(0) options = lldb.SBExpressionOptions() options.SetIgnoreBreakpoints(False) options.SetUnwindOnError(False) options.SetOneThreadTimeoutInMicroSeconds(timeout) val = main_frame.EvaluateExpression("a_function_to_call()", options) self.assertTrue( val.GetError().Fail(), "We did not complete the execution.") error_str = val.GetError().GetCString() self.assertTrue( "Execution was interrupted, reason: breakpoint" in error_str, "And the reason was right.") thread = lldbutil.get_one_thread_stopped_at_breakpoint( self.process(), bkpt) self.assertTrue( thread.IsValid(), "We are indeed stopped at our breakpoint") # Now unwind the expression, and make sure we got back to where we # started. error = thread.UnwindInnermostExpression() self.assertTrue(error.Success(), "We succeeded in unwinding") cur_frame = thread.GetFrameAtIndex(0) self.assertTrue( cur_frame.IsEqual(main_frame), "We got back to the main frame.") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/xvalue/TestXValuePrinting.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/xvalue/TestXValuePrinting.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/xvalue/TestXValuePrinting.py (revision 337147) @@ -1,37 +1,37 @@ from __future__ import print_function import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ExprXValuePrintingTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) self.main_source = "main.cpp" self.main_source_spec = lldb.SBFileSpec(self.main_source) def do_test(self, dictionary=None): """Printing an xvalue should work.""" self.build(dictionary=dictionary) - (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, '// Break here', self.main_source_spec) frame = thread.GetFrameAtIndex(0) value = frame.EvaluateExpression("foo().data") self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertEqual(value.GetValueAsSigned(), 1234) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr21765") def test(self): self.do_test() Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/TestBadAddressBreakpoints.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/TestBadAddressBreakpoints.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/address_breakpoints/TestBadAddressBreakpoints.py (revision 337147) @@ -1,50 +1,50 @@ """ Test that breakpoints set on a bad address say they are bad. """ from __future__ import print_function import os import time import re import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * class BadAddressBreakpointTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True def test_bad_address_breakpoints(self): """Test that breakpoints set on a bad address say they are bad.""" self.build() self.address_breakpoints() def setUp(self): # Call super's setUp(). TestBase.setUp(self) def address_breakpoints(self): """Test that breakpoints set on a bad address say they are bad.""" target, process, thread, bkpt = \ - lldbutil.run_to_source_breakpoint(self, + lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here", lldb.SBFileSpec("main.c")) # Now see if we can read from 0. If I can't do that, I don't # have a good way to know what an illegal address is... error = lldb.SBError() ptr = process.ReadPointerFromMemory(0x0, error) if not error.Success(): bkpt = target.BreakpointCreateByAddress(0x0) for bp_loc in bkpt: self.assertTrue(bp_loc.IsResolved() == False) else: self.fail( "Could not find an illegal address at which to set a bad breakpoint.") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/auto_continue/TestBreakpointAutoContinue.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/auto_continue/TestBreakpointAutoContinue.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/auto_continue/TestBreakpointAutoContinue.py (revision 337147) @@ -1,104 +1,104 @@ """ Test that the breakpoint auto-continue flag works correctly. """ from __future__ import print_function import os import time import re import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * class BreakpointAutoContinue(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True def test_breakpoint_auto_continue(self): """Make sure the auto continue continues with no other complications""" self.build() self.simple_auto_continue() def test_auto_continue_with_command(self): """Add a command, make sure the command gets run""" self.build() self.auto_continue_with_command() def test_auto_continue_on_location(self): """Set auto-continue on a location and make sure only that location continues""" self.build() self.auto_continue_location() - def make_target_and_bkpt(self, additional_options=None, num_expected_loc=1, + def make_target_and_bkpt(self, additional_options=None, num_expected_loc=1, pattern="Set a breakpoint here"): exe = self.getBuildArtifact("a.out") self.target = self.dbg.CreateTarget(exe) self.assertTrue(self.target.IsValid(), "Target is not valid") - + extra_options_txt = "--auto-continue 1 " if additional_options: extra_options_txt += additional_options - bpno = lldbutil.run_break_set_by_source_regexp(self, pattern, - extra_options = extra_options_txt, + bpno = lldbutil.run_break_set_by_source_regexp(self, pattern, + extra_options = extra_options_txt, num_expected_locations = num_expected_loc) return bpno def launch_it (self, expected_state): error = lldb.SBError() launch_info = lldb.SBLaunchInfo(None) launch_info.SetWorkingDirectory(self.get_process_working_directory()) process = self.target.Launch(launch_info, error) self.assertTrue(error.Success(), "Launch failed.") state = process.GetState() self.assertEqual(state, expected_state, "Didn't get expected state") return process def setUp(self): # Call super's setUp(). TestBase.setUp(self) def simple_auto_continue(self): bpno = self.make_target_and_bkpt() process = self.launch_it(lldb.eStateExited) bkpt = self.target.FindBreakpointByID(bpno) self.assertEqual(bkpt.GetHitCount(), 2, "Should have run through the breakpoint twice") def auto_continue_with_command(self): bpno = self.make_target_and_bkpt("-N BKPT -C 'break modify --auto-continue 0 BKPT'") process = self.launch_it(lldb.eStateStopped) state = process.GetState() self.assertEqual(state, lldb.eStateStopped, "Process should be stopped") bkpt = self.target.FindBreakpointByID(bpno) threads = lldbutil.get_threads_stopped_at_breakpoint(process, bkpt) self.assertEqual(len(threads), 1, "There was a thread stopped at our breakpoint") self.assertEqual(bkpt.GetHitCount(), 2, "Should have hit the breakpoint twice") def auto_continue_location(self): bpno = self.make_target_and_bkpt(pattern="Set a[^ ]* breakpoint here", num_expected_loc=2) bkpt = self.target.FindBreakpointByID(bpno) bkpt.SetAutoContinue(False) loc = lldb.SBBreakpointLocation() for i in range(0,2): func_name = bkpt.location[i].GetAddress().function.name if func_name == "main": loc = bkpt.location[i] self.assertTrue(loc.IsValid(), "Didn't find a location in main") loc.SetAutoContinue(True) process = self.launch_it(lldb.eStateStopped) threads = lldbutil.get_threads_stopped_at_breakpoint(process, bkpt) self.assertEqual(len(threads), 1, "Didn't get one thread stopped at our breakpoint") func_name = threads[0].frame[0].function.name self.assertEqual(func_name, "call_me") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/TestBreakpointLocations.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/TestBreakpointLocations.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_locations/TestBreakpointLocations.py (revision 337147) @@ -1,200 +1,200 @@ """ Test breakpoint commands for a breakpoint ID with multiple locations. """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class BreakpointLocationsTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528") def test_enable(self): """Test breakpoint enable/disable for a breakpoint ID with multiple locations.""" self.build() self.breakpoint_locations_test() def test_shadowed_cond_options(self): """Test that options set on the breakpoint and location behave correctly.""" self.build() self.shadowed_bkpt_cond_test() def test_shadowed_command_options(self): """Test that options set on the breakpoint and location behave correctly.""" self.build() self.shadowed_bkpt_command_test() def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Find the line number to break inside main(). self.line = line_number('main.c', '// Set break point at this line.') def set_breakpoint (self): exe = self.getBuildArtifact("a.out") target = self.dbg.CreateTarget(exe) self.assertTrue(target, "Target %s is not valid"%(exe)) # This should create a breakpoint with 3 locations. - + bkpt = target.BreakpointCreateByLocation("main.c", self.line) # The breakpoint list should show 3 locations. self.assertEqual(bkpt.GetNumLocations(), 3, "Wrong number of locations") - + self.expect( "breakpoint list -f", "Breakpoint locations shown correctly", substrs=[ "1: file = 'main.c', line = %d, exact_match = 0, locations = 3" % self.line], patterns=[ "where = a.out`func_inlined .+unresolved, hit count = 0", "where = a.out`main .+\[inlined\].+unresolved, hit count = 0"]) - return bkpt + return bkpt def shadowed_bkpt_cond_test(self): """Test that options set on the breakpoint and location behave correctly.""" # Breakpoint option propagation from bkpt to loc used to be done the first time # a breakpoint location option was specifically set. After that the other options # on that location would stop tracking the breakpoint. That got fixed, and this test # makes sure only the option touched is affected. bkpt = self.set_breakpoint() bkpt_cond = "1 == 0" bkpt.SetCondition(bkpt_cond) self.assertEqual(bkpt.GetCondition(), bkpt_cond,"Successfully set condition") self.assertTrue(bkpt.location[0].GetCondition() == bkpt.GetCondition(), "Conditions are the same") # Now set a condition on the locations, make sure that this doesn't effect the bkpt: bkpt_loc_1_cond = "1 == 1" bkpt.location[0].SetCondition(bkpt_loc_1_cond) self.assertEqual(bkpt.location[0].GetCondition(), bkpt_loc_1_cond, "Successfully changed location condition") self.assertNotEqual(bkpt.GetCondition(), bkpt_loc_1_cond, "Changed location changed Breakpoint condition") self.assertEqual(bkpt.location[1].GetCondition(), bkpt_cond, "Changed another location's condition") # Now make sure that setting one options doesn't fix the value of another: bkpt.SetIgnoreCount(10) self.assertEqual(bkpt.GetIgnoreCount(), 10, "Set the ignore count successfully") self.assertEqual(bkpt.location[0].GetIgnoreCount(), 10, "Location doesn't track top-level bkpt.") # Now make sure resetting the condition to "" resets the tracking: bkpt.location[0].SetCondition("") bkpt_new_cond = "1 == 3" bkpt.SetCondition(bkpt_new_cond) self.assertEqual(bkpt.location[0].GetCondition(), bkpt_new_cond, "Didn't go back to tracking condition") def shadowed_bkpt_command_test(self): """Test that options set on the breakpoint and location behave correctly.""" # Breakpoint option propagation from bkpt to loc used to be done the first time # a breakpoint location option was specifically set. After that the other options # on that location would stop tracking the breakpoint. That got fixed, and this test # makes sure only the option touched is affected. bkpt = self.set_breakpoint() commands = ["AAAAAA", "BBBBBB", "CCCCCC"] str_list = lldb.SBStringList() str_list.AppendList(commands, len(commands)) - + bkpt.SetCommandLineCommands(str_list) cmd_list = lldb.SBStringList() bkpt.GetCommandLineCommands(cmd_list) list_size = str_list.GetSize() self.assertEqual(cmd_list.GetSize() , list_size, "Added the right number of commands") for i in range(0,list_size): self.assertEqual(str_list.GetStringAtIndex(i), cmd_list.GetStringAtIndex(i), "Mismatched commands.") commands = ["DDDDDD", "EEEEEE", "FFFFFF", "GGGGGG"] loc_list = lldb.SBStringList() loc_list.AppendList(commands, len(commands)) bkpt.location[1].SetCommandLineCommands(loc_list) loc_cmd_list = lldb.SBStringList() bkpt.location[1].GetCommandLineCommands(loc_cmd_list) loc_list_size = loc_list.GetSize() - + # Check that the location has the right commands: self.assertEqual(loc_cmd_list.GetSize() , loc_list_size, "Added the right number of commands to location") for i in range(0,loc_list_size): self.assertEqual(loc_list.GetStringAtIndex(i), loc_cmd_list.GetStringAtIndex(i), "Mismatched commands.") # Check that we didn't mess up the breakpoint level commands: self.assertEqual(cmd_list.GetSize() , list_size, "Added the right number of commands") for i in range(0,list_size): self.assertEqual(str_list.GetStringAtIndex(i), cmd_list.GetStringAtIndex(i), "Mismatched commands.") # And check we didn't mess up another location: untouched_loc_cmds = lldb.SBStringList() bkpt.location[0].GetCommandLineCommands(untouched_loc_cmds) self.assertEqual(untouched_loc_cmds.GetSize() , 0, "Changed the wrong location") def breakpoint_locations_test(self): """Test breakpoint enable/disable for a breakpoint ID with multiple locations.""" self.set_breakpoint() # The 'breakpoint disable 3.*' command should fail gracefully. self.expect("breakpoint disable 3.*", "Disabling an invalid breakpoint should fail gracefully", error=True, startstr="error: '3' is not a valid breakpoint ID.") # The 'breakpoint disable 1.*' command should disable all 3 locations. self.expect( "breakpoint disable 1.*", "All 3 breakpoint locatons disabled correctly", startstr="3 breakpoints disabled.") # Run the program. self.runCmd("run", RUN_SUCCEEDED) # We should not stopped on any breakpoint at all. self.expect("process status", "No stopping on any disabled breakpoint", patterns=["^Process [0-9]+ exited with status = 0"]) # The 'breakpoint enable 1.*' command should enable all 3 breakpoints. self.expect( "breakpoint enable 1.*", "All 3 breakpoint locatons enabled correctly", startstr="3 breakpoints enabled.") # The 'breakpoint disable 1.1' command should disable 1 location. self.expect( "breakpoint disable 1.1", "1 breakpoint locatons disabled correctly", startstr="1 breakpoints disabled.") # Run the program again. We should stop on the two breakpoint # locations. self.runCmd("run", RUN_SUCCEEDED) # Stopped once. self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, substrs=["stop reason = breakpoint 1."]) # Continue the program, there should be another stop. self.runCmd("process continue") # Stopped again. self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT, substrs=["stop reason = breakpoint 1."]) # At this point, 1.1 has a hit count of 0 and the other a hit count of # 1". self.expect( "breakpoint list -f", "The breakpoints should report correct hit counts", patterns=[ "1\.1: .+ unresolved, hit count = 0 +Options: disabled", "1\.2: .+ resolved, hit count = 1", "1\.3: .+ resolved, hit count = 1"]) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_names/TestBreakpointNames.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_names/TestBreakpointNames.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_names/TestBreakpointNames.py (revision 337147) @@ -1,367 +1,367 @@ """ Test breakpoint names. """ from __future__ import print_function import os import time import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class BreakpointNames(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True @add_test_categories(['pyapi']) def test_setting_names(self): """Use Python APIs to test that we can set breakpoint names.""" self.build() self.setup_target() self.do_check_names() def test_illegal_names(self): """Use Python APIs to test that we don't allow illegal names.""" self.build() self.setup_target() self.do_check_illegal_names() def test_using_names(self): """Use Python APIs to test that operations on names works correctly.""" self.build() self.setup_target() self.do_check_using_names() def test_configuring_names(self): """Use Python APIs to test that configuring options on breakpoint names works correctly.""" self.build() self.make_a_dummy_name() self.setup_target() self.do_check_configuring_names() def test_configuring_permissions_sb(self): """Use Python APIs to test that configuring permissions on names works correctly.""" self.build() self.setup_target() self.do_check_configuring_permissions_sb() def test_configuring_permissions_cli(self): """Use Python APIs to test that configuring permissions on names works correctly.""" self.build() self.setup_target() self.do_check_configuring_permissions_cli() def setup_target(self): exe = self.getBuildArtifact("a.out") # Create a targets we are making breakpoint in and copying to: self.target = self.dbg.CreateTarget(exe) self.assertTrue(self.target, VALID_TARGET) self.main_file_spec = lldb.SBFileSpec(os.path.join(self.getSourceDir(), "main.c")) - + def check_name_in_target(self, bkpt_name): name_list = lldb.SBStringList() self.target.GetBreakpointNames(name_list) found_it = False for name in name_list: if name == bkpt_name: found_it = True break self.assertTrue(found_it, "Didn't find the name %s in the target's name list:"%(bkpt_name)) - + def setUp(self): # Call super's setUp(). TestBase.setUp(self) # These are the settings we're going to be putting into names & breakpoints: self.bp_name_string = "ABreakpoint" self.is_one_shot = True self.ignore_count = 1000 self.condition = "1 == 2" self.auto_continue = True self.tid = 0xaaaa self.tidx = 10 self.thread_name = "Fooey" self.queue_name = "Blooey" self.cmd_list = lldb.SBStringList() self.cmd_list.AppendString("frame var") self.cmd_list.AppendString("bt") self.help_string = "I do something interesting" def do_check_names(self): """Use Python APIs to check that we can set & retrieve breakpoint names""" bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10) bkpt_name = "ABreakpoint" other_bkpt_name = "_AnotherBreakpoint" # Add a name and make sure we match it: success = bkpt.AddName(bkpt_name) self.assertTrue(success, "We couldn't add a legal name to a breakpoint.") matches = bkpt.MatchesName(bkpt_name) self.assertTrue(matches, "We didn't match the name we just set") - + # Make sure we don't match irrelevant names: matches = bkpt.MatchesName("NotABreakpoint") self.assertTrue(not matches, "We matched a name we didn't set.") # Make sure the name is also in the target: self.check_name_in_target(bkpt_name) - + # Add another name, make sure that works too: bkpt.AddName(other_bkpt_name) matches = bkpt.MatchesName(bkpt_name) self.assertTrue(matches, "Adding a name means we didn't match the name we just set") self.check_name_in_target(other_bkpt_name) # Remove the name and make sure we no longer match it: bkpt.RemoveName(bkpt_name) matches = bkpt.MatchesName(bkpt_name) self.assertTrue(not matches,"We still match a name after removing it.") # Make sure the name list has the remaining name: name_list = lldb.SBStringList() bkpt.GetNames(name_list) num_names = name_list.GetSize() self.assertTrue(num_names == 1, "Name list has %d items, expected 1."%(num_names)) - + name = name_list.GetStringAtIndex(0) self.assertTrue(name == other_bkpt_name, "Remaining name was: %s expected %s."%(name, other_bkpt_name)) def do_check_illegal_names(self): """Use Python APIs to check that we reject illegal names.""" bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10) bad_names = ["-CantStartWithADash", "1CantStartWithANumber", "^CantStartWithNonAlpha", "CantHave-ADash", "Cant Have Spaces"] for bad_name in bad_names: success = bkpt.AddName(bad_name) self.assertTrue(not success,"We allowed an illegal name: %s"%(bad_name)) bp_name = lldb.SBBreakpointName(self.target, bad_name) self.assertFalse(bp_name.IsValid(), "We made a breakpoint name with an illegal name: %s"%(bad_name)); retval =lldb.SBCommandReturnObject() self.dbg.GetCommandInterpreter().HandleCommand("break set -n whatever -N '%s'"%(bad_name), retval) self.assertTrue(not retval.Succeeded(), "break set succeeded with: illegal name: %s"%(bad_name)) def do_check_using_names(self): """Use Python APIs to check names work in place of breakpoint ID's.""" - + bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10) bkpt_name = "ABreakpoint" other_bkpt_name= "_AnotherBreakpoint" # Add a name and make sure we match it: success = bkpt.AddName(bkpt_name) self.assertTrue(success, "We couldn't add a legal name to a breakpoint.") bkpts = lldb.SBBreakpointList(self.target) self.target.FindBreakpointsByName(bkpt_name, bkpts) self.assertTrue(bkpts.GetSize() == 1, "One breakpoint matched.") found_bkpt = bkpts.GetBreakpointAtIndex(0) self.assertTrue(bkpt.GetID() == found_bkpt.GetID(),"The right breakpoint.") retval = lldb.SBCommandReturnObject() self.dbg.GetCommandInterpreter().HandleCommand("break disable %s"%(bkpt_name), retval) self.assertTrue(retval.Succeeded(), "break disable failed with: %s."%(retval.GetError())) self.assertTrue(not bkpt.IsEnabled(), "We didn't disable the breakpoint.") # Also make sure we don't apply commands to non-matching names: self.dbg.GetCommandInterpreter().HandleCommand("break modify --one-shot 1 %s"%(other_bkpt_name), retval) self.assertTrue(retval.Succeeded(), "break modify failed with: %s."%(retval.GetError())) self.assertTrue(not bkpt.IsOneShot(), "We applied one-shot to the wrong breakpoint.") def check_option_values(self, bp_object): self.assertEqual(bp_object.IsOneShot(), self.is_one_shot, "IsOneShot") self.assertEqual(bp_object.GetIgnoreCount(), self.ignore_count, "IgnoreCount") self.assertEqual(bp_object.GetCondition(), self.condition, "Condition") self.assertEqual(bp_object.GetAutoContinue(), self.auto_continue, "AutoContinue") self.assertEqual(bp_object.GetThreadID(), self.tid, "Thread ID") self.assertEqual(bp_object.GetThreadIndex(), self.tidx, "Thread Index") self.assertEqual(bp_object.GetThreadName(), self.thread_name, "Thread Name") self.assertEqual(bp_object.GetQueueName(), self.queue_name, "Queue Name") set_cmds = lldb.SBStringList() bp_object.GetCommandLineCommands(set_cmds) self.assertEqual(set_cmds.GetSize(), self.cmd_list.GetSize(), "Size of command line commands") for idx in range(0, set_cmds.GetSize()): self.assertEqual(self.cmd_list.GetStringAtIndex(idx), set_cmds.GetStringAtIndex(idx), "Command %d"%(idx)) def make_a_dummy_name(self): "This makes a breakpoint name in the dummy target to make sure it gets copied over" dummy_target = self.dbg.GetDummyTarget() self.assertTrue(dummy_target.IsValid(), "Dummy target was not valid.") def cleanup (): self.dbg.GetDummyTarget().DeleteBreakpointName(self.bp_name_string) # Execute the cleanup function during test case tear down. self.addTearDownHook(cleanup) # Now find it in the dummy target, and make sure these settings took: bp_name = lldb.SBBreakpointName(dummy_target, self.bp_name_string) # Make sure the name is right: self.assertTrue (bp_name.GetName() == self.bp_name_string, "Wrong bp_name: %s"%(bp_name.GetName())) bp_name.SetOneShot(self.is_one_shot) bp_name.SetIgnoreCount(self.ignore_count) bp_name.SetCondition(self.condition) bp_name.SetAutoContinue(self.auto_continue) bp_name.SetThreadID(self.tid) bp_name.SetThreadIndex(self.tidx) bp_name.SetThreadName(self.thread_name) bp_name.SetQueueName(self.queue_name) bp_name.SetCommandLineCommands(self.cmd_list) # Now look it up again, and make sure it got set correctly. bp_name = lldb.SBBreakpointName(dummy_target, self.bp_name_string) self.assertTrue(bp_name.IsValid(), "Failed to make breakpoint name.") self.check_option_values(bp_name) def do_check_configuring_names(self): """Use Python APIs to check that configuring breakpoint names works correctly.""" other_bp_name_string = "AnotherBreakpointName" cl_bp_name_string = "CLBreakpointName" # Now find the version copied in from the dummy target, and make sure these settings took: bp_name = lldb.SBBreakpointName(self.target, self.bp_name_string) self.assertTrue(bp_name.IsValid(), "Failed to make breakpoint name.") self.check_option_values(bp_name) # Now add this name to a breakpoint, and make sure it gets configured properly bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10) success = bkpt.AddName(self.bp_name_string) self.assertTrue(success, "Couldn't add this name to the breakpoint") self.check_option_values(bkpt) # Now make a name from this breakpoint, and make sure the new name is properly configured: new_name = lldb.SBBreakpointName(bkpt, other_bp_name_string) self.assertTrue(new_name.IsValid(), "Couldn't make a valid bp_name from a breakpoint.") self.check_option_values(bkpt) # Now change the name's option and make sure it gets propagated to # the breakpoint: new_auto_continue = not self.auto_continue bp_name.SetAutoContinue(new_auto_continue) self.assertEqual(bp_name.GetAutoContinue(), new_auto_continue, "Couldn't change auto-continue on the name") self.assertEqual(bkpt.GetAutoContinue(), new_auto_continue, "Option didn't propagate to the breakpoint.") - + # Now make this same breakpoint name - but from the command line - cmd_str = "breakpoint name configure %s -o %d -i %d -c '%s' -G %d -t %d -x %d -T '%s' -q '%s' -H '%s'"%(cl_bp_name_string, - self.is_one_shot, - self.ignore_count, - self.condition, + cmd_str = "breakpoint name configure %s -o %d -i %d -c '%s' -G %d -t %d -x %d -T '%s' -q '%s' -H '%s'"%(cl_bp_name_string, + self.is_one_shot, + self.ignore_count, + self.condition, self.auto_continue, self.tid, self.tidx, self.thread_name, self.queue_name, self.help_string) for cmd in self.cmd_list: cmd_str += " -C '%s'"%(cmd) - + self.runCmd(cmd_str, check=True) # Now look up this name again and check its options: cl_name = lldb.SBBreakpointName(self.target, cl_bp_name_string) self.check_option_values(cl_name) # Also check the help string: self.assertEqual(self.help_string, cl_name.GetHelpString(), "Help string didn't match") # Change the name and make sure that works: new_help = "I do something even more interesting" cl_name.SetHelpString(new_help) self.assertEqual(new_help, cl_name.GetHelpString(), "SetHelpString didn't") - + # We should have three names now, make sure the target can list them: name_list = lldb.SBStringList() self.target.GetBreakpointNames(name_list) for name_string in [self.bp_name_string, other_bp_name_string, cl_bp_name_string]: self.assertTrue(name_string in name_list, "Didn't find %s in names"%(name_string)) - # Delete the name from the current target. Make sure that works and deletes the + # Delete the name from the current target. Make sure that works and deletes the # name from the breakpoint as well: self.target.DeleteBreakpointName(self.bp_name_string) name_list.Clear() self.target.GetBreakpointNames(name_list) self.assertTrue(self.bp_name_string not in name_list, "Didn't delete %s from a real target"%(self.bp_name_string)) # Also make sure the name got removed from breakpoints holding it: self.assertFalse(bkpt.MatchesName(self.bp_name_string), "Didn't remove the name from the breakpoint.") # Test that deleting the name we injected into the dummy target works (there's also a # cleanup that will do this, but that won't test the result... dummy_target = self.dbg.GetDummyTarget() dummy_target.DeleteBreakpointName(self.bp_name_string) name_list.Clear() dummy_target.GetBreakpointNames(name_list) self.assertTrue(self.bp_name_string not in name_list, "Didn't delete %s from the dummy target"%(self.bp_name_string)) # Also make sure the name got removed from breakpoints holding it: self.assertFalse(bkpt.MatchesName(self.bp_name_string), "Didn't remove the name from the breakpoint.") - + def check_permission_results(self, bp_name): self.assertEqual(bp_name.GetAllowDelete(), False, "Didn't set allow delete.") protected_bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10) protected_id = protected_bkpt.GetID() unprotected_bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10) unprotected_id = unprotected_bkpt.GetID() success = protected_bkpt.AddName(self.bp_name_string) self.assertTrue(success, "Couldn't add this name to the breakpoint") self.target.DisableAllBreakpoints() self.assertEqual(protected_bkpt.IsEnabled(), True, "Didnt' keep breakpoint from being disabled") self.assertEqual(unprotected_bkpt.IsEnabled(), False, "Protected too many breakpoints from disabling.") # Try from the command line too: unprotected_bkpt.SetEnabled(True) result = lldb.SBCommandReturnObject() self.dbg.GetCommandInterpreter().HandleCommand("break disable", result) self.assertTrue(result.Succeeded()) self.assertEqual(protected_bkpt.IsEnabled(), True, "Didnt' keep breakpoint from being disabled") self.assertEqual(unprotected_bkpt.IsEnabled(), False, "Protected too many breakpoints from disabling.") self.target.DeleteAllBreakpoints() bkpt = self.target.FindBreakpointByID(protected_id) self.assertTrue(bkpt.IsValid(), "Didn't keep the breakpoint from being deleted.") bkpt = self.target.FindBreakpointByID(unprotected_id) self.assertFalse(bkpt.IsValid(), "Protected too many breakpoints from deletion.") # Remake the unprotected breakpoint and try again from the command line: unprotected_bkpt = self.target.BreakpointCreateByLocation(self.main_file_spec, 10) unprotected_id = unprotected_bkpt.GetID() self.dbg.GetCommandInterpreter().HandleCommand("break delete -f", result) self.assertTrue(result.Succeeded()) bkpt = self.target.FindBreakpointByID(protected_id) self.assertTrue(bkpt.IsValid(), "Didn't keep the breakpoint from being deleted.") bkpt = self.target.FindBreakpointByID(unprotected_id) self.assertFalse(bkpt.IsValid(), "Protected too many breakpoints from deletion.") def do_check_configuring_permissions_sb(self): bp_name = lldb.SBBreakpointName(self.target, self.bp_name_string) # Make a breakpoint name with delete disallowed: bp_name = lldb.SBBreakpointName(self.target, self.bp_name_string) self.assertTrue(bp_name.IsValid(), "Failed to make breakpoint name for valid name.") bp_name.SetAllowDelete(False) bp_name.SetAllowDisable(False) bp_name.SetAllowList(False) self.check_permission_results(bp_name) def do_check_configuring_permissions_cli(self): # Make the name with the right options using the command line: self.runCmd("breakpoint name configure -L 0 -D 0 -A 0 %s"%(self.bp_name_string), check=True) # Now look up the breakpoint we made, and check that it works. bp_name = lldb.SBBreakpointName(self.target, self.bp_name_string) self.assertTrue(bp_name.IsValid(), "Didn't make a breakpoint name we could find.") self.check_permission_results(bp_name) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/hardware_breakpoints/hardware_breakpoint_on_multiple_threads/TestHWBreakMultiThread.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/hardware_breakpoints/hardware_breakpoint_on_multiple_threads/TestHWBreakMultiThread.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/hardware_breakpoints/hardware_breakpoint_on_multiple_threads/TestHWBreakMultiThread.py (revision 337147) @@ -1,110 +1,110 @@ """ Test hardware breakpoints for multiple threads. """ from __future__ import print_function import os import time import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil # Hardware breakpoints are supported only by platforms mentioned in oslist. @skipUnlessPlatform(oslist=['linux']) class HardwareBreakpointMultiThreadTestCase(TestBase): NO_DEBUG_INFO_TESTCASE = True mydir = TestBase.compute_mydir(__file__) # LLDB supports hardware breakpoints for arm and aarch64 architectures. @skipIf(archs=no_match(['arm', 'aarch64'])) @expectedFailureAndroid def test_hw_break_set_delete_multi_thread(self): self.build() - self.setTearDownCleanup() + self.setTearDownCleanup() self.break_multi_thread('delete') # LLDB supports hardware breakpoints for arm and aarch64 architectures. @skipIf(archs=no_match(['arm', 'aarch64'])) @expectedFailureAndroid def test_hw_break_set_disable_multi_thread(self): self.build() - self.setTearDownCleanup() + self.setTearDownCleanup() self.break_multi_thread('disable') def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Our simple source filename. self.source = 'main.cpp' # Find the line number to break inside main(). self.first_stop = line_number( self.source, 'Starting thread creation with hardware breakpoint set') def break_multi_thread(self, removal_type): """Test that lldb hardware breakpoints work for multiple threads.""" self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) # Stop in main before creating any threads. lldbutil.run_break_set_by_file_and_line( self, None, self.first_stop, num_expected_locations=1) # Run the program. self.runCmd("run", RUN_SUCCEEDED) # We should be stopped again due to the breakpoint. # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) # Now set a hardware breakpoint in thread function. self.expect("breakpoint set -b hw_break_function --hardware", substrs=[ 'Breakpoint', 'hw_break_function', 'address = 0x']) # We should stop in hw_break_function function for 4 threads. count = 0 while count < 2 : - + self.runCmd("process continue") # We should be stopped in hw_break_function # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=[ 'stop reason = breakpoint', 'hw_break_function']) # Continue the loop and test that we are stopped 4 times. count += 1 if removal_type == 'delete': self.runCmd("settings set auto-confirm true") # Now 'breakpoint delete' should just work fine without confirmation # prompt from the command interpreter. self.expect("breakpoint delete", startstr="All breakpoints removed") # Restore the original setting of auto-confirm. self.runCmd("settings clear auto-confirm") elif removal_type == 'disable': self.expect("breakpoint disable", startstr="All breakpoints disabled.") # Continue. Program should exit without stopping anywhere. self.runCmd("process continue") # Process should have stopped and exited with status = 0 self.expect("process status", PROCESS_STOPPED, patterns=['Process .* exited with status = 0']) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/serialize/TestBreakpointSerialization.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/serialize/TestBreakpointSerialization.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/serialize/TestBreakpointSerialization.py (revision 337147) @@ -1,288 +1,284 @@ """ Test breakpoint serialization. """ from __future__ import print_function import os import time import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class BreakpointSerialization(TestBase): mydir = TestBase.compute_mydir(__file__) @add_test_categories(['pyapi']) def test_resolvers(self): """Use Python APIs to test that we serialize resolvers.""" self.build() self.setup_targets_and_cleanup() self.do_check_resolvers() def test_filters(self): """Use Python APIs to test that we serialize search filters correctly.""" self.build() self.setup_targets_and_cleanup() self.do_check_filters() def test_options(self): """Use Python APIs to test that we serialize breakpoint options correctly.""" self.build() self.setup_targets_and_cleanup() self.do_check_options() def test_appending(self): """Use Python APIs to test that we serialize breakpoint options correctly.""" self.build() self.setup_targets_and_cleanup() self.do_check_appending() def test_name_filters(self): """Use python APIs to test that reading in by name works correctly.""" self.build() self.setup_targets_and_cleanup() self.do_check_names() def setup_targets_and_cleanup(self): def cleanup (): self.RemoveTempFile(self.bkpts_file_path) if self.orig_target.IsValid(): self.dbg.DeleteTarget(self.orig_target) self.dbg.DeleteTarget(self.copy_target) self.addTearDownHook(cleanup) self.RemoveTempFile(self.bkpts_file_path) exe = self.getBuildArtifact("a.out") # Create the targets we are making breakpoints in and copying them to: self.orig_target = self.dbg.CreateTarget(exe) self.assertTrue(self.orig_target, VALID_TARGET) - + self.copy_target = self.dbg.CreateTarget(exe) self.assertTrue(self.copy_target, VALID_TARGET) def setUp(self): # Call super's setUp(). TestBase.setUp(self) self.bkpts_file_path = self.getBuildArtifact("breakpoints.json") self.bkpts_file_spec = lldb.SBFileSpec(self.bkpts_file_path) def check_equivalence(self, source_bps, do_write = True): error = lldb.SBError() if (do_write): error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, source_bps) self.assertTrue(error.Success(), "Failed writing breakpoints to file: %s."%(error.GetCString())) copy_bps = lldb.SBBreakpointList(self.copy_target) error = self.copy_target.BreakpointsCreateFromFile(self.bkpts_file_spec, copy_bps) self.assertTrue(error.Success(), "Failed reading breakpoints from file: %s"%(error.GetCString())) num_source_bps = source_bps.GetSize() num_copy_bps = copy_bps.GetSize() self.assertTrue(num_source_bps == num_copy_bps, "Didn't get same number of input and output breakpoints - orig: %d copy: %d"%(num_source_bps, num_copy_bps)) - + for i in range(0, num_source_bps): source_bp = source_bps.GetBreakpointAtIndex(i) source_desc = lldb.SBStream() source_bp.GetDescription(source_desc, False) source_text = source_desc.GetData() # I am assuming here that the breakpoints will get written out in breakpoint ID order, and # read back in ditto. That is true right now, and I can't see any reason to do it differently # but if we do we can go to writing the breakpoints one by one, or sniffing the descriptions to # see which one is which. copy_id = source_bp.GetID() copy_bp = copy_bps.FindBreakpointByID(copy_id) self.assertTrue(copy_bp.IsValid(), "Could not find copy breakpoint %d."%(copy_id)) copy_desc = lldb.SBStream() copy_bp.GetDescription(copy_desc, False) copy_text = copy_desc.GetData() # These two should be identical. # print ("Source text for %d is %s."%(i, source_text)) self.assertTrue (source_text == copy_text, "Source and dest breakpoints are not identical: \nsource: %s\ndest: %s"%(source_text, copy_text)) def do_check_resolvers(self): """Use Python APIs to check serialization of breakpoint resolvers""" empty_module_list = lldb.SBFileSpecList() empty_cu_list = lldb.SBFileSpecList() blubby_file_spec = lldb.SBFileSpec(os.path.join(self.getSourceDir(), "blubby.c")) # It isn't actually important for these purposes that these breakpoint # actually have locations. source_bps = lldb.SBBreakpointList(self.orig_target) source_bps.Append(self.orig_target.BreakpointCreateByLocation("blubby.c", 666)) # Make sure we do one breakpoint right: self.check_equivalence(source_bps) source_bps.Clear() source_bps.Append(self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list)) source_bps.Append(self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeFull, empty_module_list,empty_cu_list)) source_bps.Append(self.orig_target.BreakpointCreateBySourceRegex("dont really care", blubby_file_spec)) - + # And some number greater than one: self.check_equivalence(source_bps) def do_check_filters(self): """Use Python APIs to check serialization of breakpoint filters.""" module_list = lldb.SBFileSpecList() module_list.Append(lldb.SBFileSpec("SomeBinary")) module_list.Append(lldb.SBFileSpec("SomeOtherBinary")) cu_list = lldb.SBFileSpecList() cu_list.Append(lldb.SBFileSpec("SomeCU.c")) cu_list.Append(lldb.SBFileSpec("AnotherCU.c")) cu_list.Append(lldb.SBFileSpec("ThirdCU.c")) blubby_file_spec = lldb.SBFileSpec(os.path.join(self.getSourceDir(), "blubby.c")) # It isn't actually important for these purposes that these breakpoint # actually have locations. source_bps = lldb.SBBreakpointList(self.orig_target) bkpt = self.orig_target.BreakpointCreateByLocation(blubby_file_spec, 666, 0, module_list) source_bps.Append(bkpt) # Make sure we do one right: self.check_equivalence(source_bps) source_bps.Clear() bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeAuto, module_list, cu_list) source_bps.Append(bkpt) bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeFull, module_list, cu_list) source_bps.Append(bkpt) bkpt = self.orig_target.BreakpointCreateBySourceRegex("dont really care", blubby_file_spec) source_bps.Append(bkpt) # And some number greater than one: self.check_equivalence(source_bps) def do_check_options(self): """Use Python APIs to check serialization of breakpoint options.""" empty_module_list = lldb.SBFileSpecList() empty_cu_list = lldb.SBFileSpecList() blubby_file_spec = lldb.SBFileSpec(os.path.join(self.getSourceDir(), "blubby.c")) # It isn't actually important for these purposes that these breakpoint # actually have locations. source_bps = lldb.SBBreakpointList(self.orig_target) bkpt = self.orig_target.BreakpointCreateByLocation("blubby.c", 666) bkpt.SetEnabled(False) bkpt.SetOneShot(True) bkpt.SetThreadID(10) source_bps.Append(bkpt) - + # Make sure we get one right: self.check_equivalence(source_bps) source_bps.Clear() bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list) bkpt.SetIgnoreCount(10) bkpt.SetThreadName("grubby") source_bps.Append(bkpt) bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeFull, empty_module_list,empty_cu_list) bkpt.SetCondition("something != something_else") bkpt.SetQueueName("grubby") bkpt.AddName("FirstName") bkpt.AddName("SecondName") bkpt.SetScriptCallbackBody('\tprint("I am a function that prints.")\n\tprint("I don\'t do anything else")\n') source_bps.Append(bkpt) bkpt = self.orig_target.BreakpointCreateBySourceRegex("dont really care", blubby_file_spec) cmd_list = lldb.SBStringList() cmd_list.AppendString("frame var") cmd_list.AppendString("thread backtrace") bkpt.SetCommandLineCommands(cmd_list) source_bps.Append(bkpt) self.check_equivalence(source_bps) def do_check_appending(self): """Use Python APIs to check appending to already serialized options.""" empty_module_list = lldb.SBFileSpecList() empty_cu_list = lldb.SBFileSpecList() blubby_file_spec = lldb.SBFileSpec(os.path.join(self.getSourceDir(), "blubby.c")) # It isn't actually important for these purposes that these breakpoint # actually have locations. all_bps = lldb.SBBreakpointList(self.orig_target) source_bps = lldb.SBBreakpointList(self.orig_target) bkpt = self.orig_target.BreakpointCreateByLocation("blubby.c", 666) bkpt.SetEnabled(False) bkpt.SetOneShot(True) bkpt.SetThreadID(10) source_bps.Append(bkpt) all_bps.Append(bkpt) - + error = lldb.SBError() error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, source_bps) self.assertTrue(error.Success(), "Failed writing breakpoints to file: %s."%(error.GetCString())) source_bps.Clear() bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeAuto, empty_module_list, empty_cu_list) bkpt.SetIgnoreCount(10) bkpt.SetThreadName("grubby") source_bps.Append(bkpt) all_bps.Append(bkpt) bkpt = self.orig_target.BreakpointCreateByName("blubby", lldb.eFunctionNameTypeFull, empty_module_list,empty_cu_list) bkpt.SetCondition("something != something_else") bkpt.SetQueueName("grubby") bkpt.AddName("FirstName") bkpt.AddName("SecondName") source_bps.Append(bkpt) all_bps.Append(bkpt) error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, source_bps, True) self.assertTrue(error.Success(), "Failed appending breakpoints to file: %s."%(error.GetCString())) self.check_equivalence(all_bps) def do_check_names(self): bkpt = self.orig_target.BreakpointCreateByLocation("blubby.c", 666) good_bkpt_name = "GoodBreakpoint" write_bps = lldb.SBBreakpointList(self.orig_target) bkpt.AddName(good_bkpt_name) write_bps.Append(bkpt) - + error = lldb.SBError() error = self.orig_target.BreakpointsWriteToFile(self.bkpts_file_spec, write_bps) self.assertTrue(error.Success(), "Failed writing breakpoints to file: %s."%(error.GetCString())) copy_bps = lldb.SBBreakpointList(self.copy_target) names_list = lldb.SBStringList() names_list.AppendString("NoSuchName") error = self.copy_target.BreakpointsCreateFromFile(self.bkpts_file_spec, names_list, copy_bps) self.assertTrue(error.Success(), "Failed reading breakpoints from file: %s"%(error.GetCString())) self.assertTrue(copy_bps.GetSize() == 0, "Found breakpoints with a nonexistent name.") names_list.AppendString(good_bkpt_name) error = self.copy_target.BreakpointsCreateFromFile(self.bkpts_file_spec, names_list, copy_bps) self.assertTrue(error.Success(), "Failed reading breakpoints from file: %s"%(error.GetCString())) self.assertTrue(copy_bps.GetSize() == 1, "Found the matching breakpoint.") - - - - Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/step_over_breakpoint/TestStepOverBreakpoint.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/step_over_breakpoint/TestStepOverBreakpoint.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/breakpoint/step_over_breakpoint/TestStepOverBreakpoint.py (revision 337147) @@ -1,119 +1,119 @@ """ Test that breakpoints do not affect stepping. -Check for correct StopReason when stepping to the line with breakpoint +Check for correct StopReason when stepping to the line with breakpoint which chould be eStopReasonBreakpoint in general, -and eStopReasonPlanComplete when breakpoint's condition fails. +and eStopReasonPlanComplete when breakpoint's condition fails. """ from __future__ import print_function import unittest2 import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class StepOverBreakpointsTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): TestBase.setUp(self) - + self.build() exe = self.getBuildArtifact("a.out") src = lldb.SBFileSpec("main.cpp") # Create a target by the debugger. self.target = self.dbg.CreateTarget(exe) self.assertTrue(self.target, VALID_TARGET) # Setup four breakpoints, two of them with false condition self.line1 = line_number('main.cpp', "breakpoint_1") self.line4 = line_number('main.cpp', "breakpoint_4") - self.breakpoint1 = self.target.BreakpointCreateByLocation(src, self.line1) + self.breakpoint1 = self.target.BreakpointCreateByLocation(src, self.line1) self.assertTrue( self.breakpoint1 and self.breakpoint1.GetNumLocations() == 1, VALID_BREAKPOINT) self.breakpoint2 = self.target.BreakpointCreateBySourceRegex("breakpoint_2", src) self.breakpoint2.GetLocationAtIndex(0).SetCondition('false') self.breakpoint3 = self.target.BreakpointCreateBySourceRegex("breakpoint_3", src) self.breakpoint3.GetLocationAtIndex(0).SetCondition('false') self.breakpoint4 = self.target.BreakpointCreateByLocation(src, self.line4) # Start debugging self.process = self.target.LaunchSimple( None, None, self.get_process_working_directory()) self.assertIsNotNone(self.process, PROCESS_IS_VALID) self.thread = lldbutil.get_one_thread_stopped_at_breakpoint(self.process, self.breakpoint1) self.assertIsNotNone(self.thread, "Didn't stop at breakpoint 1.") - def test_step_instruction(self): + def test_step_instruction(self): # Count instructions between breakpoint_1 and breakpoint_4 contextList = self.target.FindFunctions('main', lldb.eFunctionNameTypeAuto) self.assertEquals(contextList.GetSize(), 1) symbolContext = contextList.GetContextAtIndex(0) function = symbolContext.GetFunction() self.assertTrue(function) instructions = function.GetInstructions(self.target) addr_1 = self.breakpoint1.GetLocationAtIndex(0).GetAddress() addr_4 = self.breakpoint4.GetLocationAtIndex(0).GetAddress() # if third argument is true then the count will be the number of # instructions on which a breakpoint can be set. # start = addr_1, end = addr_4, canSetBreakpoint = True steps_expected = instructions.GetInstructionsCount(addr_1, addr_4, True) step_count = 0 # Step from breakpoint_1 to breakpoint_4 while True: self.thread.StepInstruction(True) step_count = step_count + 1 self.assertEquals(self.process.GetState(), lldb.eStateStopped) self.assertTrue(self.thread.GetStopReason() == lldb.eStopReasonPlanComplete or self.thread.GetStopReason() == lldb.eStopReasonBreakpoint) if (self.thread.GetStopReason() == lldb.eStopReasonBreakpoint) : # we should not stop on breakpoint_2 and _3 because they have false condition self.assertEquals(self.thread.GetFrameAtIndex(0).GetLineEntry().GetLine(), self.line4) # breakpoint_2 and _3 should not affect step count self.assertTrue(step_count >= steps_expected) break # Run the process until termination self.process.Continue() self.assertEquals(self.process.GetState(), lldb.eStateExited) @skipIf(bugnumber="llvm.org/pr31972", hostoslist=["windows"]) def test_step_over(self): #lldb.DBG.EnableLog("lldb", ["step","breakpoint"]) - + self.thread.StepOver() # We should be stopped at the breakpoint_2 line with stop plan complete reason self.assertEquals(self.process.GetState(), lldb.eStateStopped) self.assertEquals(self.thread.GetStopReason(), lldb.eStopReasonPlanComplete) self.thread.StepOver() # We should be stopped at the breakpoint_3 line with stop plan complete reason self.assertEquals(self.process.GetState(), lldb.eStateStopped) self.assertEquals(self.thread.GetStopReason(), lldb.eStopReasonPlanComplete) self.thread.StepOver() # We should be stopped at the breakpoint_4 self.assertEquals(self.process.GetState(), lldb.eStateStopped) self.assertEquals(self.thread.GetStopReason(), lldb.eStopReasonBreakpoint) thread1 = lldbutil.get_one_thread_stopped_at_breakpoint(self.process, self.breakpoint4) self.assertEquals(self.thread, thread1, "Didn't stop at breakpoint 4.") # Check that stepping does not affect breakpoint's hit count self.assertEquals(self.breakpoint1.GetHitCount(), 1) self.assertEquals(self.breakpoint2.GetHitCount(), 0) self.assertEquals(self.breakpoint3.GetHitCount(), 0) self.assertEquals(self.breakpoint4.GetHitCount(), 1) # Run the process until termination self.process.Continue() self.assertEquals(self.process.GetState(), lldb.eStateExited) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py (revision 337147) @@ -1,280 +1,320 @@ """ Test the lldb command line completion mechanism. """ from __future__ import print_function import os import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbplatform from lldbsuite.test import lldbutil class CommandLineCompletionTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True @classmethod def classCleanup(cls): """Cleanup the test byproducts.""" try: os.remove("child_send.txt") os.remove("child_read.txt") except: pass @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_at(self): """Test that 'at' completes to 'attach '.""" self.complete_from_to('at', 'attach ') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_de(self): """Test that 'de' completes to 'detach '.""" self.complete_from_to('de', 'detach ') @skipIfFreeBSD # timing out on the FreeBSD buildbot + def test_frame_variable(self): + self.build() + self.main_source = "main.cpp" + self.main_source_spec = lldb.SBFileSpec(self.main_source) + self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + '// Break here', self.main_source_spec) + self.assertEquals(process.GetState(), lldb.eStateStopped) + # FIXME: This pulls in the debug information to make the completions work, + # but the completions should also work without. + self.runCmd("frame variable fooo") + + self.complete_from_to('frame variable fo', + 'frame variable fooo') + self.complete_from_to('frame variable fooo.', + 'frame variable fooo.') + self.complete_from_to('frame variable fooo.dd', + 'frame variable fooo.dd') + + self.complete_from_to('frame variable ptr_fooo->', + 'frame variable ptr_fooo->') + self.complete_from_to('frame variable ptr_fooo->dd', + 'frame variable ptr_fooo->dd') + + self.complete_from_to('frame variable cont', + 'frame variable container') + self.complete_from_to('frame variable container.', + 'frame variable container.MemberVar') + self.complete_from_to('frame variable container.Mem', + 'frame variable container.MemberVar') + + self.complete_from_to('frame variable ptr_cont', + 'frame variable ptr_container') + self.complete_from_to('frame variable ptr_container->', + 'frame variable ptr_container->MemberVar') + self.complete_from_to('frame variable ptr_container->Mem', + 'frame variable ptr_container->MemberVar') + + @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_process_attach_dash_dash_con(self): """Test that 'process attach --con' completes to 'process attach --continue '.""" self.complete_from_to( 'process attach --con', 'process attach --continue ') # @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_infinite_loop_while_completing(self): """Test that 'process print hello\' completes to itself and does not infinite loop.""" self.complete_from_to('process print hello\\', 'process print hello\\', turn_off_re_match=True) @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_watchpoint_co(self): """Test that 'watchpoint co' completes to 'watchpoint command '.""" self.complete_from_to('watchpoint co', 'watchpoint command ') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_watchpoint_command_space(self): """Test that 'watchpoint command ' completes to ['add', 'delete', 'list'].""" self.complete_from_to( 'watchpoint command ', [ 'add', 'delete', 'list']) @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_watchpoint_command_a(self): """Test that 'watchpoint command a' completes to 'watchpoint command add '.""" self.complete_from_to( 'watchpoint command a', 'watchpoint command add ') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_watchpoint_set_ex(self): """Test that 'watchpoint set ex' completes to 'watchpoint set expression '.""" self.complete_from_to( 'watchpoint set ex', 'watchpoint set expression ') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_watchpoint_set_var(self): """Test that 'watchpoint set var' completes to 'watchpoint set variable '.""" self.complete_from_to('watchpoint set var', 'watchpoint set variable ') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_help_fi(self): """Test that 'help fi' completes to ['file', 'finish'].""" self.complete_from_to( 'help fi', [ 'file', 'finish']) @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_help_watchpoint_s(self): """Test that 'help watchpoint s' completes to 'help watchpoint set '.""" self.complete_from_to('help watchpoint s', 'help watchpoint set ') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_append_target_er(self): """Test that 'settings append target.er' completes to 'settings append target.error-path'.""" self.complete_from_to( 'settings append target.er', 'settings append target.error-path') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_insert_after_target_en(self): """Test that 'settings insert-after target.env' completes to 'settings insert-after target.env-vars'.""" self.complete_from_to( 'settings insert-after target.env', 'settings insert-after target.env-vars') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_insert_before_target_en(self): """Test that 'settings insert-before target.env' completes to 'settings insert-before target.env-vars'.""" self.complete_from_to( 'settings insert-before target.env', 'settings insert-before target.env-vars') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_replace_target_ru(self): """Test that 'settings replace target.ru' completes to 'settings replace target.run-args'.""" self.complete_from_to( 'settings replace target.ru', 'settings replace target.run-args') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_s(self): """Test that 'settings s' completes to ['set', 'show'].""" self.complete_from_to( 'settings s', [ 'set', 'show']) @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_set_th(self): """Test that 'settings set thread-f' completes to 'settings set thread-format'.""" self.complete_from_to('settings set thread-f', 'settings set thread-format') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_s_dash(self): """Test that 'settings set -' completes to 'settings set -g'.""" self.complete_from_to('settings set -', 'settings set -g') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_clear_th(self): """Test that 'settings clear thread-f' completes to 'settings clear thread-format'.""" self.complete_from_to( 'settings clear thread-f', 'settings clear thread-format') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_set_ta(self): """Test that 'settings set ta' completes to 'settings set target.'.""" self.complete_from_to( 'settings set target.ma', 'settings set target.max-') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_set_target_exec(self): """Test that 'settings set target.exec' completes to 'settings set target.exec-search-paths '.""" self.complete_from_to( 'settings set target.exec', 'settings set target.exec-search-paths') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_set_target_pr(self): """Test that 'settings set target.pr' completes to [ 'target.prefer-dynamic-value', 'target.process.'].""" self.complete_from_to('settings set target.pr', ['target.prefer-dynamic-value', 'target.process.']) @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_set_target_process(self): """Test that 'settings set target.process' completes to 'settings set target.process.'.""" self.complete_from_to( 'settings set target.process', 'settings set target.process.') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_set_target_process_dot(self): """Test that 'settings set target.process.t' completes to 'settings set target.process.thread.'.""" self.complete_from_to( 'settings set target.process.t', 'settings set target.process.thread.') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_settings_set_target_process_thread_dot(self): """Test that 'settings set target.process.thread.' completes to [ 'target.process.thread.step-avoid-regexp', 'target.process.thread.trace-thread'].""" self.complete_from_to('settings set target.process.thread.', ['target.process.thread.step-avoid-regexp', 'target.process.thread.trace-thread']) @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_target_space(self): """Test that 'target ' completes to ['create', 'delete', 'list', 'modules', 'select', 'stop-hook', 'variable'].""" self.complete_from_to('target ', ['create', 'delete', 'list', 'modules', 'select', 'stop-hook', 'variable']) @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_target_create_dash_co(self): """Test that 'target create --co' completes to 'target variable --core '.""" self.complete_from_to('target create --co', 'target create --core ') @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_target_va(self): """Test that 'target va' completes to 'target variable '.""" self.complete_from_to('target va', 'target variable ') def test_command_argument_completion(self): """Test completion of command arguments""" self.complete_from_to("watchpoint set variable -", ["-w", "-s"]) self.complete_from_to('watchpoint set variable -w', 'watchpoint set variable -w ') self.complete_from_to("watchpoint set variable --", ["--watch", "--size"]) self.complete_from_to("watchpoint set variable --w", "watchpoint set variable --watch") self.complete_from_to('watchpoint set variable -w ', ['read', 'write', 'read_write']) self.complete_from_to("watchpoint set variable --watch ", ["read", "write", "read_write"]) self.complete_from_to("watchpoint set variable --watch w", "watchpoint set variable --watch write") self.complete_from_to('watchpoint set variable -w read_', 'watchpoint set variable -w read_write') # Now try the same thing with a variable name (non-option argument) to # test that getopts arg reshuffling doesn't confuse us. self.complete_from_to("watchpoint set variable foo -", ["-w", "-s"]) self.complete_from_to('watchpoint set variable foo -w', 'watchpoint set variable foo -w ') self.complete_from_to("watchpoint set variable foo --", ["--watch", "--size"]) self.complete_from_to("watchpoint set variable foo --w", "watchpoint set variable foo --watch") self.complete_from_to('watchpoint set variable foo -w ', ['read', 'write', 'read_write']) self.complete_from_to("watchpoint set variable foo --watch ", ["read", "write", "read_write"]) self.complete_from_to("watchpoint set variable foo --watch w", "watchpoint set variable foo --watch write") self.complete_from_to('watchpoint set variable foo -w read_', 'watchpoint set variable foo -w read_write') @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24489") def test_symbol_name(self): self.build() self.dbg.CreateTarget(self.getBuildArtifact("a.out")) self.complete_from_to('breakpoint set -n Fo', 'breakpoint set -n Foo::Bar(int,\\ int)', turn_off_re_match=True) def complete_from_to(self, str_input, patterns, turn_off_re_match=False): """Test that the completion mechanism completes str_input to patterns, where patterns could be a pattern-string or a list of pattern-strings""" # Patterns should not be None in order to proceed. self.assertFalse(patterns is None) # And should be either a string or list of strings. Check for list type # below, if not, make a list out of the singleton string. If patterns # is not a string or not a list of strings, there'll be runtime errors # later on. if not isinstance(patterns, list): patterns = [patterns] interp = self.dbg.GetCommandInterpreter() match_strings = lldb.SBStringList() num_matches = interp.HandleCompletion(str_input, len(str_input), 0, -1, match_strings) common_match = match_strings.GetStringAtIndex(0) if num_matches == 0: compare_string = str_input - else: + else: if common_match != None and len(common_match) > 0: compare_string = str_input + common_match else: compare_string = "" for idx in range(1, num_matches+1): compare_string += match_strings.GetStringAtIndex(idx) + "\n" for p in patterns: if turn_off_re_match: self.expect( compare_string, msg=COMPLETION_MSG( - str_input, p), exe=False, substrs=[p]) + str_input, p, match_strings), exe=False, substrs=[p]) else: self.expect( compare_string, msg=COMPLETION_MSG( - str_input, p), exe=False, patterns=[p]) + str_input, p, match_strings), exe=False, patterns=[p]) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/completion/main.cpp =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/completion/main.cpp (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/completion/main.cpp (revision 337147) @@ -1,14 +1,21 @@ class Foo { public: int Bar(int x, int y) { return x + y; } }; +struct Container { int MemberVar; }; + int main() { - Foo f; - f.Bar(1, 2); + Foo fooo; + Foo *ptr_fooo = &fooo; + fooo.Bar(1, 2); + + Container container; + Container *ptr_container = &container; + return container.MemberVar = 3; // Break here } Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/bitset/TestDataFormatterLibcxxBitset.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/bitset/TestDataFormatterLibcxxBitset.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/bitset/TestDataFormatterLibcxxBitset.py (revision 337147) @@ -1,62 +1,62 @@ """ Test lldb data formatter subsystem. """ from __future__ import print_function import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestDataFormatterLibcxxBitset(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): TestBase.setUp(self) primes = [1]*300 primes[0] = primes[1] = 0 for i in range(2, len(primes)): for j in range(2*i, len(primes), i): primes[j] = 0 self.primes = primes def check(self, name, size): var = self.frame().FindVariable(name) self.assertTrue(var.IsValid()) self.assertEqual(var.GetNumChildren(), size) for i in range(size): child = var.GetChildAtIndex(i) self.assertEqual(child.GetValueAsUnsigned(), self.primes[i], "variable: %s, index: %d"%(name, size)) @add_test_categories(["libc++"]) def test_value(self): """Test that std::bitset is displayed correctly""" self.build() lldbutil.run_to_source_breakpoint(self, '// break here', lldb.SBFileSpec("main.cpp", False)) self.check("empty", 0) self.check("small", 13) self.check("large", 200) @add_test_categories(["libc++"]) def test_ptr_and_ref(self): """Test that ref and ptr to std::bitset is displayed correctly""" self.build() - (_, process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, + (_, process, _, bkpt) = lldbutil.run_to_source_breakpoint(self, 'Check ref and ptr', lldb.SBFileSpec("main.cpp", False)) self.check("ref", 13) self.check("ptr", 13) lldbutil.continue_to_breakpoint(process, bkpt) - + self.check("ref", 200) self.check("ptr", 200) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/TestDataFormatterLibcxxList.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/TestDataFormatterLibcxxList.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/list/TestDataFormatterLibcxxList.py (revision 337147) @@ -1,219 +1,219 @@ """ Test lldb data formatter subsystem. """ from __future__ import print_function import os import time import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class LibcxxListDataFormatterTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Find the line number to break at. self.line = line_number('main.cpp', '// Set break point at this line.') self.line2 = line_number('main.cpp', '// Set second break point at this line.') self.line3 = line_number('main.cpp', '// Set third break point at this line.') self.line4 = line_number('main.cpp', '// Set fourth break point at this line.') @add_test_categories(["libc++"]) @skipIf(debug_info="gmodules", bugnumber="https://bugs.llvm.org/show_bug.cgi?id=36048") def test_with_run_command(self): """Test that that file and class static variables display correctly.""" self.build() self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) lldbutil.run_break_set_by_file_and_line( self, "main.cpp", self.line, num_expected_locations=-1) lldbutil.run_break_set_by_file_and_line( self, "main.cpp", self.line2, num_expected_locations=-1) lldbutil.run_break_set_by_file_and_line( self, "main.cpp", self.line3, num_expected_locations=-1) lldbutil.run_break_set_by_file_and_line( self, "main.cpp", self.line4, num_expected_locations=-1) self.runCmd("run", RUN_SUCCEEDED) lldbutil.skip_if_library_missing( self, self.target(), lldbutil.PrintableRegex("libc\+\+")) # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) # This is the function to remove the custom formats in order to have a # clean slate for the next test case. def cleanup(): self.runCmd('type format clear', check=False) self.runCmd('type summary clear', check=False) self.runCmd('type filter clear', check=False) self.runCmd('type synth clear', check=False) self.runCmd( "settings set target.max-children-count 256", check=False) # Execute the cleanup function during test case tear down. self.addTearDownHook(cleanup) self.runCmd("frame variable numbers_list --show-types") self.runCmd( "type summary add std::int_list std::string_list int_list string_list --summary-string \"list has ${svar%#} items\" -e") self.runCmd("type format add -f hex int") self.expect("frame variable numbers_list --raw", matching=False, substrs=['list has 0 items', '{}']) self.expect("frame variable numbers_list", substrs=['list has 0 items', '{}']) self.expect("p numbers_list", substrs=['list has 0 items', '{}']) self.runCmd("n") # This gets up past the printf self.runCmd("n") # Now advance over the first push_back. - + self.expect("frame variable numbers_list", substrs=['list has 1 items', '[0] = ', '0x12345678']) self.runCmd("n") self.runCmd("n") self.runCmd("n") self.expect("frame variable numbers_list", substrs=['list has 4 items', '[0] = ', '0x12345678', '[1] =', '0x11223344', '[2] =', '0xbeeffeed', '[3] =', '0x00abba00']) self.runCmd("n") self.runCmd("n") self.expect("frame variable numbers_list", substrs=['list has 6 items', '[0] = ', '0x12345678', '0x11223344', '0xbeeffeed', '0x00abba00', '[4] =', '0x0abcdef0', '[5] =', '0x0cab0cab']) self.expect("p numbers_list", substrs=['list has 6 items', '[0] = ', '0x12345678', '0x11223344', '0xbeeffeed', '0x00abba00', '[4] =', '0x0abcdef0', '[5] =', '0x0cab0cab']) # check access-by-index self.expect("frame variable numbers_list[0]", substrs=['0x12345678']) self.expect("frame variable numbers_list[1]", substrs=['0x11223344']) self.runCmd("n") self.expect("frame variable numbers_list", substrs=['list has 0 items', '{}']) self.runCmd("n") self.runCmd("n") self.runCmd("n") self.runCmd("n") self.expect("frame variable numbers_list", substrs=['list has 4 items', '[0] = ', '1', '[1] = ', '2', '[2] = ', '3', '[3] = ', '4']) # check that MightHaveChildren() gets it right self.assertTrue( self.frame().FindVariable("numbers_list").MightHaveChildren(), "numbers_list.MightHaveChildren() says False for non empty!") self.runCmd("type format delete int") self.runCmd("c") self.expect("frame variable text_list", substrs=['list has 3 items', '[0]', 'goofy', '[1]', 'is', '[2]', 'smart']) # check that MightHaveChildren() gets it right self.assertTrue( self.frame().FindVariable("text_list").MightHaveChildren(), "text_list.MightHaveChildren() says False for non empty!") self.expect("p text_list", substrs=['list has 3 items', '\"goofy\"', '\"is\"', '\"smart\"']) self.runCmd("n") # This gets us past the printf self.runCmd("n") # check access-by-index self.expect("frame variable text_list[0]", substrs=['goofy']) self.expect("frame variable text_list[3]", substrs=['!!!']) self.runCmd("continue") # check that the list provider correctly updates if elements move countingList = self.frame().FindVariable("countingList") countingList.SetPreferDynamicValue(True) countingList.SetPreferSyntheticValue(True) self.assertTrue(countingList.GetChildAtIndex( 0).GetValueAsUnsigned(0) == 3141, "list[0] == 3141") self.assertTrue(countingList.GetChildAtIndex( 1).GetValueAsUnsigned(0) == 3141, "list[1] == 3141") self.runCmd("continue") self.assertTrue( countingList.GetChildAtIndex(0).GetValueAsUnsigned(0) == 3141, "uniqued list[0] == 3141") self.assertTrue( countingList.GetChildAtIndex(1).GetValueAsUnsigned(0) == 3142, "uniqued list[1] == 3142") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/TestDataFormatterLibcxxMultiSet.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/TestDataFormatterLibcxxMultiSet.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/TestDataFormatterLibcxxMultiSet.py (revision 337147) @@ -1,145 +1,145 @@ """ Test lldb data formatter subsystem. """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class LibcxxMultiSetDataFormatterTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): TestBase.setUp(self) ns = 'ndk' if lldbplatformutil.target_is_android() else '' self.namespace = 'std::__' + ns + '1' def getVariableType(self, name): var = self.frame().FindVariable(name) self.assertTrue(var.IsValid()) return var.GetType().GetCanonicalType().GetName() def check_ii(self, var_name): """ This checks the value of the bitset stored in ii at the call to by_ref_and_ptr. We use this to make sure we get the same values for ii when we look at the object directly, and when we look at a reference to the object. """ self.expect( "frame variable " + var_name, substrs=["size=7", "[2] = 2", "[3] = 3", "[6] = 6"]) self.expect("frame variable " + var_name + "[2]", substrs=[" = 2"]) self.expect( "p " + var_name, substrs=[ "size=7", "[2] = 2", "[3] = 3", "[6] = 6"]) @add_test_categories(["libc++"]) def test_with_run_command(self): """Test that that file and class static variables display correctly.""" self.build() (self.target, process, _, bkpt) = lldbutil.run_to_source_breakpoint( self, "Set break point at this line.", lldb.SBFileSpec("main.cpp", False)) # This is the function to remove the custom formats in order to have a # clean slate for the next test case. def cleanup(): self.runCmd('type format clear', check=False) self.runCmd('type summary clear', check=False) self.runCmd('type filter clear', check=False) self.runCmd('type synth clear', check=False) self.runCmd( "settings set target.max-children-count 256", check=False) # Execute the cleanup function during test case tear down. self.addTearDownHook(cleanup) ii_type = self.getVariableType("ii") self.assertTrue(ii_type.startswith(self.namespace + "::multiset"), "Type: " + ii_type) self.expect("frame variable ii", substrs=["size=0", "{}"]) lldbutil.continue_to_breakpoint(process, bkpt) self.expect( "frame variable ii", substrs=[ "size=6", "[0] = 0", "[1] = 1", "[2] = 2", "[3] = 3", "[4] = 4", "[5] = 5"]) lldbutil.continue_to_breakpoint(process, bkpt) self.check_ii("ii") lldbutil.continue_to_breakpoint(process, bkpt) self.expect("frame variable ii", substrs=["size=0", "{}"]) lldbutil.continue_to_breakpoint(process, bkpt) self.expect("frame variable ii", substrs=["size=0", "{}"]) ss_type = self.getVariableType("ss") self.assertTrue(ss_type.startswith(self.namespace + "::multiset"), "Type: " + ss_type) self.expect("frame variable ss", substrs=["size=0", "{}"]) lldbutil.continue_to_breakpoint(process, bkpt) self.expect( "frame variable ss", substrs=[ "size=2", '[0] = "a"', '[1] = "a very long string is right here"']) lldbutil.continue_to_breakpoint(process, bkpt) self.expect( "frame variable ss", substrs=[ "size=4", '[2] = "b"', '[3] = "c"', '[0] = "a"', '[1] = "a very long string is right here"']) self.expect( "p ss", substrs=[ "size=4", '[2] = "b"', '[3] = "c"', '[0] = "a"', '[1] = "a very long string is right here"']) self.expect("frame variable ss[2]", substrs=[' = "b"']) lldbutil.continue_to_breakpoint(process, bkpt) self.expect( "frame variable ss", substrs=[ "size=3", '[0] = "a"', '[1] = "a very long string is right here"', '[2] = "c"']) @add_test_categories(["libc++"]) def test_ref_and_ptr(self): """Test that the data formatters work on ref and ptr.""" self.build() (self.target, process, _, bkpt) = lldbutil.run_to_source_breakpoint( - self, "Stop here to check by ref and ptr.", + self, "Stop here to check by ref and ptr.", lldb.SBFileSpec("main.cpp", False)) # The reference should print just like the value: self.check_ii("ref") self.expect("frame variable ptr", substrs=["ptr =", "size=7"]) self.expect("expr ptr", substrs=["size=7"]) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/TestDataFormatterLibcxxSet.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/TestDataFormatterLibcxxSet.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/TestDataFormatterLibcxxSet.py (revision 337147) @@ -1,142 +1,142 @@ """ Test lldb data formatter subsystem. """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class LibcxxSetDataFormatterTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): TestBase.setUp(self) ns = 'ndk' if lldbplatformutil.target_is_android() else '' self.namespace = 'std::__' + ns + '1' def getVariableType(self, name): var = self.frame().FindVariable(name) self.assertTrue(var.IsValid()) return var.GetType().GetCanonicalType().GetName() def check_ii(self, var_name): """ This checks the value of the bitset stored in ii at the call to by_ref_and_ptr. We use this to make sure we get the same values for ii when we look at the object directly, and when we look at a reference to the object. """ self.expect( "frame variable " + var_name, substrs=["size=7", "[2] = 2", "[3] = 3", "[6] = 6"]) self.expect("frame variable " + var_name + "[2]", substrs=[" = 2"]) self.expect( "p " + var_name, substrs=[ "size=7", "[2] = 2", "[3] = 3", "[6] = 6"]) @add_test_categories(["libc++"]) def test_with_run_command(self): """Test that that file and class static variables display correctly.""" self.build() (self.target, process, _, bkpt) = lldbutil.run_to_source_breakpoint( self, "Set break point at this line.", lldb.SBFileSpec("main.cpp", False)) # This is the function to remove the custom formats in order to have a # clean slate for the next test case. def cleanup(): self.runCmd('type format clear', check=False) self.runCmd('type summary clear', check=False) self.runCmd('type filter clear', check=False) self.runCmd('type synth clear', check=False) self.runCmd( "settings set target.max-children-count 256", check=False) # Execute the cleanup function during test case tear down. self.addTearDownHook(cleanup) ii_type = self.getVariableType("ii") self.assertTrue(ii_type.startswith(self.namespace + "::set"), "Type: " + ii_type) self.expect("frame variable ii", substrs=["size=0", "{}"]) lldbutil.continue_to_breakpoint(process, bkpt) self.expect( "frame variable ii", substrs=["size=6", "[0] = 0", "[1] = 1", "[2] = 2", "[3] = 3", "[4] = 4", "[5] = 5"]) lldbutil.continue_to_breakpoint(process, bkpt) self.check_ii("ii") lldbutil.continue_to_breakpoint(process, bkpt) self.expect("frame variable ii", substrs=["size=0", "{}"]) lldbutil.continue_to_breakpoint(process, bkpt) self.expect("frame variable ii", substrs=["size=0", "{}"]) ss_type = self.getVariableType("ss") self.assertTrue(ii_type.startswith(self.namespace + "::set"), "Type: " + ss_type) self.expect("frame variable ss", substrs=["size=0", "{}"]) lldbutil.continue_to_breakpoint(process, bkpt) self.expect( "frame variable ss", substrs=["size=2", '[0] = "a"', '[1] = "a very long string is right here"']) lldbutil.continue_to_breakpoint(process, bkpt) self.expect( "frame variable ss", substrs=["size=4", '[2] = "b"', '[3] = "c"', '[0] = "a"', '[1] = "a very long string is right here"']) self.expect( "p ss", substrs=["size=4", '[2] = "b"', '[3] = "c"', '[0] = "a"', '[1] = "a very long string is right here"']) self.expect("frame variable ss[2]", substrs=[' = "b"']) lldbutil.continue_to_breakpoint(process, bkpt) self.expect( "frame variable ss", substrs=["size=3", '[0] = "a"', '[1] = "a very long string is right here"', '[2] = "c"']) @add_test_categories(["libc++"]) def test_ref_and_ptr(self): """Test that the data formatters work on ref and ptr.""" self.build() (self.target, process, _, bkpt) = lldbutil.run_to_source_breakpoint( - self, "Stop here to check by ref and ptr.", + self, "Stop here to check by ref and ptr.", lldb.SBFileSpec("main.cpp", False)) # The reference should print just like the value: self.check_ii("ref") self.expect("frame variable ptr", substrs=["ptr =", "size=7"]) self.expect("expr ptr", substrs=["size=7"]) - + Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/TestDataFormatterLibcxxVector.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/TestDataFormatterLibcxxVector.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/vector/TestDataFormatterLibcxxVector.py (revision 337147) @@ -1,198 +1,198 @@ """ Test lldb data formatter subsystem. """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class LibcxxVectorDataFormatterTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def check_numbers(self, var_name): self.expect("frame variable " + var_name, substrs=[var_name + ' = size=7', '[0] = 1', '[1] = 12', '[2] = 123', '[3] = 1234', '[4] = 12345', '[5] = 123456', '[6] = 1234567', '}']) self.expect("p " + var_name, substrs=['$', 'size=7', '[0] = 1', '[1] = 12', '[2] = 123', '[3] = 1234', '[4] = 12345', '[5] = 123456', '[6] = 1234567', '}']) # check access-by-index self.expect("frame variable " + var_name + "[0]", substrs=['1']) self.expect("frame variable " + var_name + "[1]", substrs=['12']) self.expect("frame variable " + var_name + "[2]", substrs=['123']) self.expect("frame variable " + var_name + "[3]", substrs=['1234']) @add_test_categories(["libc++"]) @skipIf(debug_info="gmodules", bugnumber="https://bugs.llvm.org/show_bug.cgi?id=36048") def test_with_run_command(self): """Test that that file and class static variables display correctly.""" self.build() (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( self, "break here", lldb.SBFileSpec("main.cpp", False)) # This is the function to remove the custom formats in order to have a # clean slate for the next test case. def cleanup(): self.runCmd('type format clear', check=False) self.runCmd('type summary clear', check=False) self.runCmd('type filter clear', check=False) self.runCmd('type synth clear', check=False) self.runCmd( "settings set target.max-children-count 256", check=False) # Execute the cleanup function during test case tear down. self.addTearDownHook(cleanup) # empty vectors (and storage pointers SHOULD BOTH BE NULL..) self.expect("frame variable numbers", substrs=['numbers = size=0']) lldbutil.continue_to_breakpoint(process, bkpt) # first value added self.expect("frame variable numbers", substrs=['numbers = size=1', '[0] = 1', '}']) # add some more data lldbutil.continue_to_breakpoint(process, bkpt) self.expect("frame variable numbers", substrs=['numbers = size=4', '[0] = 1', '[1] = 12', '[2] = 123', '[3] = 1234', '}']) self.expect("p numbers", substrs=['$', 'size=4', '[0] = 1', '[1] = 12', '[2] = 123', '[3] = 1234', '}']) # check access to synthetic children self.runCmd( "type summary add --summary-string \"item 0 is ${var[0]}\" std::int_vect int_vect") self.expect('frame variable numbers', substrs=['item 0 is 1']) self.runCmd( "type summary add --summary-string \"item 0 is ${svar[0]}\" std::int_vect int_vect") self.expect('frame variable numbers', substrs=['item 0 is 1']) # move on with synths self.runCmd("type summary delete std::int_vect") self.runCmd("type summary delete int_vect") # add some more data lldbutil.continue_to_breakpoint(process, bkpt) self.check_numbers("numbers") # clear out the vector and see that we do the right thing once again lldbutil.continue_to_breakpoint(process, bkpt) self.expect("frame variable numbers", substrs=['numbers = size=0']) lldbutil.continue_to_breakpoint(process, bkpt) # first value added self.expect("frame variable numbers", substrs=['numbers = size=1', '[0] = 7', '}']) # check if we can display strings self.expect("frame variable strings", substrs=['goofy', 'is', 'smart']) self.expect("p strings", substrs=['goofy', 'is', 'smart']) # test summaries based on synthetic children self.runCmd( "type summary add std::string_vect string_vect --summary-string \"vector has ${svar%#} items\" -e") self.expect("frame variable strings", substrs=['vector has 3 items', 'goofy', 'is', 'smart']) self.expect("p strings", substrs=['vector has 3 items', 'goofy', 'is', 'smart']) lldbutil.continue_to_breakpoint(process, bkpt) self.expect("frame variable strings", substrs=['vector has 4 items']) # check access-by-index self.expect("frame variable strings[0]", substrs=['goofy']) self.expect("frame variable strings[1]", substrs=['is']) lldbutil.continue_to_breakpoint(process, bkpt) self.expect("frame variable strings", substrs=['vector has 0 items']) @add_test_categories(["libc++"]) @skipIf(debug_info="gmodules", bugnumber="https://bugs.llvm.org/show_bug.cgi?id=36048") def test_ref_and_ptr(self): """Test that that file and class static variables display correctly.""" self.build() (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( self, "Stop here to check by ref", lldb.SBFileSpec("main.cpp", False)) # The reference should display the same was as the value did self.check_numbers("ref") # The pointer should just show the right number of elements: - + self.expect("frame variable ptr", substrs=['ptr =', ' size=7']) self.expect("p ptr", substrs=['$', 'size=7']) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/tuple/TestDataFormatterStdTuple.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/tuple/TestDataFormatterStdTuple.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/tuple/TestDataFormatterStdTuple.py (revision 337147) @@ -1,47 +1,47 @@ """ Test lldb data formatter subsystem. """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class StdTupleDataFormatterTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) @add_test_categories(["libstdcxx"]) def test_with_run_command(self): self.build() self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) lldbutil.run_break_set_by_source_regexp( self, "Set break point at this line.") self.runCmd("run", RUN_SUCCEEDED) # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) frame = self.frame() self.assertTrue(frame.IsValid()) self.expect("frame variable ti", substrs=['[0] = 1']) self.expect("frame variable ts", substrs=['[0] = "foobar"']) self.expect("frame variable tt", substrs=['[0] = 1', '[1] = "baz"', '[2] = 2']) self.assertEqual(1, frame.GetValueForVariablePath("ti[0]").GetValueAsUnsigned()) self.assertFalse(frame.GetValueForVariablePath("ti[1]").IsValid()) self.assertEqual('"foobar"', frame.GetValueForVariablePath("ts[0]").GetSummary()) self.assertFalse(frame.GetValueForVariablePath("ts[1]").IsValid()) - + self.assertEqual(1, frame.GetValueForVariablePath("tt[0]").GetValueAsUnsigned()) self.assertEqual('"baz"', frame.GetValueForVariablePath("tt[1]").GetSummary()) self.assertEqual(2, frame.GetValueForVariablePath("tt[2]").GetValueAsUnsigned()) self.assertFalse(frame.GetValueForVariablePath("tt[3]").IsValid()) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/exec/TestExec.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/exec/TestExec.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/exec/TestExec.py (revision 337147) @@ -1,117 +1,117 @@ """ Test some lldb command abbreviations. """ from __future__ import print_function import lldb import os import time from lldbsuite.support import seven from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil def execute_command(command): #print('%% %s' % (command)) (exit_status, output) = seven.get_command_status_output(command) # if output: # print(output) #print('status = %u' % (exit_status)) return exit_status class ExecTestCase(TestBase): NO_DEBUG_INFO_TESTCASE = True mydir = TestBase.compute_mydir(__file__) @skipUnlessDarwin @expectedFailureAll(archs=['i386'], bugnumber="rdar://28656532") @expectedFailureAll(oslist=["ios", "tvos", "watchos", "bridgeos"], bugnumber="rdar://problem/34559552") # this exec test has problems on ios systems def test_hitting_exec (self): self.do_test(False) @skipUnlessDarwin @expectedFailureAll(archs=['i386'], bugnumber="rdar://28656532") @expectedFailureAll(oslist=["ios", "tvos", "watchos", "bridgeos"], bugnumber="rdar://problem/34559552") # this exec test has problems on ios systems def test_skipping_exec (self): self.do_test(True) def do_test(self, skip_exec): self.build() exe = self.getBuildArtifact("a.out") secondprog = self.getBuildArtifact("secondprog") # Create the target target = self.dbg.CreateTarget(exe) # Create any breakpoints we need breakpoint1 = target.BreakpointCreateBySourceRegex( 'Set breakpoint 1 here', lldb.SBFileSpec("main.cpp", False)) self.assertTrue(breakpoint1, VALID_BREAKPOINT) breakpoint2 = target.BreakpointCreateBySourceRegex( 'Set breakpoint 2 here', lldb.SBFileSpec("secondprog.cpp", False)) self.assertTrue(breakpoint2, VALID_BREAKPOINT) # Launch the process process = target.LaunchSimple( None, None, self.get_process_working_directory()) self.assertTrue(process, PROCESS_IS_VALID) if skip_exec: self.dbg.HandleCommand("settings set target.process.stop-on-exec false") def cleanup(): self.runCmd("settings set target.process.stop-on-exec false", check=False) # Execute the cleanup function during test case tear down. self.addTearDownHook(cleanup) # The stop reason of the thread should be breakpoint. self.assertTrue(process.GetState() == lldb.eStateStopped, STOPPED_DUE_TO_BREAKPOINT) threads = lldbutil.get_threads_stopped_at_breakpoint( process, breakpoint1) self.assertTrue(len(threads) == 1) # We had a deadlock tearing down the TypeSystemMap on exec, but only if some # expression had been evaluated. So make sure we do that here so the teardown # is not trivial. thread = threads[0] value = thread.frames[0].EvaluateExpression("1 + 2") self.assertTrue( value.IsValid(), "Expression evaluated successfully") int_value = value.GetValueAsSigned() self.assertTrue(int_value == 3, "Expression got the right result.") # Run and we should stop due to exec process.Continue() if not skip_exec: self.assertTrue(process.GetState() == lldb.eStateStopped, "Process should be stopped at __dyld_start") - + threads = lldbutil.get_stopped_threads( process, lldb.eStopReasonExec) self.assertTrue( len(threads) == 1, "We got a thread stopped for exec.") # Run and we should stop at breakpoint in main after exec process.Continue() threads = lldbutil.get_threads_stopped_at_breakpoint( process, breakpoint2) if self.TraceOn(): for t in process.threads: print(t) if t.GetStopReason() != lldb.eStopReasonBreakpoint: self.runCmd("bt") self.assertTrue(len(threads) == 1, "Stopped at breakpoint in exec'ed process.") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/frame-language/TestGuessLanguage.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/frame-language/TestGuessLanguage.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/frame-language/TestGuessLanguage.py (revision 337147) @@ -1,89 +1,89 @@ """ Test the SB API SBFrame::GuessLanguage. """ from __future__ import print_function import os import time import re import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * class TestFrameGuessLanguage(TestBase): mydir = TestBase.compute_mydir(__file__) - # If your test case doesn't stress debug info, the + # If your test case doesn't stress debug info, the # set this to true. That way it won't be run once for # each debug info format. NO_DEBUG_INFO_TESTCASE = True @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr37658") def test_guess_language(self): """Test GuessLanguage for C and C++.""" self.build() self.do_test() def setUp(self): # Call super's setUp(). TestBase.setUp(self) def check_language(self, thread, frame_no, test_lang): frame = thread.frames[frame_no] self.assertTrue(frame.IsValid(), "Frame %d was not valid."%(frame_no)) lang = frame.GuessLanguage() self.assertEqual(lang, test_lang) def do_test(self): """Test GuessLanguage for C & C++.""" exe = self.getBuildArtifact("a.out") # Create a target by the debugger. target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) # Now create a breakpoint in main.c at the source matching # "Set a breakpoint here" breakpoint = target.BreakpointCreateBySourceRegex( "Set breakpoint here", lldb.SBFileSpec("somefunc.c")) self.assertTrue(breakpoint and breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT) error = lldb.SBError() # This is the launch info. If you want to launch with arguments or # environment variables, add them using SetArguments or # SetEnvironmentEntries launch_info = lldb.SBLaunchInfo(None) process = target.Launch(launch_info, error) self.assertTrue(process, PROCESS_IS_VALID) # Did we hit our breakpoint? from lldbsuite.test.lldbutil import get_threads_stopped_at_breakpoint threads = get_threads_stopped_at_breakpoint(process, breakpoint) self.assertTrue( len(threads) == 1, "There should be a thread stopped at our breakpoint") # The hit count for the breakpoint should be 1. self.assertTrue(breakpoint.GetHitCount() == 1) thread = threads[0] c_frame_language = lldb.eLanguageTypeC99 # gcc emits DW_LANG_C89 even if -std=c99 was specified if "gcc" in self.getCompiler(): c_frame_language = lldb.eLanguageTypeC89 self.check_language(thread, 0, c_frame_language) self.check_language(thread, 1, lldb.eLanguageTypeC_plus_plus) self.check_language(thread, 2, lldb.eLanguageTypeC_plus_plus) - - + + Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/frame_var/TestFrameVar.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/frame_var/TestFrameVar.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/frame_var/TestFrameVar.py (revision 337147) @@ -1,99 +1,99 @@ """ Make sure the frame variable -g, -a, and -l flags work. """ from __future__ import print_function import os import time import re import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * class TestFrameVar(TestBase): mydir = TestBase.compute_mydir(__file__) - # If your test case doesn't stress debug info, the + # If your test case doesn't stress debug info, the # set this to true. That way it won't be run once for # each debug info format. NO_DEBUG_INFO_TESTCASE = True def test_frame_var(self): self.build() self.do_test() def setUp(self): # Call super's setUp(). TestBase.setUp(self) def do_test(self): exe = self.getBuildArtifact("a.out") # Create a target by the debugger. target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) # Now create a breakpoint in main.c at the source matching # "Set a breakpoint here" breakpoint = target.BreakpointCreateBySourceRegex( "Set a breakpoint here", lldb.SBFileSpec("main.c")) self.assertTrue(breakpoint and breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT) error = lldb.SBError() # This is the launch info. If you want to launch with arguments or # environment variables, add them using SetArguments or # SetEnvironmentEntries launch_info = lldb.SBLaunchInfo(None) process = target.Launch(launch_info, error) self.assertTrue(process, PROCESS_IS_VALID) # Did we hit our breakpoint? from lldbsuite.test.lldbutil import get_threads_stopped_at_breakpoint threads = get_threads_stopped_at_breakpoint(process, breakpoint) self.assertTrue( len(threads) == 1, "There should be a thread stopped at our breakpoint") # The hit count for the breakpoint should be 1. self.assertTrue(breakpoint.GetHitCount() == 1) frame = threads[0].GetFrameAtIndex(0) command_result = lldb.SBCommandReturnObject() interp = self.dbg.GetCommandInterpreter() - + # Just get args: result = interp.HandleCommand("frame var -l", command_result) self.assertEqual(result, lldb.eReturnStatusSuccessFinishResult, "frame var -a didn't succeed") output = command_result.GetOutput() self.assertTrue("argc" in output, "Args didn't find argc") self.assertTrue("argv" in output, "Args didn't find argv") self.assertTrue("test_var" not in output, "Args found a local") self.assertTrue("g_var" not in output, "Args found a global") # Just get locals: result = interp.HandleCommand("frame var -a", command_result) self.assertEqual(result, lldb.eReturnStatusSuccessFinishResult, "frame var -a didn't succeed") output = command_result.GetOutput() self.assertTrue("argc" not in output, "Locals found argc") self.assertTrue("argv" not in output, "Locals found argv") self.assertTrue("test_var" in output, "Locals didn't find test_var") self.assertTrue("g_var" not in output, "Locals found a global") - + # Get the file statics: result = interp.HandleCommand("frame var -l -a -g", command_result) self.assertEqual(result, lldb.eReturnStatusSuccessFinishResult, "frame var -a didn't succeed") output = command_result.GetOutput() self.assertTrue("argc" not in output, "Globals found argc") self.assertTrue("argv" not in output, "Globals found argv") self.assertTrue("test_var" not in output, "Globals found test_var") self.assertTrue("g_var" in output, "Globals didn't find g_var") - - + + Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestTargetXMLArch.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestTargetXMLArch.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestTargetXMLArch.py (revision 337147) @@ -1,124 +1,124 @@ from __future__ import print_function import lldb from lldbsuite.test.lldbtest import * from lldbsuite.test.decorators import * from gdbclientutils import * class TestTargetXMLArch(GDBRemoteTestBase): @skipIfXmlSupportMissing @expectedFailureAll(archs=["i386"]) @skipIfRemote def test(self): """ Test lldb's parsing of the tag in the target.xml register description packet. """ class MyResponder(MockGDBServerResponder): def qXferRead(self, obj, annex, offset, length): if annex == "target.xml": return """ i386:x86-64 """, False else: return None, False def qC(self): return "QC1" def haltReason(self): return "T05thread:00000001;06:9038d60f00700000;07:98b4062680ffffff;10:c0d7bf1b80ffffff;" def readRegister(self, register): - regs = {0x0: "00b0060000610000", - 0xa: "68fe471c80ffffff", - 0xc: "60574a1c80ffffff", - 0xd: "18f3042680ffffff", - 0xe: "be8a4d7142000000", - 0xf: "50df471c80ffffff", + regs = {0x0: "00b0060000610000", + 0xa: "68fe471c80ffffff", + 0xc: "60574a1c80ffffff", + 0xd: "18f3042680ffffff", + 0xe: "be8a4d7142000000", + 0xf: "50df471c80ffffff", 0x10: "c0d7bf1b80ffffff" } if register in regs: return regs[register] else: return "0000000000000000" self.server.responder = MyResponder() interp = self.dbg.GetCommandInterpreter() result = lldb.SBCommandReturnObject() if self.TraceOn(): interp.HandleCommand("log enable gdb-remote packets", result) target = self.dbg.CreateTarget('') self.assertEqual('', target.GetTriple()) process = self.connect(target) if self.TraceOn(): interp.HandleCommand("target list", result) print(result.GetOutput()) self.assertTrue(target.GetTriple().startswith('x86_64--')) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/gdbclientutils.py (revision 337147) @@ -1,477 +1,477 @@ import os import os.path import subprocess import threading import socket import lldb from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbtest_config def checksum(message): """ Calculate the GDB server protocol checksum of the message. The GDB server protocol uses a simple modulo 256 sum. """ check = 0 for c in message: check += ord(c) return check % 256 def frame_packet(message): """ Create a framed packet that's ready to send over the GDB connection channel. Framing includes surrounding the message between $ and #, and appending a two character hex checksum. """ return "$%s#%02x" % (message, checksum(message)) def escape_binary(message): """ Escape the binary message using the process described in the GDB server protocol documentation. Most bytes are sent through as-is, but $, #, and { are escaped by writing a { followed by the original byte mod 0x20. """ out = "" for c in message: d = ord(c) if d in (0x23, 0x24, 0x7d): out += chr(0x7d) out += chr(d ^ 0x20) else: out += c return out def hex_encode_bytes(message): """ Encode the binary message by converting each byte into a two-character hex string. """ out = "" for c in message: out += "%02x" % ord(c) return out def hex_decode_bytes(hex_bytes): """ Decode the hex string into a binary message by converting each two-character hex string into a single output byte. """ out = "" hex_len = len(hex_bytes) while i < hex_len - 1: out += chr(int(hex_bytes[i:i + 2]), 16) i += 2 return out class MockGDBServerResponder: """ A base class for handling client packets and issuing server responses for GDB tests. This handles many typical situations, while still allowing subclasses to completely customize their responses. Most subclasses will be interested in overriding the other() method, which handles any packet not recognized in the common packet handling code. """ registerCount = 40 packetLog = None def __init__(self): self.packetLog = [] def respond(self, packet): """ Return the unframed packet data that the server should issue in response to the given packet received from the client. """ self.packetLog.append(packet) if packet is MockGDBServer.PACKET_INTERRUPT: return self.interrupt() if packet == "c": return self.cont() if packet == "g": return self.readRegisters() if packet[0] == "G": return self.writeRegisters(packet[1:]) if packet[0] == "p": return self.readRegister(int(packet[1:], 16)) if packet[0] == "P": register, value = packet[1:].split("=") return self.readRegister(int(register, 16), value) if packet[0] == "m": addr, length = [int(x, 16) for x in packet[1:].split(',')] return self.readMemory(addr, length) if packet[0] == "M": location, encoded_data = packet[1:].split(":") addr, length = [int(x, 16) for x in location.split(',')] return self.writeMemory(addr, encoded_data) if packet[0:7] == "qSymbol": return self.qSymbol(packet[8:]) if packet[0:10] == "qSupported": return self.qSupported(packet[11:].split(";")) if packet == "qfThreadInfo": return self.qfThreadInfo() if packet == "qC": return self.qC() if packet == "QEnableErrorStrings": return self.QEnableErrorStrings() if packet == "?": return self.haltReason() if packet[0] == "H": return self.selectThread(packet[1], int(packet[2:], 16)) if packet[0:6] == "qXfer:": obj, read, annex, location = packet[6:].split(":") offset, length = [int(x, 16) for x in location.split(',')] data, has_more = self.qXferRead(obj, annex, offset, length) if data is not None: return self._qXferResponse(data, has_more) return "" if packet.startswith("vAttach;"): pid = packet.partition(';')[2] return self.vAttach(int(pid, 16)) if packet[0] == "Z": return self.setBreakpoint(packet) return self.other(packet) def interrupt(self): raise self.UnexpectedPacketException() def cont(self): raise self.UnexpectedPacketException() def readRegisters(self): return "00000000" * self.registerCount def readRegister(self, register): return "00000000" def writeRegisters(self, registers_hex): return "OK" def writeRegister(self, register, value_hex): return "OK" def readMemory(self, addr, length): return "00" * length def writeMemory(self, addr, data_hex): return "OK" def qSymbol(self, symbol_args): return "OK" def qSupported(self, client_supported): return "qXfer:features:read+;PacketSize=3fff;QStartNoAckMode+" def qfThreadInfo(self): return "l" def qC(self): return "QC0" def QEnableErrorStrings(self): return "OK" def haltReason(self): # SIGINT is 2, return type is 2 digit hex string return "S02" def qXferRead(self, obj, annex, offset, length): return None, False def _qXferResponse(self, data, has_more): return "%s%s" % ("m" if has_more else "l", escape_binary(data)) def vAttach(self, pid): raise self.UnexpectedPacketException() def selectThread(self, op, thread_id): return "OK" def setBreakpoint(self, packet): raise self.UnexpectedPacketException() def other(self, packet): # empty string means unsupported return "" """ Raised when we receive a packet for which there is no default action. Override the responder class to implement behavior suitable for the test at hand. """ class UnexpectedPacketException(Exception): pass class MockGDBServer: """ A simple TCP-based GDB server that can test client behavior by receiving commands and issuing custom-tailored responses. Responses are generated via the .responder property, which should be an instance of a class based on MockGDBServerResponder. """ responder = None port = 0 _socket = None _client = None _thread = None _receivedData = None _receivedDataOffset = None _shouldSendAck = True def __init__(self, port = 0): self.responder = MockGDBServerResponder() self.port = port self._socket = socket.socket() def start(self): # Block until the socket is up, so self.port is available immediately. # Then start a thread that waits for a client connection. addr = ("127.0.0.1", self.port) self._socket.bind(addr) self.port = self._socket.getsockname()[1] self._socket.listen(0) self._thread = threading.Thread(target=self._run) self._thread.start() def stop(self): self._socket.close() self._thread.join() self._thread = None def _run(self): # For testing purposes, we only need to worry about one client # connecting just one time. try: # accept() is stubborn and won't fail even when the socket is # shutdown, so we'll use a timeout self._socket.settimeout(2.0) client, client_addr = self._socket.accept() self._client = client # The connected client inherits its timeout from self._socket, # but we'll use a blocking socket for the client self._client.settimeout(None) except: return self._shouldSendAck = True self._receivedData = "" self._receivedDataOffset = 0 data = None while True: try: data = self._client.recv(4096) if data is None or len(data) == 0: break # In Python 2, sockets return byte strings. In Python 3, sockets return bytes. # If we got bytes (and not a byte string), decode them to a string for later handling. if isinstance(data, bytes) and not isinstance(data, str): data = data.decode() self._receive(data) except Exception as e: self._client.close() break def _receive(self, data): """ Collects data, parses and responds to as many packets as exist. Any leftover data is kept for parsing the next time around. """ self._receivedData += data try: packet = self._parsePacket() while packet is not None: self._handlePacket(packet) packet = self._parsePacket() except self.InvalidPacketException: self._client.close() def _parsePacket(self): """ Reads bytes from self._receivedData, returning: - a packet's contents if a valid packet is found - the PACKET_ACK unique object if we got an ack - None if we only have a partial packet Raises an InvalidPacketException if unexpected data is received or if checksums fail. Once a complete packet is found at the front of self._receivedData, its data is removed form self._receivedData. """ data = self._receivedData i = self._receivedDataOffset data_len = len(data) if data_len == 0: return None if i == 0: # If we're looking at the start of the received data, that means # we're looking for the start of a new packet, denoted by a $. # It's also possible we'll see an ACK here, denoted by a + if data[0] == '+': self._receivedData = data[1:] return self.PACKET_ACK if ord(data[0]) == 3: self._receivedData = data[1:] return self.PACKET_INTERRUPT if data[0] == '$': i += 1 else: raise self.InvalidPacketException( "Unexpected leading byte: %s" % data[0]) # If we're looking beyond the start of the received data, then we're # looking for the end of the packet content, denoted by a #. # Note that we pick up searching from where we left off last time while i < data_len and data[i] != '#': i += 1 # If there isn't enough data left for a checksum, just remember where # we left off so we can pick up there the next time around if i > data_len - 3: self._receivedDataOffset = i return None # If we have enough data remaining for the checksum, extract it and # compare to the packet contents packet = data[1:i] i += 1 try: check = int(data[i:i + 2], 16) except ValueError: raise self.InvalidPacketException("Checksum is not valid hex") i += 2 if check != checksum(packet): raise self.InvalidPacketException( "Checksum %02x does not match content %02x" % (check, checksum(packet))) # remove parsed bytes from _receivedData and reset offset so parsing # can start on the next packet the next time around self._receivedData = data[i:] self._receivedDataOffset = 0 return packet def _handlePacket(self, packet): if packet is self.PACKET_ACK: # Ignore ACKs from the client. For the future, we can consider # adding validation code to make sure the client only sends ACKs # when it's supposed to. return response = "" # We'll handle the ack stuff here since it's not something any of the # tests will be concerned about, and it'll get turned off quickly anyway. if self._shouldSendAck: self._client.sendall('+'.encode()) if packet == "QStartNoAckMode": self._shouldSendAck = False response = "OK" elif self.responder is not None: # Delegate everything else to our responder response = self.responder.respond(packet) # Handle packet framing since we don't want to bother tests with it. if response is not None: framed = frame_packet(response) # In Python 2, sockets send byte strings. In Python 3, sockets send bytes. # If we got a string (and not a byte string), encode it before sending. if isinstance(framed, str) and not isinstance(framed, bytes): framed = framed.encode() self._client.sendall(framed) PACKET_ACK = object() PACKET_INTERRUPT = object() class InvalidPacketException(Exception): pass class GDBRemoteTestBase(TestBase): """ Base class for GDB client tests. This class will setup and start a mock GDB server for the test to use. It also provides assertPacketLogContains, which simplifies the checking of packets sent by the client. """ NO_DEBUG_INFO_TESTCASE = True mydir = TestBase.compute_mydir(__file__) server = None def setUp(self): TestBase.setUp(self) self.server = MockGDBServer() self.server.start() def tearDown(self): # TestBase.tearDown will kill the process, but we need to kill it early # so its client connection closes and we can stop the server before # finally calling the base tearDown. if self.process() is not None: self.process().Kill() self.server.stop() TestBase.tearDown(self) def createTarget(self, yaml_path): """ Create a target by auto-generating the object based on the given yaml instructions. This will track the generated object so it can be automatically removed during tearDown. """ yaml_base, ext = os.path.splitext(yaml_path) obj_path = self.getBuildArtifact(yaml_base) self.yaml2obj(yaml_path, obj_path) return self.dbg.CreateTarget(obj_path) def connect(self, target): """ Create a process by connecting to the mock GDB server. Includes assertions that the process was successfully created. """ listener = self.dbg.GetListener() error = lldb.SBError() url = "connect://localhost:%d" % self.server.port process = target.ConnectRemote(listener, url, "gdb-remote", error) self.assertTrue(error.Success(), error.description) self.assertTrue(process, PROCESS_IS_VALID) return process def assertPacketLogContains(self, packets): """ Assert that the mock server's packet log contains the given packets. The packet log includes all packets sent by the client and received by the server. This fuction makes it easy to verify that the client sent the expected packets to the server. The check does not require that the packets be consecutive, but does require that they are ordered in the log as they ordered in the arg. """ i = 0 j = 0 log = self.server.responder.packetLog - + while i < len(packets) and j < len(log): if log[j] == packets[i]: i += 1 j += 1 if i < len(packets): self.fail(u"Did not receive: %s\nLast 10 packets:\n\t%s" % (packets[i], u'\n\t'.join(log[-10:]))) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/history/TestHistoryRecall.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/history/TestHistoryRecall.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/history/TestHistoryRecall.py (revision 337147) @@ -1,45 +1,45 @@ """ Make sure the !N and !-N commands work properly. """ from __future__ import print_function import os import time import re import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * class TestHistoryRecall(TestBase): mydir = TestBase.compute_mydir(__file__) - # If your test case doesn't stress debug info, the + # If your test case doesn't stress debug info, the # set this to true. That way it won't be run once for # each debug info format. NO_DEBUG_INFO_TESTCASE = True def test_history_recall(self): """Test the !N and !-N functionality of the command interpreter.""" self.sample_test() def setUp(self): # Call super's setUp(). TestBase.setUp(self) def sample_test(self): interp = self.dbg.GetCommandInterpreter() result = lldb.SBCommandReturnObject() interp.HandleCommand("command history", result, True) interp.HandleCommand("platform list", result, True) - + interp.HandleCommand("!0", result, False) self.assertTrue(result.Succeeded(), "!0 command did not work: %s"%(result.GetError())) self.assertTrue("command history" in result.GetOutput(), "!0 didn't rerun command history") interp.HandleCommand("!-1", result, False) self.assertTrue(result.Succeeded(), "!-1 command did not work: %s"%(result.GetError())) self.assertTrue("host:" in result.GetOutput(), "!-1 didn't rerun platform list.") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/load_unload/TestLoadUnload.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/load_unload/TestLoadUnload.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/load_unload/TestLoadUnload.py (revision 337147) @@ -1,405 +1,405 @@ """ Test that breakpoint by symbol name works correctly with dynamic libs. """ from __future__ import print_function import os import time import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently class LoadUnloadTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True - + def setUp(self): # Call super's setUp(). TestBase.setUp(self) self.setup_test() # Invoke the default build rule. self.build() # Find the line number to break for main.cpp. self.line = line_number( 'main.cpp', '// Set break point at this line for test_lldb_process_load_and_unload_commands().') self.line_d_function = line_number( 'd.cpp', '// Find this line number within d_dunction().') def setup_test(self): lldbutil.mkdir_p(self.getBuildArtifact("hidden")) if not self.platformIsDarwin(): if not lldb.remote_platform and "LD_LIBRARY_PATH" in os.environ: self.runCmd( "settings set target.env-vars " + self.dylibPath + "=" + os.environ["LD_LIBRARY_PATH"] + ":" + self.getBuildDir()) else: if lldb.remote_platform: wd = lldb.remote_platform.GetWorkingDirectory() else: wd = self.getBuildDir() self.runCmd( "settings set target.env-vars " + self.dylibPath + "=" + wd) def copy_shlibs_to_remote(self, hidden_dir=False): """ Copies the shared libs required by this test suite to remote. Does nothing in case of non-remote platforms. """ if lldb.remote_platform: ext = 'so' if self.platformIsDarwin(): ext = 'dylib' shlibs = ['libloadunload_a.' + ext, 'libloadunload_b.' + ext, 'libloadunload_c.' + ext, 'libloadunload_d.' + ext] wd = lldb.remote_platform.GetWorkingDirectory() cwd = os.getcwd() for f in shlibs: err = lldb.remote_platform.Put( lldb.SBFileSpec(self.getBuildArtifact(f)), lldb.SBFileSpec(os.path.join(wd, f))) if err.Fail(): raise RuntimeError( "Unable copy '%s' to '%s'.\n>>> %s" % (f, wd, err.GetCString())) if hidden_dir: shlib = 'libloadunload_d.' + ext hidden_dir = os.path.join(wd, 'hidden') hidden_file = os.path.join(hidden_dir, shlib) err = lldb.remote_platform.MakeDirectory(hidden_dir) if err.Fail(): raise RuntimeError( "Unable to create a directory '%s'." % hidden_dir) err = lldb.remote_platform.Put( lldb.SBFileSpec(os.path.join('hidden', shlib)), lldb.SBFileSpec(hidden_file)) if err.Fail(): raise RuntimeError( "Unable copy 'libloadunload_d.so' to '%s'.\n>>> %s" % (wd, err.GetCString())) # libloadunload_d.so does not appear in the image list because executable # dependencies are resolved relative to the debuggers PWD. Bug? @expectedFailureAll(oslist=["linux"]) @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support @not_remote_testsuite_ready @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently def test_modules_search_paths(self): """Test target modules list after loading a different copy of the library libd.dylib, and verifies that it works with 'target modules search-paths add'.""" if self.platformIsDarwin(): dylibName = 'libloadunload_d.dylib' else: dylibName = 'libloadunload_d.so' # The directory with the dynamic library we did not link to. new_dir = os.path.join(self.getBuildDir(), "hidden") old_dylib = os.path.join(self.getBuildDir(), dylibName) new_dylib = os.path.join(new_dir, dylibName) exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) self.expect("target modules list", substrs=[old_dylib]) # self.expect("target modules list -t 3", # patterns = ["%s-[^-]*-[^-]*" % self.getArchitecture()]) # Add an image search path substitution pair. self.runCmd( "target modules search-paths add %s %s" % (self.getBuildDir(), new_dir)) self.expect("target modules search-paths list", substrs=[self.getBuildDir(), new_dir]) self.expect( "target modules search-paths query %s" % self.getBuildDir(), "Image search path successfully transformed", substrs=[new_dir]) # Obliterate traces of libd from the old location. os.remove(old_dylib) # Inform (DY)LD_LIBRARY_PATH of the new path, too. env_cmd_string = "settings set target.env-vars " + self.dylibPath + "=" + new_dir if self.TraceOn(): print("Set environment to: ", env_cmd_string) self.runCmd(env_cmd_string) self.runCmd("settings show target.env-vars") remove_dyld_path_cmd = "settings remove target.env-vars " + self.dylibPath self.addTearDownHook( lambda: self.dbg.HandleCommand(remove_dyld_path_cmd)) self.runCmd("run") self.expect( "target modules list", "LLDB successfully locates the relocated dynamic library", substrs=[new_dylib]) # libloadunload_d.so does not appear in the image list because executable # dependencies are resolved relative to the debuggers PWD. Bug? @expectedFailureAll(oslist=["linux"]) @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support @expectedFailureAndroid # wrong source file shows up for hidden library @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently def test_dyld_library_path(self): """Test (DY)LD_LIBRARY_PATH after moving libd.dylib, which defines d_function, somewhere else.""" self.copy_shlibs_to_remote(hidden_dir=True) exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # Shut off ANSI color usage so we don't get ANSI escape sequences # mixed in with stop locations. self.dbg.SetUseColor(False) if self.platformIsDarwin(): dylibName = 'libloadunload_d.dylib' dsymName = 'libloadunload_d.dylib.dSYM' else: dylibName = 'libloadunload_d.so' # The directory to relocate the dynamic library and its debugging info. special_dir = "hidden" if lldb.remote_platform: wd = lldb.remote_platform.GetWorkingDirectory() else: wd = self.getBuildDir() old_dir = wd new_dir = os.path.join(wd, special_dir) old_dylib = os.path.join(old_dir, dylibName) remove_dyld_path_cmd = "settings remove target.env-vars " \ + self.dylibPath self.addTearDownHook( lambda: self.dbg.HandleCommand(remove_dyld_path_cmd)) # For now we don't track (DY)LD_LIBRARY_PATH, so the old # library will be in the modules list. self.expect("target modules list", substrs=[os.path.basename(old_dylib)], matching=True) lldbutil.run_break_set_by_file_and_line( self, "d.cpp", self.line_d_function, num_expected_locations=1) # After run, make sure the non-hidden library is picked up. self.expect("run", substrs=["return", "700"]) self.runCmd("continue") # Add the hidden directory first in the search path. env_cmd_string = ("settings set target.env-vars %s=%s" % (self.dylibPath, new_dir)) if not self.platformIsDarwin(): env_cmd_string += ":" + wd self.runCmd(env_cmd_string) - + # This time, the hidden library should be picked up. self.expect("run", substrs=["return", "12345"]) @expectedFailureAll( bugnumber="llvm.org/pr25805", hostoslist=["windows"], triple='.*-android') @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently def test_lldb_process_load_and_unload_commands(self): """Test that lldb process load/unload command work correctly.""" self.copy_shlibs_to_remote() exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # Break at main.cpp before the call to dlopen(). # Use lldb's process load command to load the dylib, instead. lldbutil.run_break_set_by_file_and_line( self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) self.runCmd("run", RUN_SUCCEEDED) ctx = self.platformContext dylibName = ctx.shlib_prefix + 'loadunload_a.' + ctx.shlib_extension localDylibPath = self.getBuildArtifact(dylibName) if lldb.remote_platform: wd = lldb.remote_platform.GetWorkingDirectory() remoteDylibPath = lldbutil.join_remote_paths(wd, dylibName) else: remoteDylibPath = localDylibPath # Make sure that a_function does not exist at this point. self.expect( "image lookup -n a_function", "a_function should not exist yet", error=True, matching=False, patterns=["1 match found"]) # Use lldb 'process load' to load the dylib. self.expect( "process load %s --install=%s" % (localDylibPath, remoteDylibPath), "%s loaded correctly" % dylibName, patterns=[ 'Loading "%s".*ok' % localDylibPath, 'Image [0-9]+ loaded']) # Search for and match the "Image ([0-9]+) loaded" pattern. output = self.res.GetOutput() pattern = re.compile("Image ([0-9]+) loaded") for l in output.split(os.linesep): #print("l:", l) match = pattern.search(l) if match: break index = match.group(1) # Now we should have an entry for a_function. self.expect( "image lookup -n a_function", "a_function should now exist", patterns=[ "1 match found .*%s" % dylibName]) # Use lldb 'process unload' to unload the dylib. self.expect( "process unload %s" % index, "%s unloaded correctly" % dylibName, patterns=[ "Unloading .* with index %s.*ok" % index]) self.runCmd("process continue") @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support def test_load_unload(self): """Test breakpoint by name works correctly with dlopen'ing.""" self.copy_shlibs_to_remote() exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # Break by function name a_function (not yet loaded). lldbutil.run_break_set_by_symbol( self, "a_function", num_expected_locations=0) self.runCmd("run", RUN_SUCCEEDED) # The stop reason of the thread should be breakpoint and at a_function. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'a_function', 'stop reason = breakpoint']) # The breakpoint should have a hit count of 1. self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, substrs=[' resolved, hit count = 1']) # Issue the 'contnue' command. We should stop agaian at a_function. # The stop reason of the thread should be breakpoint and at a_function. self.runCmd("continue") # rdar://problem/8508987 # The a_function breakpoint should be encountered twice. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'a_function', 'stop reason = breakpoint']) # The breakpoint should have a hit count of 2. self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, substrs=[' resolved, hit count = 2']) @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently def test_step_over_load(self): """Test stepping over code that loads a shared library works correctly.""" self.copy_shlibs_to_remote() exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # Break by function name a_function (not yet loaded). lldbutil.run_break_set_by_file_and_line( self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) self.runCmd("run", RUN_SUCCEEDED) # The stop reason of the thread should be breakpoint and at a_function. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) self.runCmd( "thread step-over", "Stepping over function that loads library") # The stop reason should be step end. self.expect("thread list", "step over succeeded.", substrs=['stopped', 'stop reason = step over']) # We can't find a breakpoint location for d_init before launching because # executable dependencies are resolved relative to the debuggers PWD. Bug? @expectedFailureAll(oslist=["linux"]) @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently def test_static_init_during_load(self): """Test that we can set breakpoints correctly in static initializers""" self.copy_shlibs_to_remote() exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) a_init_bp_num = lldbutil.run_break_set_by_symbol( self, "a_init", num_expected_locations=0) b_init_bp_num = lldbutil.run_break_set_by_symbol( self, "b_init", num_expected_locations=0) d_init_bp_num = lldbutil.run_break_set_by_symbol( self, "d_init", num_expected_locations=1) self.runCmd("run", RUN_SUCCEEDED) self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'd_init', 'stop reason = breakpoint %d' % d_init_bp_num]) self.runCmd("continue") self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'b_init', 'stop reason = breakpoint %d' % b_init_bp_num]) self.expect("thread backtrace", substrs=['b_init', 'dlopen', 'main']) self.runCmd("continue") self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'a_init', 'stop reason = breakpoint %d' % a_init_bp_num]) self.expect("thread backtrace", substrs=['a_init', 'dlopen', 'main']) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/load_using_paths/TestLoadUsingPaths.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/load_using_paths/TestLoadUsingPaths.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/load_using_paths/TestLoadUsingPaths.py (revision 337147) @@ -1,143 +1,143 @@ """ Test that SBProcess.LoadImageUsingPaths works correctly. """ from __future__ import print_function import os import time import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil @skipIfWindows # The Windows platform doesn't implement DoLoadImage. class LoadUsingPathsTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True - + def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Make the hidden directory in the build hierarchy: lldbutil.mkdir_p(self.getBuildArtifact("hidden")) # Invoke the default build rule. self.build() ext = 'so' if self.platformIsDarwin(): ext = 'dylib' self.lib_name = 'libloadunload.' + ext self.wd = self.getBuildDir() self.hidden_dir = os.path.join(self.wd, 'hidden') self.hidden_lib = os.path.join(self.hidden_dir, self.lib_name) @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support @not_remote_testsuite_ready @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently def test_load_using_paths(self): """Test that we can load a module by providing a set of search paths.""" if self.platformIsDarwin(): dylibName = 'libloadunload_d.dylib' else: dylibName = 'libloadunload_d.so' # The directory with the dynamic library we did not link to. path_dir = os.path.join(self.getBuildDir(), "hidden") - (target, process, thread, - _) = lldbutil.run_to_source_breakpoint(self, + (target, process, thread, + _) = lldbutil.run_to_source_breakpoint(self, "Break here to do the load using paths", lldb.SBFileSpec("main.cpp")) error = lldb.SBError() lib_spec = lldb.SBFileSpec(self.lib_name) paths = lldb.SBStringList() paths.AppendString(self.wd) paths.AppendString(os.path.join(self.wd, "no_such_dir")) out_spec = lldb.SBFileSpec() - + # First try with no correct directories on the path, and make sure that doesn't blow up: token = process.LoadImageUsingPaths(lib_spec, paths, out_spec, error) self.assertEqual(token, lldb.LLDB_INVALID_IMAGE_TOKEN, "Only looked on the provided path.") - + # Now add the correct dir to the paths list and try again: paths.AppendString(self.hidden_dir) token = process.LoadImageUsingPaths(lib_spec, paths, out_spec, error) self.assertNotEqual(token, lldb.LLDB_INVALID_IMAGE_TOKEN, "Got a valid token") self.assertEqual(out_spec, lldb.SBFileSpec(self.hidden_lib), "Found the expected library") - + # Make sure this really is in the image list: loaded_module = target.FindModule(out_spec) self.assertTrue(loaded_module.IsValid(), "The loaded module is in the image list.") # Now see that we can call a function in the loaded module. value = thread.frames[0].EvaluateExpression("d_function()", lldb.SBExpressionOptions()) self.assertTrue(value.GetError().Success(), "Got a value from the expression") ret_val = value.GetValueAsSigned() self.assertEqual(ret_val, 12345, "Got the right value") # Make sure the token works to unload it: process.UnloadImage(token) - # Make sure this really is no longer in the image list: + # Make sure this really is no longer in the image list: loaded_module = target.FindModule(out_spec) self.assertFalse(loaded_module.IsValid(), "The unloaded module is no longer in the image list.") - + # Make sure a relative path also works: paths.Clear() paths.AppendString(os.path.join(self.wd, "no_such_dir")) paths.AppendString(self.wd) relative_spec = lldb.SBFileSpec(os.path.join("hidden", self.lib_name)) out_spec = lldb.SBFileSpec() token = process.LoadImageUsingPaths(relative_spec, paths, out_spec, error) self.assertNotEqual(token, lldb.LLDB_INVALID_IMAGE_TOKEN, "Got a valid token with relative path") self.assertEqual(out_spec, lldb.SBFileSpec(self.hidden_lib), "Found the expected library with relative path") process.UnloadImage(token) - + # Make sure the presence of an empty path doesn't mess anything up: paths.Clear() paths.AppendString("") paths.AppendString(os.path.join(self.wd, "no_such_dir")) paths.AppendString(self.wd) relative_spec = lldb.SBFileSpec(os.path.join("hidden", self.lib_name)) out_spec = lldb.SBFileSpec() token = process.LoadImageUsingPaths(relative_spec, paths, out_spec, error) self.assertNotEqual(token, lldb.LLDB_INVALID_IMAGE_TOKEN, "Got a valid token with included empty path") self.assertEqual(out_spec, lldb.SBFileSpec(self.hidden_lib), "Found the expected library with included empty path") process.UnloadImage(token) - + # Finally, passing in an absolute path should work like the basename: # This should NOT work because we've taken hidden_dir off the paths: abs_spec = lldb.SBFileSpec(os.path.join(self.hidden_dir, self.lib_name)) token = process.LoadImageUsingPaths(lib_spec, paths, out_spec, error) self.assertEqual(token, lldb.LLDB_INVALID_IMAGE_TOKEN, "Only looked on the provided path.") # But it should work when we add the dir: # Now add the correct dir to the paths list and try again: paths.AppendString(self.hidden_dir) token = process.LoadImageUsingPaths(lib_spec, paths, out_spec, error) self.assertNotEqual(token, lldb.LLDB_INVALID_IMAGE_TOKEN, "Got a valid token") self.assertEqual(out_spec, lldb.SBFileSpec(self.hidden_lib), "Found the expected library") - + Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/pre_run_dylibs/TestPreRunDylibs.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/pre_run_dylibs/TestPreRunDylibs.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/pre_run_dylibs/TestPreRunDylibs.py (revision 337147) @@ -1,38 +1,38 @@ from __future__ import print_function import unittest2 import lldb from lldbsuite.test.lldbtest import * import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.decorators import * class TestPreRunLibraries(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True def setUp(self): # Call super's setUp(). TestBase.setUp(self) @skipIf(oslist=no_match(['darwin','macos'])) def test(self): """Test that we find directly linked dylib pre-run.""" self.build() target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) self.assertTrue(target, VALID_TARGET) # I don't know what the name of a shared library # extension is in general, so instead of using FindModule, # I'll iterate through the module and do a basename match. found_it = False for module in target.modules: file_name = module.GetFileSpec().GetFilename() if file_name.find("unlikely_name") != -1: found_it = True break self.assertTrue(found_it, "Couldn't find unlikely_to_occur_name in loaded libraries.") - + Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/register/intel_avx/TestZMMRegister.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/register/intel_avx/TestZMMRegister.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/register/intel_avx/TestZMMRegister.py (revision 337147) @@ -1,126 +1,126 @@ """ Test that we correctly read the YMM registers. """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestYMMRegister(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True @skipUnlessDarwin @skipIfiOSSimulator @skipIf(archs=no_match(['i386', 'x86_64'])) @debugserver_test @skipUnlessFeature('hw.optional.avx512f') def test(self): self.build(dictionary={"CFLAGS_EXTRAS": "-mavx512f"}) self.setTearDownCleanup() exe = self.getBuildArtifact("a.out") target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) byte_pattern1 = 0x80 byte_pattern2 = 0xFF # This test is working with assembly instructions; it'll make # it easier to debug the console output if the assembly is # displayed. self.runCmd("settings set stop-disassembly-display always") # Launch the process and stop. self.expect("run", PROCESS_STOPPED, substrs=['stopped']) # Check stop reason; Should be either signal SIGTRAP or EXC_BREAKPOINT output = self.res.GetOutput() matched = False substrs = [ 'stop reason = EXC_BREAKPOINT', 'stop reason = signal SIGTRAP'] for str1 in substrs: matched = output.find(str1) != -1 with recording(self, False) as sbuf: print("%s sub string: %s" % ('Expecting', str1), file=sbuf) print("Matched" if matched else "Not Matched", file=sbuf) if matched: break self.assertTrue(matched, STOPPED_DUE_TO_SIGNAL) if self.getArchitecture() == 'x86_64': register_range = 16 else: register_range = 8 for i in range(register_range): j = i - ((i / 8) * 8) self.runCmd("thread step-inst") register_byte = (byte_pattern1 | j) pattern = "ymm" + str(i) + " = " + str('{') + ( str(hex(register_byte)) + ' ') * 31 + str(hex(register_byte)) + str('}') self.expect( "register read ymm" + str(i), substrs=[pattern]) register_byte = (byte_pattern2 | j) pattern = "ymm" + str(i) + " = " + str('{') + ( str(hex(register_byte)) + ' ') * 31 + str(hex(register_byte)) + str('}') self.runCmd("thread step-inst") self.expect( "register read ymm" + str(i), substrs=[pattern]) - + self.expect("continue", PROCESS_STOPPED, substrs=['stopped']) # Check stop reason; Should be either signal SIGTRAP or EXC_BREAKPOINT output = self.res.GetOutput() matched = False substrs = [ 'stop reason = EXC_BREAKPOINT', 'stop reason = signal SIGTRAP'] for str1 in substrs: matched = output.find(str1) != -1 with recording(self, False) as sbuf: print("%s sub string: %s" % ('Expecting', str1), file=sbuf) print("Matched" if matched else "Not Matched", file=sbuf) if matched: break self.assertTrue(matched, STOPPED_DUE_TO_SIGNAL) if self.getArchitecture() == 'x86_64': register_range = 32 else: register_range = 8 for i in range(register_range): j = i - ((i / 8) * 8) self.runCmd("thread step-inst") self.runCmd("thread step-inst") register_byte = (byte_pattern2 | j) pattern = "zmm" + str(i) + " = " + str('{') + ( str(hex(register_byte)) + ' ') * 63 + str(hex(register_byte)) + str('}') self.expect( "register read zmm" + str(i), substrs=[pattern]) register_byte = (byte_pattern2 | j) pattern = "zmm" + str(i) + " = " + str('{') + ( str(hex(register_byte)) + ' ') * 63 + str(hex(register_byte)) + str('}') self.runCmd("thread step-inst") self.expect( "register read zmm" + str(i), substrs=[pattern]) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/return-value/TestReturnValue.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/return-value/TestReturnValue.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/return-value/TestReturnValue.py (revision 337147) @@ -1,246 +1,246 @@ """ Test getting return-values correctly when stepping out """ from __future__ import print_function import os import time import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ReturnValueTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def affected_by_pr33042(self): return ("clang" in self.getCompiler() and self.getArchitecture() == "aarch64" and self.getPlatform() == "linux") # ABIMacOSX_arm can't fetch simple values inside a structure def affected_by_radar_34562999(self): return (self.getArchitecture() == 'armv7' or self.getArchitecture() == 'armv7k') and self.platformIsDarwin() @expectedFailureAll(oslist=["freebsd"], archs=["i386"]) @expectedFailureAll(oslist=["macosx"], archs=["i386"], bugnumber="") @expectedFailureAll( oslist=["linux"], compiler="clang", compiler_version=[ "<=", "3.6"], archs=["i386"]) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778") @add_test_categories(['pyapi']) def test_with_python(self): """Test getting return values from stepping out.""" self.build() exe = self.getBuildArtifact("a.out") (self.target, self.process, thread, inner_sint_bkpt) = lldbutil.run_to_name_breakpoint(self, "inner_sint", exe_name = exe) error = lldb.SBError() # inner_sint returns the variable value, so capture that here: in_int = thread.GetFrameAtIndex(0).FindVariable( "value").GetValueAsSigned(error) self.assertTrue(error.Success()) thread.StepOut() self.assertTrue(self.process.GetState() == lldb.eStateStopped) self.assertTrue(thread.GetStopReason() == lldb.eStopReasonPlanComplete) frame = thread.GetFrameAtIndex(0) fun_name = frame.GetFunctionName() self.assertTrue(fun_name == "outer_sint") return_value = thread.GetStopReturnValue() self.assertTrue(return_value.IsValid()) ret_int = return_value.GetValueAsSigned(error) self.assertTrue(error.Success()) self.assertTrue(in_int == ret_int) # Run again and we will stop in inner_sint the second time outer_sint is called. # Then test stepping out two frames at once: - + thread_list = lldbutil.continue_to_breakpoint(self.process, inner_sint_bkpt) self.assertTrue(len(thread_list) == 1) thread = thread_list[0] # We are done with the inner_sint breakpoint: self.target.BreakpointDelete(inner_sint_bkpt.GetID()) frame = thread.GetFrameAtIndex(1) fun_name = frame.GetFunctionName() self.assertTrue(fun_name == "outer_sint") in_int = frame.FindVariable("value").GetValueAsSigned(error) self.assertTrue(error.Success()) thread.StepOutOfFrame(frame) self.assertTrue(self.process.GetState() == lldb.eStateStopped) self.assertTrue(thread.GetStopReason() == lldb.eStopReasonPlanComplete) frame = thread.GetFrameAtIndex(0) fun_name = frame.GetFunctionName() self.assertTrue(fun_name == "main") ret_value = thread.GetStopReturnValue() self.assertTrue(return_value.IsValid()) ret_int = ret_value.GetValueAsSigned(error) self.assertTrue(error.Success()) self.assertTrue(2 * in_int == ret_int) # Now try some simple returns that have different types: inner_float_bkpt = self.target.BreakpointCreateByName( "inner_float", exe) self.assertTrue(inner_float_bkpt, VALID_BREAKPOINT) self.process.Continue() thread_list = lldbutil.get_threads_stopped_at_breakpoint( self.process, inner_float_bkpt) self.assertTrue(len(thread_list) == 1) thread = thread_list[0] self.target.BreakpointDelete(inner_float_bkpt.GetID()) frame = thread.GetFrameAtIndex(0) in_value = frame.FindVariable("value") in_float = float(in_value.GetValue()) thread.StepOut() self.assertTrue(self.process.GetState() == lldb.eStateStopped) self.assertTrue(thread.GetStopReason() == lldb.eStopReasonPlanComplete) frame = thread.GetFrameAtIndex(0) fun_name = frame.GetFunctionName() self.assertTrue(fun_name == "outer_float") #return_value = thread.GetStopReturnValue() #self.assertTrue(return_value.IsValid()) #return_float = float(return_value.GetValue()) #self.assertTrue(in_float == return_float) if not self.affected_by_radar_34562999(): self.return_and_test_struct_value("return_one_int") self.return_and_test_struct_value("return_two_int") self.return_and_test_struct_value("return_three_int") self.return_and_test_struct_value("return_four_int") if not self.affected_by_pr33042(): self.return_and_test_struct_value("return_five_int") self.return_and_test_struct_value("return_two_double") self.return_and_test_struct_value("return_one_double_two_float") self.return_and_test_struct_value("return_one_int_one_float_one_int") self.return_and_test_struct_value("return_one_pointer") self.return_and_test_struct_value("return_two_pointer") self.return_and_test_struct_value("return_one_float_one_pointer") self.return_and_test_struct_value("return_one_int_one_pointer") self.return_and_test_struct_value("return_three_short_one_float") self.return_and_test_struct_value("return_one_int_one_double") self.return_and_test_struct_value("return_one_int_one_double_one_int") self.return_and_test_struct_value( "return_one_short_one_double_one_short") self.return_and_test_struct_value("return_one_float_one_int_one_float") self.return_and_test_struct_value("return_two_float") # I am leaving out the packed test until we have a way to tell CLANG # about alignment when reading DWARF for packed types. #self.return_and_test_struct_value ("return_one_int_one_double_packed") self.return_and_test_struct_value("return_one_int_one_long") @expectedFailureAll(oslist=["freebsd"], archs=["i386"]) @expectedFailureAll(oslist=["macosx"], archs=["i386"], bugnumber="") @expectedFailureAll( oslist=["linux"], compiler="clang", compiler_version=[ "<=", "3.6"], archs=["i386"]) @expectedFailureAll(compiler=["gcc"], archs=["x86_64", "i386"]) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778") @skipIfDarwinEmbedded # ABIMacOSX_arm64 doesn't get structs this big correctly def test_vector_values(self): self.build() exe = self.getBuildArtifact("a.out") error = lldb.SBError() self.target = self.dbg.CreateTarget(exe) self.assertTrue(self.target, VALID_TARGET) main_bktp = self.target.BreakpointCreateByName("main", exe) self.assertTrue(main_bktp, VALID_BREAKPOINT) self.process = self.target.LaunchSimple( None, None, self.get_process_working_directory()) self.assertEqual(len(lldbutil.get_threads_stopped_at_breakpoint( self.process, main_bktp)), 1) self.return_and_test_struct_value("return_vector_size_float32_8") self.return_and_test_struct_value("return_vector_size_float32_16") self.return_and_test_struct_value("return_vector_size_float32_32") self.return_and_test_struct_value("return_ext_vector_size_float32_2") self.return_and_test_struct_value("return_ext_vector_size_float32_4") self.return_and_test_struct_value("return_ext_vector_size_float32_8") def return_and_test_struct_value(self, func_name): """Pass in the name of the function to return from - takes in value, returns value.""" # Set the breakpoint, run to it, finish out. bkpt = self.target.BreakpointCreateByName(func_name) self.assertTrue(bkpt.GetNumResolvedLocations() > 0) self.process.Continue() thread_list = lldbutil.get_threads_stopped_at_breakpoint( self.process, bkpt) self.assertTrue(len(thread_list) == 1) thread = thread_list[0] self.target.BreakpointDelete(bkpt.GetID()) in_value = thread.GetFrameAtIndex(0).FindVariable("value") self.assertTrue(in_value.IsValid()) num_in_children = in_value.GetNumChildren() # This is a little hokey, but if we don't get all the children now, then # once we've stepped we won't be able to get them? for idx in range(0, num_in_children): in_child = in_value.GetChildAtIndex(idx) in_child_str = in_child.GetValue() thread.StepOut() self.assertTrue(self.process.GetState() == lldb.eStateStopped) self.assertTrue(thread.GetStopReason() == lldb.eStopReasonPlanComplete) # Assuming all these functions step out to main. Could figure out the caller dynamically # if that would add something to the test. frame = thread.GetFrameAtIndex(0) fun_name = frame.GetFunctionName() self.assertTrue(fun_name == "main") frame = thread.GetFrameAtIndex(0) ret_value = thread.GetStopReturnValue() self.assertTrue(ret_value.IsValid()) num_ret_children = ret_value.GetNumChildren() self.assertTrue(num_in_children == num_ret_children) for idx in range(0, num_ret_children): in_child = in_value.GetChildAtIndex(idx) ret_child = ret_value.GetChildAtIndex(idx) in_child_str = in_child.GetValue() ret_child_str = ret_child.GetValue() self.assertEqual(in_child_str, ret_child_str) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/TestCreateDuringStep.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/TestCreateDuringStep.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/create_during_step/TestCreateDuringStep.py (revision 337147) @@ -1,154 +1,154 @@ """ Test number of threads. """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class CreateDuringStepTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) @expectedFailureAll( oslist=["linux"], bugnumber="llvm.org/pr15824 thread states not properly maintained") @expectedFailureAll( oslist=lldbplatformutil.getDarwinOSTriples(), bugnumber="llvm.org/pr15824 thread states not properly maintained, ") @expectedFailureAll( oslist=["freebsd"], bugnumber="llvm.org/pr18190 thread states not properly maintained") @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr24668: Breakpoints not resolved correctly") def test_step_inst(self): """Test thread creation during step-inst handling.""" self.build(dictionary=self.getBuildFlags()) self.create_during_step_base( "thread step-inst -m all-threads", 'stop reason = instruction step') @expectedFailureAll( oslist=["linux"], bugnumber="llvm.org/pr15824 thread states not properly maintained") @expectedFailureAll( oslist=lldbplatformutil.getDarwinOSTriples(), bugnumber="llvm.org/pr15824 thread states not properly maintained, ") @expectedFailureAll( oslist=["freebsd"], bugnumber="llvm.org/pr18190 thread states not properly maintained") @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr24668: Breakpoints not resolved correctly") def test_step_over(self): """Test thread creation during step-over handling.""" self.build(dictionary=self.getBuildFlags()) self.create_during_step_base( "thread step-over -m all-threads", 'stop reason = step over') @expectedFailureAll( oslist=["linux"], bugnumber="llvm.org/pr15824 thread states not properly maintained") @expectedFailureAll( oslist=lldbplatformutil.getDarwinOSTriples(), bugnumber="") @expectedFailureAll( oslist=["freebsd"], bugnumber="llvm.org/pr18190 thread states not properly maintained") @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr24668: Breakpoints not resolved correctly") def test_step_in(self): """Test thread creation during step-in handling.""" self.build(dictionary=self.getBuildFlags()) self.create_during_step_base( "thread step-in -m all-threads", 'stop reason = step in') def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Find the line numbers to break and continue. self.breakpoint = line_number('main.cpp', '// Set breakpoint here') self.continuepoint = line_number('main.cpp', '// Continue from here') def create_during_step_base(self, step_cmd, step_stop_reason): """Test thread creation while using step-in.""" exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # Get the target process target = self.dbg.GetSelectedTarget() # This should create a breakpoint in the stepping thread. - self.bkpt = target.BreakpointCreateByLocation("main.cpp", self.breakpoint) + self.bkpt = target.BreakpointCreateByLocation("main.cpp", self.breakpoint) # Run the program. self.runCmd("run", RUN_SUCCEEDED) process = target.GetProcess() # The stop reason of the thread should be breakpoint. stepping_thread = lldbutil.get_one_thread_stopped_at_breakpoint(process, self.bkpt) self.assertTrue(stepping_thread.IsValid(), "We stopped at the right breakpoint") # Get the number of threads num_threads = process.GetNumThreads() # Make sure we see only two threads self.assertTrue( num_threads == 2, 'Number of expected threads and actual threads do not match.') # Get the thread objects thread1 = process.GetThreadAtIndex(0) thread2 = process.GetThreadAtIndex(1) current_line = self.breakpoint # Keep stepping until we've reached our designated continue point while current_line != self.continuepoint: if stepping_thread != process.GetSelectedThread(): process.SetSelectedThread(stepping_thread) self.runCmd(step_cmd) frame = stepping_thread.GetFrameAtIndex(0) current_line = frame.GetLineEntry().GetLine() # Make sure we're still where we thought we were self.assertTrue( current_line >= self.breakpoint, "Stepped to unexpected line, " + str(current_line)) self.assertTrue( current_line <= self.continuepoint, "Stepped to unexpected line, " + str(current_line)) # Update the number of threads num_threads = process.GetNumThreads() # Check to see that we increased the number of threads as expected self.assertTrue( num_threads == 3, 'Number of expected threads and actual threads do not match after thread exit.') stop_reason = stepping_thread.GetStopReason() self.assertEqual(stop_reason, lldb.eStopReasonPlanComplete, "Stopped for plan completion") # Run to completion self.runCmd("process continue") # At this point, the inferior process should have exited. self.assertTrue( process.GetState() == lldb.eStateExited, PROCESS_EXITED) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py (revision 337147) @@ -1,125 +1,125 @@ """ Test number of threads. """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * import lldbsuite.test.lldbutil as lldbutil class NumberOfThreadsTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Find the line numbers for our break points. self.thread3_notify_all_line = line_number('main.cpp', '// Set thread3 break point on notify_all at this line.') self.thread3_before_lock_line = line_number('main.cpp', '// thread3-before-lock') def test_number_of_threads(self): """Test number of threads.""" self.build() exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # This should create a breakpoint with 1 location. lldbutil.run_break_set_by_file_and_line( self, "main.cpp", self.thread3_notify_all_line, num_expected_locations=1) # The breakpoint list should show 1 location. self.expect( "breakpoint list -f", "Breakpoint location shown correctly", substrs=[ "1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % self.thread3_notify_all_line]) # Run the program. self.runCmd("run", RUN_SUCCEEDED) # Stopped once. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=["stop reason = breakpoint 1."]) # Get the target process target = self.dbg.GetSelectedTarget() process = target.GetProcess() # Get the number of threads num_threads = process.GetNumThreads() # Using std::thread may involve extra threads, so we assert that there are # at least 4 rather than exactly 4. self.assertTrue( num_threads >= 13, 'Number of expected threads and actual threads do not match.') @skipIfDarwin # rdar://33462362 @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr37658") def test_unique_stacks(self): """Test backtrace unique with multiple threads executing the same stack.""" self.build() exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # Set a break point on the thread3 notify all (should get hit on threads 4-13). lldbutil.run_break_set_by_file_and_line( self, "main.cpp", self.thread3_before_lock_line, num_expected_locations=1) # Run the program. self.runCmd("run", RUN_SUCCEEDED) # Stopped once. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=["stop reason = breakpoint 1."]) process = self.process() # Get the number of threads num_threads = process.GetNumThreads() # Using std::thread may involve extra threads, so we assert that there are # at least 10 thread3's rather than exactly 10. self.assertTrue( num_threads >= 10, 'Number of expected threads and actual threads do not match.') - + # Attempt to walk each of the thread's executing the thread3 function to # the same breakpoint. def is_thread3(thread): for frame in thread: if "thread3" in frame.GetFunctionName(): return True return False expect_threads = "" for i in range(num_threads): thread = process.GetThreadAtIndex(i) self.assertTrue(thread.IsValid()) if not is_thread3(thread): continue # If we aren't stopped out the thread breakpoint try to resume. if thread.GetStopReason() != lldb.eStopReasonBreakpoint: self.runCmd("thread continue %d"%(i+1)) self.assertEqual(thread.GetStopReason(), lldb.eStopReasonBreakpoint) expect_threads += " #%d"%(i+1) # Construct our expected back trace string expect_string = "10 thread(s)%s" % (expect_threads) # Now that we are stopped, we should have 10 threads waiting in the # thread3 function. All of these threads should show as one stack. self.expect("thread backtrace unique", "Backtrace with unique stack shown correctly", substrs=[expect_string, "main.cpp:%d"%self.thread3_before_lock_line]) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/step_until/TestStepUntil.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/step_until/TestStepUntil.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/step_until/TestStepUntil.py (revision 337147) @@ -1,89 +1,89 @@ """Test stepping over vrs. hitting breakpoints & subsequent stepping in various forms.""" from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class StepUntilTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Find the line numbers that we will step to in main: self.main_source = "main.c" self.less_than_two = line_number('main.c', 'Less than 2') self.greater_than_two = line_number('main.c', 'Greater than or equal to 2.') self.back_out_in_main = line_number('main.c', 'Back out in main') def do_until (self, args, until_lines, expected_linenum): self.build() exe = self.getBuildArtifact("a.out") target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) main_source_spec = lldb.SBFileSpec(self.main_source) break_before = target.BreakpointCreateBySourceRegex( 'At the start', main_source_spec) self.assertTrue(break_before, VALID_BREAKPOINT) # Now launch the process, and do not stop at entry point. process = target.LaunchSimple( args, None, self.get_process_working_directory()) self.assertTrue(process, PROCESS_IS_VALID) # The stop reason of the thread should be breakpoint. threads = lldbutil.get_threads_stopped_at_breakpoint( process, break_before) if len(threads) != 1: self.fail("Failed to stop at first breakpoint in main.") thread = threads[0] return thread thread = self.common_setup(None) cmd_interp = self.dbg.GetCommandInterpreter() ret_obj = lldb.SBCommandReturnObject() cmd_line = "thread until" for line_num in until_lines: cmd_line += " %d"%(line_num) - + cmd_interp.HandleCommand(cmd_line, ret_obj) self.assertTrue(ret_obj.Succeeded(), "'%s' failed: %s."%(cmd_line, ret_obj.GetError())) frame = thread.frames[0] line = frame.GetLineEntry().GetLine() self.assertEqual(line, expected_linenum, 'Did not get the expected stop line number') def test_hitting_one (self): """Test thread step until - targeting one line and hitting it.""" self.do_until(None, [self.less_than_two], self.less_than_two) def test_targetting_two_hitting_first (self): """Test thread step until - targeting two lines and hitting one.""" self.do_until(["foo", "bar", "baz"], [self.less_than_two, self.greater_than_two], self.greater_than_two) def test_targetting_two_hitting_second (self): """Test thread step until - targeting two lines and hitting the other one.""" self.do_until(None, [self.less_than_two, self.greater_than_two], self.less_than_two) def test_missing_one (self): """Test thread step until - targeting one line and missing it.""" self.do_until(["foo", "bar", "baz"], [self.less_than_two], self.back_out_in_main) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/watchpoint/multi_watchpoint_slots/TestWatchpointMultipleSlots.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/watchpoint/multi_watchpoint_slots/TestWatchpointMultipleSlots.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/watchpoint/multi_watchpoint_slots/TestWatchpointMultipleSlots.py (revision 337147) @@ -1,103 +1,103 @@ """ Test watchpoint slots we should not be able to install multiple watchpoints within same word boundary. We should be able to install individual watchpoints on any of the bytes, half-word, or word. This is only for ARM/AArch64 targets. """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class WatchpointSlotsTestCase(TestBase): NO_DEBUG_INFO_TESTCASE = True mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Source filename. self.source = 'main.c' # Output filename. self.exe_name = self.getBuildArtifact("a.out") self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name} # This is a arm and aarch64 specific test case. No other architectures tested. @skipIf(archs=no_match(['arm', 'aarch64'])) def test_multiple_watchpoints_on_same_word(self): self.build(dictionary=self.d) self.setTearDownCleanup(dictionary=self.d) exe = self.getBuildArtifact(self.exe_name) self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # Detect line number after which we are going to increment arrayName. loc_line = line_number('main.c', '// About to write byteArray') # Set a breakpoint on the line detected above. lldbutil.run_break_set_by_file_and_line( self, "main.c", loc_line, num_expected_locations=1, loc_exact=True) # Run the program. self.runCmd("run", RUN_SUCCEEDED) # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) # Delete breakpoint we just hit. self.expect("breakpoint delete 1", substrs=['1 breakpoints deleted']) # Set a watchpoint at byteArray[0] self.expect("watchpoint set variable byteArray[0]", WATCHPOINT_CREATED, substrs=['Watchpoint created','size = 1']) # Use the '-v' option to do verbose listing of the watchpoint. # The hit count should be 0 initially. self.expect("watchpoint list -v 1", substrs=['hit_count = 0']) # debugserver on ios doesn't give an error, it creates another watchpoint, # only expect errors on non-darwin platforms. if not self.platformIsDarwin(): # Try setting a watchpoint at byteArray[1] self.expect("watchpoint set variable byteArray[1]", error=True, substrs=['Watchpoint creation failed']) self.runCmd("process continue") # We should be stopped due to the watchpoint. # The stop reason of the thread should be watchpoint. self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT, substrs=['stopped', 'stop reason = watchpoint 1']) # Delete the watchpoint we hit above successfully. self.expect("watchpoint delete 1", substrs=['1 watchpoints deleted']) # Set a watchpoint at byteArray[3] self.expect("watchpoint set variable byteArray[3]", WATCHPOINT_CREATED, substrs=['Watchpoint created','size = 1']) - + # Resume inferior. self.runCmd("process continue") # We should be stopped due to the watchpoint. # The stop reason of the thread should be watchpoint. if self.platformIsDarwin(): # On darwin we'll hit byteArray[3] which is watchpoint 2 self.expect("thread list -v", STOPPED_DUE_TO_WATCHPOINT, substrs=['stopped', 'stop reason = watchpoint 2']) else: self.expect("thread list -v", STOPPED_DUE_TO_WATCHPOINT, substrs=['stopped', 'stop reason = watchpoint 3']) - + # Resume inferior. self.runCmd("process continue") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_disable/TestWatchpointDisable.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_disable/TestWatchpointDisable.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/watchpoint/watchpoint_disable/TestWatchpointDisable.py (revision 337147) @@ -1,81 +1,81 @@ """ Test that the SBWatchpoint::SetEnable API works. """ import os import lldb from lldbsuite.test.lldbtest import * from lldbsuite.test.decorators import * from lldbsuite.test import lldbplatform, lldbplatformutil class TestWatchpointSetEnable(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr24446: WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows") def test_disable_works (self): """Set a watchpoint, disable it, and make sure it doesn't get hit.""" self.build() self.do_test(False) @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr24446: WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows") def test_disable_enable_works (self): """Set a watchpoint, disable it, and make sure it doesn't get hit.""" self.build() self.do_test(True) def do_test(self, test_enable): """Set a watchpoint, disable it and make sure it doesn't get hit.""" exe = self.getBuildArtifact("a.out") main_file_spec = lldb.SBFileSpec("main.c") # Create a target by the debugger. self.target = self.dbg.CreateTarget(exe) self.assertTrue(self.target, VALID_TARGET) bkpt_before = self.target.BreakpointCreateBySourceRegex("Set a breakpoint here", main_file_spec) self.assertEqual(bkpt_before.GetNumLocations(), 1, "Failed setting the before breakpoint.") bkpt_after = self.target.BreakpointCreateBySourceRegex("We should have stopped", main_file_spec) self.assertEqual(bkpt_after.GetNumLocations(), 1, "Failed setting the after breakpoint.") process = self.target.LaunchSimple( None, None, self.get_process_working_directory()) self.assertTrue(process, PROCESS_IS_VALID) thread = lldbutil.get_one_thread_stopped_at_breakpoint(process, bkpt_before) self.assertTrue(thread.IsValid(), "We didn't stop at the before breakpoint.") ret_val = lldb.SBCommandReturnObject() self.dbg.GetCommandInterpreter().HandleCommand("watchpoint set variable -w write global_var", ret_val) self.assertTrue(ret_val.Succeeded(), "Watchpoint set variable did not return success.") wp = self.target.FindWatchpointByID(1) self.assertTrue(wp.IsValid(), "Didn't make a valid watchpoint.") self.assertTrue(wp.GetWatchAddress() != lldb.LLDB_INVALID_ADDRESS, "Watch address is invalid") wp.SetEnabled(False) self.assertTrue(not wp.IsEnabled(), "The watchpoint thinks it is still enabled") - + process.Continue() - + stop_reason = thread.GetStopReason() self.assertEqual(stop_reason, lldb.eStopReasonBreakpoint, "We didn't stop at our breakpoint.") if test_enable: wp.SetEnabled(True) self.assertTrue(wp.IsEnabled(), "The watchpoint thinks it is still disabled.") process.Continue() stop_reason = thread.GetStopReason() self.assertEqual(stop_reason, lldb.eStopReasonWatchpoint, "We didn't stop at our watchpoint") - + Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/find_struct_type/TestFindStructTypes.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/find_struct_type/TestFindStructTypes.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/find_struct_type/TestFindStructTypes.py (revision 337147) @@ -1,67 +1,67 @@ """ Make sure FindTypes finds struct types with the struct prefix. """ from __future__ import print_function import os import time import re import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * class TestFindTypesOnStructType(TestBase): mydir = TestBase.compute_mydir(__file__) - # If your test case doesn't stress debug info, the + # If your test case doesn't stress debug info, the # set this to true. That way it won't be run once for # each debug info format. NO_DEBUG_INFO_TESTCASE = True def test_find_types_struct_type(self): """Make sure FindTypes actually finds 'struct typename' not just 'typename'.""" self.build() self.do_test() def setUp(self): # Call super's setUp(). TestBase.setUp(self) def do_test(self): """Make sure FindTypes actually finds 'struct typename' not just 'typename'.""" exe = self.getBuildArtifact("a.out") # Create a target by the debugger. target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) # Make sure this works with struct type_list = target.FindTypes("struct mytype") self.assertEqual(type_list.GetSize(), 1, "Found one instance of the type with struct") # Make sure this works without the struct: type_list = target.FindTypes("mytype") self.assertEqual(type_list.GetSize(), 1, "Found one instance of the type without struct") # Make sure it works with union type_list = target.FindTypes("union myunion") self.assertEqual(type_list.GetSize(), 1, "Found one instance of the type with union") # Make sure this works without the union: type_list = target.FindTypes("myunion") self.assertEqual(type_list.GetSize(), 1, "Found one instance of the type without union") # Make sure it works with typedef type_list = target.FindTypes("typedef MyType") self.assertEqual(type_list.GetSize(), 1, "Found one instance of the type with typedef") # Make sure this works without the typedef: type_list = target.FindTypes("MyType") self.assertEqual(type_list.GetSize(), 1, "Found one instance of the type without typedef") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/dynamic-value-same-basename/TestDynamicValueSameBase.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/dynamic-value-same-basename/TestDynamicValueSameBase.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/dynamic-value-same-basename/TestDynamicValueSameBase.py (revision 337147) @@ -1,66 +1,66 @@ """ Make sure if we have two classes with the same base name the dynamic value calculator doesn't confuse them """ from __future__ import print_function import os import time import re import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * class DynamicValueSameBaseTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) - # If your test case doesn't stress debug info, the + # If your test case doesn't stress debug info, the # set this to true. That way it won't be run once for # each debug info format. NO_DEBUG_INFO_TESTCASE = True def test_same_basename_this(self): """Test that the we use the full name to resolve dynamic types.""" self.build() self.main_source_file = lldb.SBFileSpec("main.cpp") self.sample_test() def setUp(self): # Call super's setUp(). TestBase.setUp(self) def sample_test(self): (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, - "Break here to get started", self.main_source_file) + "Break here to get started", self.main_source_file) # Set breakpoints in the two class methods and run to them: namesp_bkpt = target.BreakpointCreateBySourceRegex("namesp function did something.", self.main_source_file) self.assertEqual(namesp_bkpt.GetNumLocations(), 1, "Namespace breakpoint invalid") virtual_bkpt = target.BreakpointCreateBySourceRegex("Virtual function did something.", self.main_source_file) self.assertEqual(virtual_bkpt.GetNumLocations(), 1, "Virtual breakpoint invalid") threads = lldbutil.continue_to_breakpoint(process, namesp_bkpt) self.assertEqual(len(threads), 1, "Didn't stop at namespace breakpoint") frame = threads[0].frame[0] namesp_this = frame.FindVariable("this", lldb.eDynamicCanRunTarget) # Clang specifies the type of this as "T *", gcc as "T * const". This # erases the difference. namesp_type = namesp_this.GetType().GetUnqualifiedType() self.assertEqual(namesp_type.GetName(), "namesp::Virtual *", "Didn't get the right dynamic type") threads = lldbutil.continue_to_breakpoint(process, virtual_bkpt) self.assertEqual(len(threads), 1, "Didn't stop at virtual breakpoint") frame = threads[0].frame[0] virtual_this = frame.FindVariable("this", lldb.eDynamicCanRunTarget) virtual_type = virtual_this.GetType().GetUnqualifiedType() self.assertEqual(virtual_type.GetName(), "Virtual *", "Didn't get the right dynamic type") - + Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/TestCppIncompleteTypes.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/TestCppIncompleteTypes.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/incomplete-types/TestCppIncompleteTypes.py (revision 337147) @@ -1,55 +1,55 @@ import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestCppIncompleteTypes(TestBase): mydir = TestBase.compute_mydir(__file__) @skipIf(compiler="gcc") def test_limit_debug_info(self): self.build() frame = self.get_test_frame('limit') value_f = frame.EvaluateExpression("f") self.assertTrue( value_f.IsValid(), "'expr f' results in a valid SBValue object") self.assertTrue(value_f.GetError().Success(), "'expr f' is successful") value_a = frame.EvaluateExpression("a") self.assertTrue( value_a.IsValid(), "'expr a' results in a valid SBValue object") self.assertTrue(value_a.GetError().Success(), "'expr a' is successful") @skipIf(compiler="gcc") # Clang on Windows asserts in external record layout in this case. @skipIfWindows def test_partial_limit_debug_info(self): self.build() frame = self.get_test_frame('nolimit') value_f = frame.EvaluateExpression("f") self.assertTrue( value_f.IsValid(), "'expr f' results in a valid SBValue object") self.assertTrue(value_f.GetError().Success(), "'expr f' is successful") value_a = frame.EvaluateExpression("a") self.assertTrue( value_a.IsValid(), "'expr a' results in a valid SBValue object") self.assertTrue(value_a.GetError().Success(), "'expr a' is successful") def get_test_frame(self, exe): # Get main source file src_file = "main.cpp" src_file_spec = lldb.SBFileSpec(src_file) - (target, process, thread, main_breakpoint) = lldbutil.run_to_source_breakpoint(self, + (target, process, thread, main_breakpoint) = lldbutil.run_to_source_breakpoint(self, "break here", src_file_spec, exe_name = exe) # Get frame for current thread return thread.GetSelectedFrame() Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/TestLLVMStyle.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/TestLLVMStyle.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/llvm-style/TestLLVMStyle.py (revision 337147) @@ -1,7 +1,7 @@ from lldbsuite.test import lldbinline from lldbsuite.test import decorators lldbinline.MakeInlineTest( __file__, globals(), [ decorators.expectedFailureAll( - oslist=["windows"], bugnumber="llvm.org/pr24764")]) \ No newline at end of file + oslist=["windows"], bugnumber="llvm.org/pr24764")]) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/scope/TestCppScope.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/scope/TestCppScope.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/scope/TestCppScope.py (revision 337147) @@ -1,91 +1,91 @@ """ Test scopes in C++. """ import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestCppScopes(TestBase): mydir = TestBase.compute_mydir(__file__) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24764") def test_all_but_c(self): self.do_test(False) @expectedFailureAll(oslist=["windows"]) def test_c(self): self.do_test(True) - + def do_test(self, test_c): self.build() # Get main source file src_file = os.path.join(self.getSourceDir(), "main.cpp") src_file_spec = lldb.SBFileSpec(src_file) self.assertTrue(src_file_spec.IsValid(), "Main source file") # Get the path of the executable exe_path = self.getBuildArtifact("a.out") # Load the executable target = self.dbg.CreateTarget(exe_path) self.assertTrue(target.IsValid(), VALID_TARGET) # Break on main function main_breakpoint = target.BreakpointCreateBySourceRegex( "// break here", src_file_spec) self.assertTrue( main_breakpoint.IsValid() and main_breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT) # Launch the process args = None env = None process = target.LaunchSimple( args, env, self.get_process_working_directory()) self.assertTrue(process.IsValid(), PROCESS_IS_VALID) # Get the thread of the process self.assertTrue( process.GetState() == lldb.eStateStopped, PROCESS_STOPPED) thread = lldbutil.get_stopped_thread( process, lldb.eStopReasonBreakpoint) # Get current fream of the thread at the breakpoint frame = thread.GetSelectedFrame() # Test result for scopes of variables global_variables = frame.GetVariables(True, True, True, False) global_variables_assert = { 'A::a': 1111, 'B::a': 2222, 'C::a': 3333, '::a': 4444, 'a': 4444 } self.assertTrue( global_variables.GetSize() == 4, "target variable returns all variables") for variable in global_variables: name = variable.GetName() self.assertTrue( name in global_variables_assert, "target variable returns wrong variable " + name) for name in global_variables_assert: if name is "C::a" and not test_c: continue if name is not "C::a" and test_c: continue value = frame.EvaluateExpression(name) assert_value = global_variables_assert[name] self.assertTrue( value.IsValid() and value.GetValueAsSigned() == assert_value, name + " = " + str(assert_value)) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/TestTrivialABI.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/TestTrivialABI.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/cpp/trivial_abi/TestTrivialABI.py (revision 337147) @@ -1,74 +1,74 @@ """ Test that we work properly with classes with the trivial_abi attribute """ from __future__ import print_function import os import time import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestTrivialABI(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True @skipUnlessSupportedTypeAttribute("trivial_abi") @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr37995") def test_call_trivial(self): """Test that we can print a variable & call a function with a trivial ABI class.""" self.build() self.main_source_file = lldb.SBFileSpec("main.cpp") self.expr_test(True) @skipUnlessSupportedTypeAttribute("trivial_abi") @expectedFailureAll(bugnumber="llvm.org/pr36870") def test_call_nontrivial(self): """Test that we can print a variable & call a function on the same class w/o the trivial ABI marker.""" self.build() self.main_source_file = lldb.SBFileSpec("main.cpp") self.expr_test(False) def setUp(self): # Call super's setUp(). TestBase.setUp(self) def check_value(self, test_var, ivar_value): self.assertTrue(test_var.GetError().Success(), "Invalid valobj: %s"%(test_var.GetError().GetCString())) ivar = test_var.GetChildMemberWithName("ivar") self.assertTrue(test_var.GetError().Success(), "Failed to fetch ivar") self.assertEqual(ivar_value, ivar.GetValueAsSigned(), "Got the right value for ivar") def check_frame(self, thread): frame = thread.frames[0] inVal_var = frame.FindVariable("inVal") self.check_value(inVal_var, 10) options = lldb.SBExpressionOptions() inVal_expr = frame.EvaluateExpression("inVal", options) self.check_value(inVal_expr, 10) - + thread.StepOut() outVal_ret = thread.GetStopReturnValue() self.check_value(outVal_ret, 30) def expr_test(self, trivial): (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, - "Set a breakpoint here", self.main_source_file) + "Set a breakpoint here", self.main_source_file) # Stop in a function that takes a trivial value, and try both frame var & expr to get its value: if trivial: self.check_frame(thread) return # Now continue to the same thing without the trivial_abi and see if we get that right: threads = lldbutil.continue_to_breakpoint(process, bkpt) self.assertEqual(len(threads), 1, "Hit my breakpoint the second time.") self.check_frame(threads[0]) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lldbdwarf.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lldbdwarf.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lldbdwarf.py (revision 337147) @@ -1,256 +1,256 @@ """ This module implement Dwarf expression opcode parser. """ import lldb # DWARF Expression operators. DW_OP_addr = 0x03 DW_OP_deref = 0x06 DW_OP_const1u = 0x08 DW_OP_const1s = 0x09 DW_OP_const2u = 0x0A DW_OP_const2s = 0x0B DW_OP_const4u = 0x0C DW_OP_const4s = 0x0D DW_OP_const8u = 0x0E DW_OP_const8s = 0x0F DW_OP_constu = 0x10 DW_OP_consts = 0x11 DW_OP_dup = 0x12 DW_OP_drop = 0x13 DW_OP_over = 0x14 DW_OP_pick = 0x15 DW_OP_swap = 0x16 DW_OP_rot = 0x17 DW_OP_xderef = 0x18 DW_OP_abs = 0x19 DW_OP_and = 0x1A DW_OP_div = 0x1B DW_OP_minus = 0x1C DW_OP_mod = 0x1D DW_OP_mul = 0x1E DW_OP_neg = 0x1F DW_OP_not = 0x20 DW_OP_or = 0x21 DW_OP_plus = 0x22 DW_OP_plus_uconst = 0x23 DW_OP_shl = 0x24 DW_OP_shr = 0x25 DW_OP_shra = 0x26 DW_OP_xor = 0x27 DW_OP_skip = 0x2F DW_OP_bra = 0x28 DW_OP_eq = 0x29 DW_OP_ge = 0x2A DW_OP_gt = 0x2B DW_OP_le = 0x2C DW_OP_lt = 0x2D DW_OP_ne = 0x2E DW_OP_lit0 = 0x30 DW_OP_lit1 = 0x31 DW_OP_lit2 = 0x32 DW_OP_lit3 = 0x33 DW_OP_lit4 = 0x34 DW_OP_lit5 = 0x35 DW_OP_lit6 = 0x36 DW_OP_lit7 = 0x37 DW_OP_lit8 = 0x38 DW_OP_lit9 = 0x39 DW_OP_lit10 = 0x3A DW_OP_lit11 = 0x3B DW_OP_lit12 = 0x3C DW_OP_lit13 = 0x3D DW_OP_lit14 = 0x3E DW_OP_lit15 = 0x3F DW_OP_lit16 = 0x40 DW_OP_lit17 = 0x41 DW_OP_lit18 = 0x42 DW_OP_lit19 = 0x43 DW_OP_lit20 = 0x44 DW_OP_lit21 = 0x45 DW_OP_lit22 = 0x46 DW_OP_lit23 = 0x47 DW_OP_lit24 = 0x48 DW_OP_lit25 = 0x49 DW_OP_lit26 = 0x4A DW_OP_lit27 = 0x4B DW_OP_lit28 = 0x4C DW_OP_lit29 = 0x4D DW_OP_lit30 = 0x4E DW_OP_lit31 = 0x4F DW_OP_reg0 = 0x50 DW_OP_reg1 = 0x51 DW_OP_reg2 = 0x52 DW_OP_reg3 = 0x53 DW_OP_reg4 = 0x54 DW_OP_reg5 = 0x55 DW_OP_reg6 = 0x56 DW_OP_reg7 = 0x57 DW_OP_reg8 = 0x58 DW_OP_reg9 = 0x59 DW_OP_reg10 = 0x5A DW_OP_reg11 = 0x5B DW_OP_reg12 = 0x5C DW_OP_reg13 = 0x5D DW_OP_reg14 = 0x5E DW_OP_reg15 = 0x5F DW_OP_reg16 = 0x60 DW_OP_reg17 = 0x61 DW_OP_reg18 = 0x62 DW_OP_reg19 = 0x63 DW_OP_reg20 = 0x64 DW_OP_reg21 = 0x65 DW_OP_reg22 = 0x66 DW_OP_reg23 = 0x67 DW_OP_reg24 = 0x68 DW_OP_reg25 = 0x69 DW_OP_reg26 = 0x6A DW_OP_reg27 = 0x6B DW_OP_reg28 = 0x6C DW_OP_reg29 = 0x6D DW_OP_reg30 = 0x6E DW_OP_reg31 = 0x6F DW_OP_breg0 = 0x70 DW_OP_breg1 = 0x71 DW_OP_breg2 = 0x72 DW_OP_breg3 = 0x73 DW_OP_breg4 = 0x74 DW_OP_breg5 = 0x75 DW_OP_breg6 = 0x76 DW_OP_breg7 = 0x77 DW_OP_breg8 = 0x78 DW_OP_breg9 = 0x79 DW_OP_breg10 = 0x7A DW_OP_breg11 = 0x7B DW_OP_breg12 = 0x7C DW_OP_breg13 = 0x7D DW_OP_breg14 = 0x7E DW_OP_breg15 = 0x7F DW_OP_breg16 = 0x80 DW_OP_breg17 = 0x81 DW_OP_breg18 = 0x82 DW_OP_breg19 = 0x83 DW_OP_breg20 = 0x84 DW_OP_breg21 = 0x85 DW_OP_breg22 = 0x86 DW_OP_breg23 = 0x87 DW_OP_breg24 = 0x88 DW_OP_breg25 = 0x89 DW_OP_breg26 = 0x8A DW_OP_breg27 = 0x8B DW_OP_breg28 = 0x8C DW_OP_breg29 = 0x8D DW_OP_breg30 = 0x8E DW_OP_breg31 = 0x8F DW_OP_regx = 0x90 DW_OP_fbreg = 0x91 DW_OP_bregx = 0x92 DW_OP_piece = 0x93 DW_OP_deref_size = 0x94 DW_OP_xderef_size = 0x95 DW_OP_nop = 0x96 DW_OP_push_object_address = 0x97 DW_OP_call2 = 0x98 DW_OP_call4 = 0x99 DW_OP_call_ref = 0x9A DW_OP_form_tls_address = 0x9B DW_OP_call_frame_cfa = 0x9C DW_OP_bit_piece = 0x9D DW_OP_implicit_value = 0x9E DW_OP_stack_value = 0x9F DW_OP_lo_user = 0xE0 DW_OP_GNU_push_tls_address = 0xE0 DW_OP_APPLE_uninit = 0xF0 DW_OP_hi_user = 0xFF class DwarfOpcodeParser(object): def updateRegInfoBitsize(self, reg_info, byte_order): """ Update the regInfo bit size. """ # Evaluate Dwarf Expression expr_result = self.evaluateDwarfExpression(reg_info["dynamic_size_dwarf_expr_bytes"], byte_order) if expr_result == 0: reg_info["bitsize"] = 32 elif expr_result == 1: reg_info["bitsize"] = 64 def evaluateDwarfExpression(self, dwarf_opcode, byte_order): """Evaluate Dwarf Expression. """ dwarf_opcode = [dwarf_opcode[i:i+2] for i in range(0,len(dwarf_opcode),2)] dwarf_data = [] for index in range(len(dwarf_opcode)): if index < len(dwarf_opcode): val = int(dwarf_opcode[index], 16) else: break if val == DW_OP_regx: - # Read register number + # Read register number self.assertTrue(len(dwarf_opcode) > (index + 1)) reg_no = int(dwarf_opcode.pop(index + 1), 16) self.reset_test_sequence() # Read register value self.test_sequence.add_log_lines( ["read packet: $p{0:x}#00".format(reg_no), {"direction": "send", "regex": r"^\$([0-9a-fA-F]+)#", "capture": {1: "p_response"}}],True) - + Context = self.expect_gdbremote_sequence() self.assertIsNotNone(Context) p_response = Context.get("p_response") self.assertIsNotNone(p_response) if byte_order == lldb.eByteOrderLittle: # In case of little endian # first decode the HEX ASCII bytes and then reverse it # to get actual value of SR register p_response = "".join(reversed([p_response[i:i+2] for i in range(0, len(p_response),2)])) # Push register value dwarf_data.append(int(p_response,16)) elif val == DW_OP_lit1: # Push literal 1 dwarf_data.append(1) - + elif val == DW_OP_lit26: # Push literal 26 dwarf_data.append(26) elif val == DW_OP_shl: # left shift and push the result back self.assertTrue(len(dwarf_data) > 1) shift_amount = dwarf_data.pop() val_to_shift = dwarf_data.pop() result = val_to_shift << shift_amount dwarf_data.append(result) elif val == DW_OP_shr: # Right shift and push the result back self.assertTrue(len(dwarf_data) > 1) shift_amount = dwarf_data.pop() val_to_shift = dwarf_data.pop() result = val_to_shift >> shift_amount dwarf_data.append(result) elif val == DW_OP_and: # And of topmost 2 elements and push the result back first_ele = dwarf_data.pop() second_ele = dwarf_data.pop() result = first_ele & second_ele dwarf_data.append(result) else: self.assertTrue(False and "Unprocess Dwarf Opcode") self.assertTrue(len(dwarf_data) == 1) expr_result = dwarf_data.pop() return expr_result Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lldbtest.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lldbtest.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lldbtest.py (revision 337147) @@ -1,2365 +1,2366 @@ """ LLDB module which provides the abstract base class of lldb test case. The concrete subclass can override lldbtest.TesBase in order to inherit the common behavior for unitest.TestCase.setUp/tearDown implemented in this file. The subclass should override the attribute mydir in order for the python runtime to locate the individual test cases when running as part of a large test suite or when running each test case as a separate python invocation. ./dotest.py provides a test driver which sets up the environment to run the entire of part of the test suite . Example: # Exercises the test suite in the types directory.... /Volumes/data/lldb/svn/ToT/test $ ./dotest.py -A x86_64 types ... Session logs for test failures/errors/unexpected successes will go into directory '2012-05-16-13_35_42' Command invoked: python ./dotest.py -A x86_64 types compilers=['clang'] Configuration: arch=x86_64 compiler=clang ---------------------------------------------------------------------- Collected 72 tests ........................................................................ ---------------------------------------------------------------------- Ran 72 tests in 135.468s OK $ """ from __future__ import absolute_import from __future__ import print_function # System modules import abc import collections from functools import wraps import gc import glob import inspect import io import os.path import re import shutil import signal from subprocess import * import sys import time import traceback import types import distutils.spawn # Third-party modules import unittest2 from six import add_metaclass from six import StringIO as SixStringIO import six # LLDB modules import use_lldb_suite import lldb from . import configuration from . import decorators from . import lldbplatformutil from . import lldbtest_config from . import lldbutil from . import test_categories from lldbsuite.support import encoded_file from lldbsuite.support import funcutils # See also dotest.parseOptionsAndInitTestdirs(), where the environment variables # LLDB_COMMAND_TRACE and LLDB_DO_CLEANUP are set from '-t' and '-r dir' # options. # By default, traceAlways is False. if "LLDB_COMMAND_TRACE" in os.environ and os.environ[ "LLDB_COMMAND_TRACE"] == "YES": traceAlways = True else: traceAlways = False # By default, doCleanup is True. if "LLDB_DO_CLEANUP" in os.environ and os.environ["LLDB_DO_CLEANUP"] == "NO": doCleanup = False else: doCleanup = True # # Some commonly used assert messages. # COMMAND_FAILED_AS_EXPECTED = "Command has failed as expected" CURRENT_EXECUTABLE_SET = "Current executable set successfully" PROCESS_IS_VALID = "Process is valid" PROCESS_KILLED = "Process is killed successfully" PROCESS_EXITED = "Process exited successfully" PROCESS_STOPPED = "Process status should be stopped" RUN_SUCCEEDED = "Process is launched successfully" RUN_COMPLETED = "Process exited successfully" BACKTRACE_DISPLAYED_CORRECTLY = "Backtrace displayed correctly" BREAKPOINT_CREATED = "Breakpoint created successfully" BREAKPOINT_STATE_CORRECT = "Breakpoint state is correct" BREAKPOINT_PENDING_CREATED = "Pending breakpoint created successfully" BREAKPOINT_HIT_ONCE = "Breakpoint resolved with hit cout = 1" BREAKPOINT_HIT_TWICE = "Breakpoint resolved with hit cout = 2" BREAKPOINT_HIT_THRICE = "Breakpoint resolved with hit cout = 3" MISSING_EXPECTED_REGISTERS = "At least one expected register is unavailable." OBJECT_PRINTED_CORRECTLY = "Object printed correctly" SOURCE_DISPLAYED_CORRECTLY = "Source code displayed correctly" STEP_OUT_SUCCEEDED = "Thread step-out succeeded" STOPPED_DUE_TO_EXC_BAD_ACCESS = "Process should be stopped due to bad access exception" STOPPED_DUE_TO_ASSERT = "Process should be stopped due to an assertion" STOPPED_DUE_TO_BREAKPOINT = "Process should be stopped due to breakpoint" STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS = "%s, %s" % ( STOPPED_DUE_TO_BREAKPOINT, "instead, the actual stop reason is: '%s'") STOPPED_DUE_TO_BREAKPOINT_CONDITION = "Stopped due to breakpoint condition" STOPPED_DUE_TO_BREAKPOINT_IGNORE_COUNT = "Stopped due to breakpoint and ignore count" STOPPED_DUE_TO_SIGNAL = "Process state is stopped due to signal" STOPPED_DUE_TO_STEP_IN = "Process state is stopped due to step in" STOPPED_DUE_TO_WATCHPOINT = "Process should be stopped due to watchpoint" DATA_TYPES_DISPLAYED_CORRECTLY = "Data type(s) displayed correctly" VALID_BREAKPOINT = "Got a valid breakpoint" VALID_BREAKPOINT_LOCATION = "Got a valid breakpoint location" VALID_COMMAND_INTERPRETER = "Got a valid command interpreter" VALID_FILESPEC = "Got a valid filespec" VALID_MODULE = "Got a valid module" VALID_PROCESS = "Got a valid process" VALID_SYMBOL = "Got a valid symbol" VALID_TARGET = "Got a valid target" VALID_PLATFORM = "Got a valid platform" VALID_TYPE = "Got a valid type" VALID_VARIABLE = "Got a valid variable" VARIABLES_DISPLAYED_CORRECTLY = "Variable(s) displayed correctly" WATCHPOINT_CREATED = "Watchpoint created successfully" def CMD_MSG(str): '''A generic "Command '%s' returns successfully" message generator.''' return "Command '%s' returns successfully" % str -def COMPLETION_MSG(str_before, str_after): +def COMPLETION_MSG(str_before, str_after, completions): '''A generic message generator for the completion mechanism.''' - return "'%s' successfully completes to '%s'" % (str_before, str_after) + return ("'%s' successfully completes to '%s', but completions were:\n%s" + % (str_before, str_after, "\n".join(completions))) def EXP_MSG(str, actual, exe): '''A generic "'%s' returns expected result" message generator if exe. Otherwise, it generates "'%s' matches expected result" message.''' return "'%s' %s expected result, got '%s'" % ( str, 'returns' if exe else 'matches', actual.strip()) def SETTING_MSG(setting): '''A generic "Value of setting '%s' is correct" message generator.''' return "Value of setting '%s' is correct" % setting def EnvArray(): """Returns an env variable array from the os.environ map object.""" return list(map(lambda k, v: k + "=" + v, list(os.environ.keys()), list(os.environ.values()))) def line_number(filename, string_to_match): """Helper function to return the line number of the first matched string.""" with io.open(filename, mode='r', encoding="utf-8") as f: for i, line in enumerate(f): if line.find(string_to_match) != -1: # Found our match. return i + 1 raise Exception( "Unable to find '%s' within file %s" % (string_to_match, filename)) def get_line(filename, line_number): """Return the text of the line at the 1-based line number.""" with io.open(filename, mode='r', encoding="utf-8") as f: return f.readlines()[line_number - 1] def pointer_size(): """Return the pointer size of the host system.""" import ctypes a_pointer = ctypes.c_void_p(0xffff) return 8 * ctypes.sizeof(a_pointer) def is_exe(fpath): """Returns true if fpath is an executable.""" return os.path.isfile(fpath) and os.access(fpath, os.X_OK) def which(program): """Returns the full path to a program; None otherwise.""" fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file return None class recording(SixStringIO): """ A nice little context manager for recording the debugger interactions into our session object. If trace flag is ON, it also emits the interactions into the stderr. """ def __init__(self, test, trace): """Create a SixStringIO instance; record the session obj and trace flag.""" SixStringIO.__init__(self) # The test might not have undergone the 'setUp(self)' phase yet, so that # the attribute 'session' might not even exist yet. self.session = getattr(test, "session", None) if test else None self.trace = trace def __enter__(self): """ Context management protocol on entry to the body of the with statement. Just return the SixStringIO object. """ return self def __exit__(self, type, value, tb): """ Context management protocol on exit from the body of the with statement. If trace is ON, it emits the recordings into stderr. Always add the recordings to our session object. And close the SixStringIO object, too. """ if self.trace: print(self.getvalue(), file=sys.stderr) if self.session: print(self.getvalue(), file=self.session) self.close() @add_metaclass(abc.ABCMeta) class _BaseProcess(object): @abc.abstractproperty def pid(self): """Returns process PID if has been launched already.""" @abc.abstractmethod def launch(self, executable, args): """Launches new process with given executable and args.""" @abc.abstractmethod def terminate(self): """Terminates previously launched process..""" class _LocalProcess(_BaseProcess): def __init__(self, trace_on): self._proc = None self._trace_on = trace_on self._delayafterterminate = 0.1 @property def pid(self): return self._proc.pid def launch(self, executable, args): self._proc = Popen( [executable] + args, stdout=open( os.devnull) if not self._trace_on else None, stdin=PIPE) def terminate(self): if self._proc.poll() is None: # Terminate _proc like it does the pexpect signals_to_try = [ sig for sig in [ 'SIGHUP', 'SIGCONT', 'SIGINT'] if sig in dir(signal)] for sig in signals_to_try: try: self._proc.send_signal(getattr(signal, sig)) time.sleep(self._delayafterterminate) if self._proc.poll() is not None: return except ValueError: pass # Windows says SIGINT is not a valid signal to send self._proc.terminate() time.sleep(self._delayafterterminate) if self._proc.poll() is not None: return self._proc.kill() time.sleep(self._delayafterterminate) def poll(self): return self._proc.poll() class _RemoteProcess(_BaseProcess): def __init__(self, install_remote): self._pid = None self._install_remote = install_remote @property def pid(self): return self._pid def launch(self, executable, args): if self._install_remote: src_path = executable dst_path = lldbutil.join_remote_paths( lldb.remote_platform.GetWorkingDirectory(), os.path.basename(executable)) dst_file_spec = lldb.SBFileSpec(dst_path, False) err = lldb.remote_platform.Install( lldb.SBFileSpec(src_path, True), dst_file_spec) if err.Fail(): raise Exception( "remote_platform.Install('%s', '%s') failed: %s" % (src_path, dst_path, err)) else: dst_path = executable dst_file_spec = lldb.SBFileSpec(executable, False) launch_info = lldb.SBLaunchInfo(args) launch_info.SetExecutableFile(dst_file_spec, True) launch_info.SetWorkingDirectory( lldb.remote_platform.GetWorkingDirectory()) # Redirect stdout and stderr to /dev/null launch_info.AddSuppressFileAction(1, False, True) launch_info.AddSuppressFileAction(2, False, True) err = lldb.remote_platform.Launch(launch_info) if err.Fail(): raise Exception( "remote_platform.Launch('%s', '%s') failed: %s" % (dst_path, args, err)) self._pid = launch_info.GetProcessID() def terminate(self): lldb.remote_platform.Kill(self._pid) # From 2.7's subprocess.check_output() convenience function. # Return a tuple (stdoutdata, stderrdata). def system(commands, **kwargs): r"""Run an os command with arguments and return its output as a byte string. If the exit code was non-zero it raises a CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute and output in the output attribute. The arguments are the same as for the Popen constructor. Example: >>> check_output(["ls", "-l", "/dev/null"]) 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' The stdout argument is not allowed as it is used internally. To capture standard error in the result, use stderr=STDOUT. >>> check_output(["/bin/sh", "-c", ... "ls -l non_existent_file ; exit 0"], ... stderr=STDOUT) 'ls: non_existent_file: No such file or directory\n' """ # Assign the sender object to variable 'test' and remove it from kwargs. test = kwargs.pop('sender', None) # [['make', 'clean', 'foo'], ['make', 'foo']] -> ['make clean foo', 'make foo'] commandList = [' '.join(x) for x in commands] output = "" error = "" for shellCommand in commandList: if 'stdout' in kwargs: raise ValueError( 'stdout argument not allowed, it will be overridden.') if 'shell' in kwargs and kwargs['shell'] == False: raise ValueError('shell=False not allowed') process = Popen( shellCommand, stdout=PIPE, stderr=PIPE, shell=True, universal_newlines=True, **kwargs) pid = process.pid this_output, this_error = process.communicate() retcode = process.poll() # Enable trace on failure return while tracking down FreeBSD buildbot # issues trace = traceAlways if not trace and retcode and sys.platform.startswith("freebsd"): trace = True with recording(test, trace) as sbuf: print(file=sbuf) print("os command:", shellCommand, file=sbuf) print("with pid:", pid, file=sbuf) print("stdout:", this_output, file=sbuf) print("stderr:", this_error, file=sbuf) print("retcode:", retcode, file=sbuf) print(file=sbuf) if retcode: cmd = kwargs.get("args") if cmd is None: cmd = shellCommand cpe = CalledProcessError(retcode, cmd) # Ensure caller can access the stdout/stderr. cpe.lldb_extensions = { "stdout_content": this_output, "stderr_content": this_error, "command": shellCommand } raise cpe output = output + this_output error = error + this_error return (output, error) def getsource_if_available(obj): """ Return the text of the source code for an object if available. Otherwise, a print representation is returned. """ import inspect try: return inspect.getsource(obj) except: return repr(obj) def builder_module(): if sys.platform.startswith("freebsd"): return __import__("builder_freebsd") if sys.platform.startswith("openbsd"): return __import__("builder_openbsd") if sys.platform.startswith("netbsd"): return __import__("builder_netbsd") if sys.platform.startswith("linux"): # sys.platform with Python-3.x returns 'linux', but with # Python-2.x it returns 'linux2'. return __import__("builder_linux") return __import__("builder_" + sys.platform) class Base(unittest2.TestCase): """ Abstract base for performing lldb (see TestBase) or other generic tests (see BenchBase for one example). lldbtest.Base works with the test driver to accomplish things. """ # The concrete subclass should override this attribute. mydir = None # Keep track of the old current working directory. oldcwd = None @staticmethod def compute_mydir(test_file): '''Subclasses should call this function to correctly calculate the required "mydir" attribute as follows: mydir = TestBase.compute_mydir(__file__) ''' # /abs/path/to/packages/group/subdir/mytest.py -> group/subdir rel_prefix = test_file[len(os.environ["LLDB_TEST"]) + 1:] return os.path.dirname(rel_prefix) def TraceOn(self): """Returns True if we are in trace mode (tracing detailed test execution).""" return traceAlways @classmethod def setUpClass(cls): """ Python unittest framework class setup fixture. Do current directory manipulation. """ # Fail fast if 'mydir' attribute is not overridden. if not cls.mydir or len(cls.mydir) == 0: raise Exception("Subclasses must override the 'mydir' attribute.") # Save old working directory. cls.oldcwd = os.getcwd() # Change current working directory if ${LLDB_TEST} is defined. # See also dotest.py which sets up ${LLDB_TEST}. if ("LLDB_TEST" in os.environ): full_dir = os.path.join(os.environ["LLDB_TEST"], cls.mydir) if traceAlways: print("Change dir to:", full_dir, file=sys.stderr) os.chdir(full_dir) # Set platform context. cls.platformContext = lldbplatformutil.createPlatformContext() @classmethod def tearDownClass(cls): """ Python unittest framework class teardown fixture. Do class-wide cleanup. """ if doCleanup: # First, let's do the platform-specific cleanup. module = builder_module() module.cleanup() # Subclass might have specific cleanup function defined. if getattr(cls, "classCleanup", None): if traceAlways: print( "Call class-specific cleanup function for class:", cls, file=sys.stderr) try: cls.classCleanup() except: exc_type, exc_value, exc_tb = sys.exc_info() traceback.print_exception(exc_type, exc_value, exc_tb) # Restore old working directory. if traceAlways: print("Restore dir to:", cls.oldcwd, file=sys.stderr) os.chdir(cls.oldcwd) @classmethod def skipLongRunningTest(cls): """ By default, we skip long running test case. This can be overridden by passing '-l' to the test driver (dotest.py). """ if "LLDB_SKIP_LONG_RUNNING_TEST" in os.environ and "NO" == os.environ[ "LLDB_SKIP_LONG_RUNNING_TEST"]: return False else: return True def enableLogChannelsForCurrentTest(self): if len(lldbtest_config.channels) == 0: return # if debug channels are specified in lldbtest_config.channels, # create a new set of log files for every test log_basename = self.getLogBasenameForCurrentTest() # confirm that the file is writeable host_log_path = "{}-host.log".format(log_basename) open(host_log_path, 'w').close() log_enable = "log enable -Tpn -f {} ".format(host_log_path) for channel_with_categories in lldbtest_config.channels: channel_then_categories = channel_with_categories.split(' ', 1) channel = channel_then_categories[0] if len(channel_then_categories) > 1: categories = channel_then_categories[1] else: categories = "default" if channel == "gdb-remote" and lldb.remote_platform is None: # communicate gdb-remote categories to debugserver os.environ["LLDB_DEBUGSERVER_LOG_FLAGS"] = categories self.ci.HandleCommand( log_enable + channel_with_categories, self.res) if not self.res.Succeeded(): raise Exception( 'log enable failed (check LLDB_LOG_OPTION env variable)') # Communicate log path name to debugserver & lldb-server # For remote debugging, these variables need to be set when starting the platform # instance. if lldb.remote_platform is None: server_log_path = "{}-server.log".format(log_basename) open(server_log_path, 'w').close() os.environ["LLDB_DEBUGSERVER_LOG_FILE"] = server_log_path # Communicate channels to lldb-server os.environ["LLDB_SERVER_LOG_CHANNELS"] = ":".join( lldbtest_config.channels) self.addTearDownHook(self.disableLogChannelsForCurrentTest) def disableLogChannelsForCurrentTest(self): # close all log files that we opened for channel_and_categories in lldbtest_config.channels: # channel format - [ [ ...]] channel = channel_and_categories.split(' ', 1)[0] self.ci.HandleCommand("log disable " + channel, self.res) if not self.res.Succeeded(): raise Exception( 'log disable failed (check LLDB_LOG_OPTION env variable)') # Retrieve the server log (if any) from the remote system. It is assumed the server log # is writing to the "server.log" file in the current test directory. This can be # achieved by setting LLDB_DEBUGSERVER_LOG_FILE="server.log" when starting remote # platform. If the remote logging is not enabled, then just let the Get() command silently # fail. if lldb.remote_platform: lldb.remote_platform.Get( lldb.SBFileSpec("server.log"), lldb.SBFileSpec( self.getLogBasenameForCurrentTest() + "-server.log")) def setPlatformWorkingDir(self): if not lldb.remote_platform or not configuration.lldb_platform_working_dir: return components = self.mydir.split(os.path.sep) + [str(self.test_number), self.getBuildDirBasename()] remote_test_dir = configuration.lldb_platform_working_dir for c in components: remote_test_dir = lldbutil.join_remote_paths(remote_test_dir, c) error = lldb.remote_platform.MakeDirectory( remote_test_dir, 448) # 448 = 0o700 if error.Fail(): raise Exception("making remote directory '%s': %s" % ( remote_test_dir, error)) lldb.remote_platform.SetWorkingDirectory(remote_test_dir) # This function removes all files from the current working directory while leaving # the directories in place. The cleaup is required to reduce the disk space required # by the test suite while leaving the directories untouched is neccessary because # sub-directories might belong to an other test def clean_working_directory(): # TODO: Make it working on Windows when we need it for remote debugging support # TODO: Replace the heuristic to remove the files with a logic what collects the # list of files we have to remove during test runs. shell_cmd = lldb.SBPlatformShellCommand( "rm %s/*" % remote_test_dir) lldb.remote_platform.Run(shell_cmd) self.addTearDownHook(clean_working_directory) def getSourceDir(self): """Return the full path to the current test.""" return os.path.join(os.environ["LLDB_TEST"], self.mydir) def getBuildDirBasename(self): return self.__class__.__module__ + "." + self.testMethodName def getBuildDir(self): """Return the full path to the current test.""" return os.path.join(os.environ["LLDB_BUILD"], self.mydir, self.getBuildDirBasename()) - - + + def makeBuildDir(self): """Create the test-specific working directory, deleting any previous contents.""" # See also dotest.py which sets up ${LLDB_BUILD}. bdir = self.getBuildDir() if os.path.isdir(bdir): shutil.rmtree(bdir) lldbutil.mkdir_p(bdir) - + def getBuildArtifact(self, name="a.out"): """Return absolute path to an artifact in the test's build directory.""" return os.path.join(self.getBuildDir(), name) - + def getSourcePath(self, name): """Return absolute path to a file in the test's source directory.""" return os.path.join(self.getSourceDir(), name) def setUp(self): """Fixture for unittest test case setup. It works with the test driver to conditionally skip tests and does other initializations.""" #import traceback # traceback.print_stack() if "LIBCXX_PATH" in os.environ: self.libcxxPath = os.environ["LIBCXX_PATH"] else: self.libcxxPath = None if "LLDBMI_EXEC" in os.environ: self.lldbMiExec = os.environ["LLDBMI_EXEC"] else: self.lldbMiExec = None # If we spawn an lldb process for test (via pexpect), do not load the # init file unless told otherwise. if "NO_LLDBINIT" in os.environ and "NO" == os.environ["NO_LLDBINIT"]: self.lldbOption = "" else: self.lldbOption = "--no-lldbinit" # Assign the test method name to self.testMethodName. # # For an example of the use of this attribute, look at test/types dir. # There are a bunch of test cases under test/types and we don't want the # module cacheing subsystem to be confused with executable name "a.out" # used for all the test cases. self.testMethodName = self._testMethodName # This is for the case of directly spawning 'lldb'/'gdb' and interacting # with it using pexpect. self.child = None self.child_prompt = "(lldb) " # If the child is interacting with the embedded script interpreter, # there are two exits required during tear down, first to quit the # embedded script interpreter and second to quit the lldb command # interpreter. self.child_in_script_interpreter = False # These are for customized teardown cleanup. self.dict = None self.doTearDownCleanup = False # And in rare cases where there are multiple teardown cleanups. self.dicts = [] self.doTearDownCleanups = False # List of spawned subproces.Popen objects self.subprocesses = [] # List of forked process PIDs self.forkedProcessPids = [] # Create a string buffer to record the session info, to be dumped into a # test case specific file if test failure is encountered. self.log_basename = self.getLogBasenameForCurrentTest() session_file = "{}.log".format(self.log_basename) # Python 3 doesn't support unbuffered I/O in text mode. Open buffered. self.session = encoded_file.open(session_file, "utf-8", mode="w") # Optimistically set __errored__, __failed__, __expected__ to False # initially. If the test errored/failed, the session info # (self.session) is then dumped into a session specific file for # diagnosis. self.__cleanup_errored__ = False self.__errored__ = False self.__failed__ = False self.__expected__ = False # We are also interested in unexpected success. self.__unexpected__ = False # And skipped tests. self.__skipped__ = False # See addTearDownHook(self, hook) which allows the client to add a hook # function to be run during tearDown() time. self.hooks = [] # See HideStdout(self). self.sys_stdout_hidden = False if self.platformContext: # set environment variable names for finding shared libraries self.dylibPath = self.platformContext.shlib_environment_var # Create the debugger instance if necessary. try: self.dbg = lldb.DBG except AttributeError: self.dbg = lldb.SBDebugger.Create() if not self.dbg: raise Exception('Invalid debugger instance') # Retrieve the associated command interpreter instance. self.ci = self.dbg.GetCommandInterpreter() if not self.ci: raise Exception('Could not get the command interpreter') # And the result object. self.res = lldb.SBCommandReturnObject() self.setPlatformWorkingDir() self.enableLogChannelsForCurrentTest() lib_dir = os.environ["LLDB_LIB_DIR"] self.dsym = None self.framework_dir = None self.darwinWithFramework = self.platformIsDarwin() if sys.platform.startswith("darwin"): # Handle the framework environment variable if it is set if hasattr(lldbtest_config, 'lldbFrameworkPath'): framework_path = lldbtest_config.lldbFrameworkPath # Framework dir should be the directory containing the framework self.framework_dir = framework_path[:framework_path.rfind('LLDB.framework')] # If a framework dir was not specified assume the Xcode build # directory layout where the framework is in LLDB_LIB_DIR. else: self.framework_dir = lib_dir self.dsym = os.path.join(self.framework_dir, 'LLDB.framework', 'LLDB') # If the framework binary doesn't exist, assume we didn't actually # build a framework, and fallback to standard *nix behavior by # setting framework_dir and dsym to None. if not os.path.exists(self.dsym): self.framework_dir = None self.dsym = None self.darwinWithFramework = False self.makeBuildDir() def setAsync(self, value): """ Sets async mode to True/False and ensures it is reset after the testcase completes.""" old_async = self.dbg.GetAsync() self.dbg.SetAsync(value) self.addTearDownHook(lambda: self.dbg.SetAsync(old_async)) def cleanupSubprocesses(self): # Ensure any subprocesses are cleaned up for p in self.subprocesses: p.terminate() del p del self.subprocesses[:] # Ensure any forked processes are cleaned up for pid in self.forkedProcessPids: if os.path.exists("/proc/" + str(pid)): os.kill(pid, signal.SIGTERM) def spawnSubprocess(self, executable, args=[], install_remote=True): """ Creates a subprocess.Popen object with the specified executable and arguments, saves it in self.subprocesses, and returns the object. NOTE: if using this function, ensure you also call: self.addTearDownHook(self.cleanupSubprocesses) otherwise the test suite will leak processes. """ proc = _RemoteProcess( install_remote) if lldb.remote_platform else _LocalProcess(self.TraceOn()) proc.launch(executable, args) self.subprocesses.append(proc) return proc def forkSubprocess(self, executable, args=[]): """ Fork a subprocess with its own group ID. NOTE: if using this function, ensure you also call: self.addTearDownHook(self.cleanupSubprocesses) otherwise the test suite will leak processes. """ child_pid = os.fork() if child_pid == 0: # If more I/O support is required, this can be beefed up. fd = os.open(os.devnull, os.O_RDWR) os.dup2(fd, 1) os.dup2(fd, 2) # This call causes the child to have its of group ID os.setpgid(0, 0) os.execvp(executable, [executable] + args) # Give the child time to get through the execvp() call time.sleep(0.1) self.forkedProcessPids.append(child_pid) return child_pid def HideStdout(self): """Hide output to stdout from the user. During test execution, there might be cases where we don't want to show the standard output to the user. For example, self.runCmd(r'''sc print("\n\n\tHello!\n")''') tests whether command abbreviation for 'script' works or not. There is no need to show the 'Hello' output to the user as long as the 'script' command succeeds and we are not in TraceOn() mode (see the '-t' option). In this case, the test method calls self.HideStdout(self) to redirect the sys.stdout to a null device, and restores the sys.stdout upon teardown. Note that you should only call this method at most once during a test case execution. Any subsequent call has no effect at all.""" if self.sys_stdout_hidden: return self.sys_stdout_hidden = True old_stdout = sys.stdout sys.stdout = open(os.devnull, 'w') def restore_stdout(): sys.stdout = old_stdout self.addTearDownHook(restore_stdout) # ======================================================================= # Methods for customized teardown cleanups as well as execution of hooks. # ======================================================================= def setTearDownCleanup(self, dictionary=None): """Register a cleanup action at tearDown() time with a dictinary""" self.dict = dictionary self.doTearDownCleanup = True def addTearDownCleanup(self, dictionary): """Add a cleanup action at tearDown() time with a dictinary""" self.dicts.append(dictionary) self.doTearDownCleanups = True def addTearDownHook(self, hook): """ Add a function to be run during tearDown() time. Hooks are executed in a first come first serve manner. """ if six.callable(hook): with recording(self, traceAlways) as sbuf: print( "Adding tearDown hook:", getsource_if_available(hook), file=sbuf) self.hooks.append(hook) return self def deletePexpectChild(self): # This is for the case of directly spawning 'lldb' and interacting with it # using pexpect. if self.child and self.child.isalive(): import pexpect with recording(self, traceAlways) as sbuf: print("tearing down the child process....", file=sbuf) try: if self.child_in_script_interpreter: self.child.sendline('quit()') self.child.expect_exact(self.child_prompt) self.child.sendline( 'settings set interpreter.prompt-on-quit false') self.child.sendline('quit') self.child.expect(pexpect.EOF) except (ValueError, pexpect.ExceptionPexpect): # child is already terminated pass except OSError as exception: import errno if exception.errno != errno.EIO: # unexpected error raise # child is already terminated pass finally: # Give it one final blow to make sure the child is terminated. self.child.close() def tearDown(self): """Fixture for unittest test case teardown.""" #import traceback # traceback.print_stack() self.deletePexpectChild() # Check and run any hook functions. for hook in reversed(self.hooks): with recording(self, traceAlways) as sbuf: print( "Executing tearDown hook:", getsource_if_available(hook), file=sbuf) if funcutils.requires_self(hook): hook(self) else: hook() # try the plain call and hope it works del self.hooks # Perform registered teardown cleanup. if doCleanup and self.doTearDownCleanup: self.cleanup(dictionary=self.dict) # In rare cases where there are multiple teardown cleanups added. if doCleanup and self.doTearDownCleanups: if self.dicts: for dict in reversed(self.dicts): self.cleanup(dictionary=dict) # ========================================================= # Various callbacks to allow introspection of test progress # ========================================================= def markError(self): """Callback invoked when an error (unexpected exception) errored.""" self.__errored__ = True with recording(self, False) as sbuf: # False because there's no need to write "ERROR" to the stderr twice. # Once by the Python unittest framework, and a second time by us. print("ERROR", file=sbuf) def markCleanupError(self): """Callback invoked when an error occurs while a test is cleaning up.""" self.__cleanup_errored__ = True with recording(self, False) as sbuf: # False because there's no need to write "CLEANUP_ERROR" to the stderr twice. # Once by the Python unittest framework, and a second time by us. print("CLEANUP_ERROR", file=sbuf) def markFailure(self): """Callback invoked when a failure (test assertion failure) occurred.""" self.__failed__ = True with recording(self, False) as sbuf: # False because there's no need to write "FAIL" to the stderr twice. # Once by the Python unittest framework, and a second time by us. print("FAIL", file=sbuf) def markExpectedFailure(self, err, bugnumber): """Callback invoked when an expected failure/error occurred.""" self.__expected__ = True with recording(self, False) as sbuf: # False because there's no need to write "expected failure" to the # stderr twice. # Once by the Python unittest framework, and a second time by us. if bugnumber is None: print("expected failure", file=sbuf) else: print( "expected failure (problem id:" + str(bugnumber) + ")", file=sbuf) def markSkippedTest(self): """Callback invoked when a test is skipped.""" self.__skipped__ = True with recording(self, False) as sbuf: # False because there's no need to write "skipped test" to the # stderr twice. # Once by the Python unittest framework, and a second time by us. print("skipped test", file=sbuf) def markUnexpectedSuccess(self, bugnumber): """Callback invoked when an unexpected success occurred.""" self.__unexpected__ = True with recording(self, False) as sbuf: # False because there's no need to write "unexpected success" to the # stderr twice. # Once by the Python unittest framework, and a second time by us. if bugnumber is None: print("unexpected success", file=sbuf) else: print( "unexpected success (problem id:" + str(bugnumber) + ")", file=sbuf) def getRerunArgs(self): return " -f %s.%s" % (self.__class__.__name__, self._testMethodName) def getLogBasenameForCurrentTest(self, prefix=None): """ returns a partial path that can be used as the beginning of the name of multiple log files pertaining to this test /--.. """ dname = os.path.join(os.environ["LLDB_TEST"], os.environ["LLDB_SESSION_DIRNAME"]) if not os.path.isdir(dname): os.mkdir(dname) components = [] if prefix is not None: components.append(prefix) for c in configuration.session_file_format: if c == 'f': components.append(self.__class__.__module__) elif c == 'n': components.append(self.__class__.__name__) elif c == 'c': compiler = self.getCompiler() if compiler[1] == ':': compiler = compiler[2:] if os.path.altsep is not None: compiler = compiler.replace(os.path.altsep, os.path.sep) path_components = [x for x in compiler.split(os.path.sep) if x != ""] # Add at most 4 path components to avoid generating very long # filenames components.extend(path_components[-4:]) elif c == 'a': components.append(self.getArchitecture()) elif c == 'm': components.append(self.testMethodName) fname = "-".join(components) return os.path.join(dname, fname) def dumpSessionInfo(self): """ Dump the debugger interactions leading to a test error/failure. This allows for more convenient postmortem analysis. See also LLDBTestResult (dotest.py) which is a singlton class derived from TextTestResult and overwrites addError, addFailure, and addExpectedFailure methods to allow us to to mark the test instance as such. """ # We are here because self.tearDown() detected that this test instance # either errored or failed. The lldb.test_result singleton contains # two lists (erros and failures) which get populated by the unittest # framework. Look over there for stack trace information. # # The lists contain 2-tuples of TestCase instances and strings holding # formatted tracebacks. # # See http://docs.python.org/library/unittest.html#unittest.TestResult. # output tracebacks into session pairs = [] if self.__errored__: pairs = configuration.test_result.errors prefix = 'Error' elif self.__cleanup_errored__: pairs = configuration.test_result.cleanup_errors prefix = 'CleanupError' elif self.__failed__: pairs = configuration.test_result.failures prefix = 'Failure' elif self.__expected__: pairs = configuration.test_result.expectedFailures prefix = 'ExpectedFailure' elif self.__skipped__: prefix = 'SkippedTest' elif self.__unexpected__: prefix = 'UnexpectedSuccess' else: prefix = 'Success' if not self.__unexpected__ and not self.__skipped__: for test, traceback in pairs: if test is self: print(traceback, file=self.session) # put footer (timestamp/rerun instructions) into session testMethod = getattr(self, self._testMethodName) if getattr(testMethod, "__benchmarks_test__", False): benchmarks = True else: benchmarks = False import datetime print( "Session info generated @", datetime.datetime.now().ctime(), file=self.session) print( "To rerun this test, issue the following command from the 'test' directory:\n", file=self.session) print( "./dotest.py %s -v %s %s" % (self.getRunOptions(), ('+b' if benchmarks else '-t'), self.getRerunArgs()), file=self.session) self.session.close() del self.session # process the log files log_files_for_this_test = glob.glob(self.log_basename + "*") if prefix != 'Success' or lldbtest_config.log_success: # keep all log files, rename them to include prefix dst_log_basename = self.getLogBasenameForCurrentTest(prefix) for src in log_files_for_this_test: if os.path.isfile(src): dst = src.replace(self.log_basename, dst_log_basename) if os.name == "nt" and os.path.isfile(dst): # On Windows, renaming a -> b will throw an exception if b exists. On non-Windows platforms # it silently replaces the destination. Ultimately this means that atomic renames are not # guaranteed to be possible on Windows, but we need this to work anyway, so just remove the # destination first if it already exists. remove_file(dst) os.rename(src, dst) else: # success! (and we don't want log files) delete log files for log_file in log_files_for_this_test: remove_file(log_file) # ==================================================== # Config. methods supported through a plugin interface # (enables reading of the current test configuration) # ==================================================== def isMIPS(self): """Returns true if the architecture is MIPS.""" arch = self.getArchitecture() if re.match("mips", arch): return True return False def isPPC64le(self): """Returns true if the architecture is PPC64LE.""" arch = self.getArchitecture() if re.match("powerpc64le", arch): return True return False def getArchitecture(self): """Returns the architecture in effect the test suite is running with.""" module = builder_module() arch = module.getArchitecture() if arch == 'amd64': arch = 'x86_64' return arch def getLldbArchitecture(self): """Returns the architecture of the lldb binary.""" if not hasattr(self, 'lldbArchitecture'): # spawn local process command = [ lldbtest_config.lldbExec, "-o", "file " + lldbtest_config.lldbExec, "-o", "quit" ] output = check_output(command) str = output.decode("utf-8") for line in str.splitlines(): m = re.search( "Current executable set to '.*' \\((.*)\\)\\.", line) if m: self.lldbArchitecture = m.group(1) break return self.lldbArchitecture def getCompiler(self): """Returns the compiler in effect the test suite is running with.""" module = builder_module() return module.getCompiler() def getCompilerBinary(self): """Returns the compiler binary the test suite is running with.""" return self.getCompiler().split()[0] def getCompilerVersion(self): """ Returns a string that represents the compiler version. Supports: llvm, clang. """ version = 'unknown' compiler = self.getCompilerBinary() version_output = system([[compiler, "-v"]])[1] for line in version_output.split(os.linesep): m = re.search('version ([0-9\.]+)', line) if m: version = m.group(1) return version def getGoCompilerVersion(self): """ Returns a string that represents the go compiler version, or None if go is not found. """ compiler = which("go") if compiler: version_output = system([[compiler, "version"]])[0] for line in version_output.split(os.linesep): m = re.search('go version (devel|go\\S+)', line) if m: return m.group(1) return None def platformIsDarwin(self): """Returns true if the OS triple for the selected platform is any valid apple OS""" return lldbplatformutil.platformIsDarwin() def hasDarwinFramework(self): return self.darwinWithFramework def getPlatform(self): """Returns the target platform the test suite is running on.""" return lldbplatformutil.getPlatform() def isIntelCompiler(self): """ Returns true if using an Intel (ICC) compiler, false otherwise. """ return any([x in self.getCompiler() for x in ["icc", "icpc", "icl"]]) def expectedCompilerVersion(self, compiler_version): """Returns True iff compiler_version[1] matches the current compiler version. Use compiler_version[0] to specify the operator used to determine if a match has occurred. Any operator other than the following defaults to an equality test: '>', '>=', "=>", '<', '<=', '=<', '!=', "!" or 'not' """ if (compiler_version is None): return True operator = str(compiler_version[0]) version = compiler_version[1] if (version is None): return True if (operator == '>'): return self.getCompilerVersion() > version if (operator == '>=' or operator == '=>'): return self.getCompilerVersion() >= version if (operator == '<'): return self.getCompilerVersion() < version if (operator == '<=' or operator == '=<'): return self.getCompilerVersion() <= version if (operator == '!=' or operator == '!' or operator == 'not'): return str(version) not in str(self.getCompilerVersion()) return str(version) in str(self.getCompilerVersion()) def expectedCompiler(self, compilers): """Returns True iff any element of compilers is a sub-string of the current compiler.""" if (compilers is None): return True for compiler in compilers: if compiler in self.getCompiler(): return True return False def expectedArch(self, archs): """Returns True iff any element of archs is a sub-string of the current architecture.""" if (archs is None): return True for arch in archs: if arch in self.getArchitecture(): return True return False def getRunOptions(self): """Command line option for -A and -C to run this test again, called from self.dumpSessionInfo().""" arch = self.getArchitecture() comp = self.getCompiler() option_str = "" if arch: option_str = "-A " + arch if comp: option_str += " -C " + comp return option_str def getDebugInfo(self): method = getattr(self, self.testMethodName) return getattr(method, "debug_info", None) # ================================================== # Build methods supported through a plugin interface # ================================================== def getstdlibFlag(self): """ Returns the proper -stdlib flag, or empty if not required.""" if self.platformIsDarwin() or self.getPlatform() == "freebsd" or self.getPlatform() == "openbsd": stdlibflag = "-stdlib=libc++" else: # this includes NetBSD stdlibflag = "" return stdlibflag def getstdFlag(self): """ Returns the proper stdflag. """ if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion(): stdflag = "-std=c++0x" else: stdflag = "-std=c++11" return stdflag def buildDriver(self, sources, exe_name): """ Platform-specific way to build a program that links with LLDB (via the liblldb.so or LLDB.framework). """ stdflag = self.getstdFlag() stdlibflag = self.getstdlibFlag() lib_dir = os.environ["LLDB_LIB_DIR"] if self.hasDarwinFramework(): d = {'CXX_SOURCES': sources, 'EXE': exe_name, 'CFLAGS_EXTRAS': "%s %s" % (stdflag, stdlibflag), 'FRAMEWORK_INCLUDES': "-F%s" % self.framework_dir, 'LD_EXTRAS': "%s -Wl,-rpath,%s" % (self.dsym, self.framework_dir), } elif sys.platform.startswith('win'): d = { 'CXX_SOURCES': sources, 'EXE': exe_name, 'CFLAGS_EXTRAS': "%s %s -I%s" % (stdflag, stdlibflag, os.path.join( os.environ["LLDB_SRC"], "include")), 'LD_EXTRAS': "-L%s -lliblldb" % os.environ["LLDB_IMPLIB_DIR"]} else: d = { 'CXX_SOURCES': sources, 'EXE': exe_name, 'CFLAGS_EXTRAS': "%s %s -I%s" % (stdflag, stdlibflag, os.path.join( os.environ["LLDB_SRC"], "include")), 'LD_EXTRAS': "-L%s/../lib -llldb -Wl,-rpath,%s/../lib" % (lib_dir, lib_dir)} if self.TraceOn(): print( "Building LLDB Driver (%s) from sources %s" % (exe_name, sources)) self.buildDefault(dictionary=d) def buildLibrary(self, sources, lib_name): """Platform specific way to build a default library. """ stdflag = self.getstdFlag() lib_dir = os.environ["LLDB_LIB_DIR"] if self.hasDarwinFramework(): d = {'DYLIB_CXX_SOURCES': sources, 'DYLIB_NAME': lib_name, 'CFLAGS_EXTRAS': "%s -stdlib=libc++" % stdflag, 'FRAMEWORK_INCLUDES': "-F%s" % self.framework_dir, 'LD_EXTRAS': "%s -Wl,-rpath,%s -dynamiclib" % (self.dsym, self.framework_dir), } elif self.getPlatform() == 'windows': d = { 'DYLIB_CXX_SOURCES': sources, 'DYLIB_NAME': lib_name, 'CFLAGS_EXTRAS': "%s -I%s " % (stdflag, os.path.join( os.environ["LLDB_SRC"], "include")), 'LD_EXTRAS': "-shared -l%s\liblldb.lib" % self.os.environ["LLDB_IMPLIB_DIR"]} else: d = { 'DYLIB_CXX_SOURCES': sources, 'DYLIB_NAME': lib_name, 'CFLAGS_EXTRAS': "%s -I%s -fPIC" % (stdflag, os.path.join( os.environ["LLDB_SRC"], "include")), 'LD_EXTRAS': "-shared -L%s/../lib -llldb -Wl,-rpath,%s/../lib" % (lib_dir, lib_dir)} if self.TraceOn(): print( "Building LLDB Library (%s) from sources %s" % (lib_name, sources)) self.buildDefault(dictionary=d) def buildProgram(self, sources, exe_name): """ Platform specific way to build an executable from C/C++ sources. """ d = {'CXX_SOURCES': sources, 'EXE': exe_name} self.buildDefault(dictionary=d) def buildDefault( self, architecture=None, compiler=None, dictionary=None): """Platform specific way to build the default binaries.""" testdir = self.mydir testname = self.getBuildDirBasename() if self.getDebugInfo(): raise Exception("buildDefault tests must set NO_DEBUG_INFO_TESTCASE") module = builder_module() dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) if not module.buildDefault(self, architecture, compiler, dictionary, testdir, testname): raise Exception("Don't know how to build default binary") def buildDsym( self, architecture=None, compiler=None, dictionary=None): """Platform specific way to build binaries with dsym info.""" testdir = self.mydir testname = self.getBuildDirBasename() if self.getDebugInfo() != "dsym": raise Exception("NO_DEBUG_INFO_TESTCASE must build with buildDefault") module = builder_module() dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) if not module.buildDsym(self, architecture, compiler, dictionary, testdir, testname): raise Exception("Don't know how to build binary with dsym") def buildDwarf( self, architecture=None, compiler=None, dictionary=None): """Platform specific way to build binaries with dwarf maps.""" testdir = self.mydir testname = self.getBuildDirBasename() if self.getDebugInfo() != "dwarf": raise Exception("NO_DEBUG_INFO_TESTCASE must build with buildDefault") module = builder_module() dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) if not module.buildDwarf(self, architecture, compiler, dictionary, testdir, testname): raise Exception("Don't know how to build binary with dwarf") def buildDwo( self, architecture=None, compiler=None, dictionary=None): """Platform specific way to build binaries with dwarf maps.""" testdir = self.mydir testname = self.getBuildDirBasename() if self.getDebugInfo() != "dwo": raise Exception("NO_DEBUG_INFO_TESTCASE must build with buildDefault") module = builder_module() dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) if not module.buildDwo(self, architecture, compiler, dictionary, testdir, testname): raise Exception("Don't know how to build binary with dwo") def buildGModules( self, architecture=None, compiler=None, dictionary=None): """Platform specific way to build binaries with gmodules info.""" testdir = self.mydir testname = self.getBuildDirBasename() if self.getDebugInfo() != "gmodules": raise Exception("NO_DEBUG_INFO_TESTCASE must build with buildDefault") module = builder_module() dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) if not module.buildGModules(self, architecture, compiler, dictionary, testdir, testname): raise Exception("Don't know how to build binary with gmodules") def buildGo(self): """Build the default go binary. """ exe = self.getBuildArtifact("a.out") system([[which('go'), 'build -gcflags "-N -l" -o %s main.go' % exe]]) def signBinary(self, binary_path): if sys.platform.startswith("darwin"): codesign_cmd = "codesign --force --sign \"%s\" %s" % ( lldbtest_config.codesign_identity, binary_path) call(codesign_cmd, shell=True) def findBuiltClang(self): """Tries to find and use Clang from the build directory as the compiler (instead of the system compiler).""" paths_to_try = [ "llvm-build/Release+Asserts/x86_64/bin/clang", "llvm-build/Debug+Asserts/x86_64/bin/clang", "llvm-build/Release/x86_64/bin/clang", "llvm-build/Debug/x86_64/bin/clang", ] lldb_root_path = os.path.join( os.path.dirname(__file__), "..", "..", "..", "..") for p in paths_to_try: path = os.path.join(lldb_root_path, p) if os.path.exists(path): return path # Tries to find clang at the same folder as the lldb lldb_dir = os.path.dirname(lldbtest_config.lldbExec) path = distutils.spawn.find_executable("clang", lldb_dir) if path is not None: return path return os.environ["CC"] def findYaml2obj(self): """ Get the path to the yaml2obj executable, which can be used to create test object files from easy to write yaml instructions. Throws an Exception if the executable cannot be found. """ # Tries to find yaml2obj at the same folder as clang clang_dir = os.path.dirname(self.findBuiltClang()) path = distutils.spawn.find_executable("yaml2obj", clang_dir) if path is not None: return path raise Exception("yaml2obj executable not found") def yaml2obj(self, yaml_path, obj_path): """ Create an object file at the given path from a yaml file. Throws subprocess.CalledProcessError if the object could not be created. """ yaml2obj = self.findYaml2obj() command = [yaml2obj, "-o=%s" % obj_path, yaml_path] system([command]) def getBuildFlags( self, use_cpp11=True, use_libcxx=False, use_libstdcxx=False): """ Returns a dictionary (which can be provided to build* functions above) which contains OS-specific build flags. """ cflags = "" ldflags = "" # On Mac OS X, unless specifically requested to use libstdc++, use # libc++ if not use_libstdcxx and self.platformIsDarwin(): use_libcxx = True if use_libcxx and self.libcxxPath: cflags += "-stdlib=libc++ " if self.libcxxPath: libcxxInclude = os.path.join(self.libcxxPath, "include") libcxxLib = os.path.join(self.libcxxPath, "lib") if os.path.isdir(libcxxInclude) and os.path.isdir(libcxxLib): cflags += "-nostdinc++ -I%s -L%s -Wl,-rpath,%s " % ( libcxxInclude, libcxxLib, libcxxLib) if use_cpp11: cflags += "-std=" if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion(): cflags += "c++0x" else: cflags += "c++11" if self.platformIsDarwin() or self.getPlatform() == "freebsd": cflags += " -stdlib=libc++" elif self.getPlatform() == "openbsd": cflags += " -stdlib=libc++" elif self.getPlatform() == "netbsd": cflags += " -stdlib=libstdc++" elif "clang" in self.getCompiler(): cflags += " -stdlib=libstdc++" return {'CFLAGS_EXTRAS': cflags, 'LD_EXTRAS': ldflags, } def cleanup(self, dictionary=None): """Platform specific way to do cleanup after build.""" module = builder_module() if not module.cleanup(self, dictionary): raise Exception( "Don't know how to do cleanup with dictionary: " + dictionary) def getLLDBLibraryEnvVal(self): """ Returns the path that the OS-specific library search environment variable (self.dylibPath) should be set to in order for a program to find the LLDB library. If an environment variable named self.dylibPath is already set, the new path is appended to it and returned. """ existing_library_path = os.environ[ self.dylibPath] if self.dylibPath in os.environ else None lib_dir = os.environ["LLDB_LIB_DIR"] if existing_library_path: return "%s:%s" % (existing_library_path, lib_dir) elif sys.platform.startswith("darwin"): return os.path.join(lib_dir, 'LLDB.framework') else: return lib_dir def getLibcPlusPlusLibs(self): if self.getPlatform() in ('freebsd', 'linux', 'netbsd', 'openbsd'): return ['libc++.so.1'] else: return ['libc++.1.dylib', 'libc++abi.dylib'] # Metaclass for TestBase to change the list of test metods when a new TestCase is loaded. # We change the test methods to create a new test method for each test for each debug info we are # testing. The name of the new test method will be '_' and with adding # the new test method we remove the old method at the same time. This functionality can be # supressed by at test case level setting the class attribute NO_DEBUG_INFO_TESTCASE or at test # level by using the decorator @no_debug_info_test. class LLDBTestCaseFactory(type): def __new__(cls, name, bases, attrs): original_testcase = super( LLDBTestCaseFactory, cls).__new__( cls, name, bases, attrs) if original_testcase.NO_DEBUG_INFO_TESTCASE: return original_testcase newattrs = {} for attrname, attrvalue in attrs.items(): if attrname.startswith("test") and not getattr( attrvalue, "__no_debug_info_test__", False): # If any debug info categories were explicitly tagged, assume that list to be # authoritative. If none were specified, try with all debug # info formats. all_dbginfo_categories = set(test_categories.debug_info_categories) categories = set( getattr( attrvalue, "categories", [])) & all_dbginfo_categories if not categories: categories = all_dbginfo_categories for cat in categories: @decorators.add_test_categories([cat]) @wraps(attrvalue) def test_method(self, attrvalue=attrvalue): return attrvalue(self) method_name = attrname + "_" + cat test_method.__name__ = method_name test_method.debug_info = cat newattrs[method_name] = test_method else: newattrs[attrname] = attrvalue return super( LLDBTestCaseFactory, cls).__new__( cls, name, bases, newattrs) # Setup the metaclass for this class to change the list of the test # methods when a new class is loaded @add_metaclass(LLDBTestCaseFactory) class TestBase(Base): """ This abstract base class is meant to be subclassed. It provides default implementations for setUpClass(), tearDownClass(), setUp(), and tearDown(), among other things. Important things for test class writers: - Overwrite the mydir class attribute, otherwise your test class won't run. It specifies the relative directory to the top level 'test' so the test harness can change to the correct working directory before running your test. - The setUp method sets up things to facilitate subsequent interactions with the debugger as part of the test. These include: - populate the test method name - create/get a debugger set with synchronous mode (self.dbg) - get the command interpreter from with the debugger (self.ci) - create a result object for use with the command interpreter (self.res) - plus other stuffs - The tearDown method tries to perform some necessary cleanup on behalf of the test to return the debugger to a good state for the next test. These include: - execute any tearDown hooks registered by the test method with TestBase.addTearDownHook(); examples can be found in settings/TestSettings.py - kill the inferior process associated with each target, if any, and, then delete the target from the debugger's target list - perform build cleanup before running the next test method in the same test class; examples of registering for this service can be found in types/TestIntegerTypes.py with the call: - self.setTearDownCleanup(dictionary=d) - Similarly setUpClass and tearDownClass perform classwise setup and teardown fixtures. The tearDownClass method invokes a default build cleanup for the entire test class; also, subclasses can implement the classmethod classCleanup(cls) to perform special class cleanup action. - The instance methods runCmd and expect are used heavily by existing test cases to send a command to the command interpreter and to perform string/pattern matching on the output of such command execution. The expect method also provides a mode to peform string/pattern matching without running a command. - The build methods buildDefault, buildDsym, and buildDwarf are used to build the binaries used during a particular test scenario. A plugin should be provided for the sys.platform running the test suite. The Mac OS X implementation is located in plugins/darwin.py. """ # Subclasses can set this to true (if they don't depend on debug info) to avoid running the # test multiple times with various debug info types. NO_DEBUG_INFO_TESTCASE = False # Maximum allowed attempts when launching the inferior process. # Can be overridden by the LLDB_MAX_LAUNCH_COUNT environment variable. maxLaunchCount = 1 # Time to wait before the next launching attempt in second(s). # Can be overridden by the LLDB_TIME_WAIT_NEXT_LAUNCH environment variable. timeWaitNextLaunch = 1.0 def generateSource(self, source): template = source + '.template' temp = os.path.join(self.getSourceDir(), template) with open(temp, 'r') as f: content = f.read() - + public_api_dir = os.path.join( os.environ["LLDB_SRC"], "include", "lldb", "API") # Look under the include/lldb/API directory and add #include statements # for all the SB API headers. public_headers = os.listdir(public_api_dir) # For different platforms, the include statement can vary. if self.hasDarwinFramework(): include_stmt = "'#include <%s>' % os.path.join('LLDB', header)" else: include_stmt = "'#include <%s>' % os.path.join('" + public_api_dir + "', header)" list = [eval(include_stmt) for header in public_headers if ( header.startswith("SB") and header.endswith(".h"))] includes = '\n'.join(list) new_content = content.replace('%include_SB_APIs%', includes) src = os.path.join(self.getBuildDir(), source) with open(src, 'w') as f: f.write(new_content) self.addTearDownHook(lambda: os.remove(src)) def setUp(self): #import traceback # traceback.print_stack() # Works with the test driver to conditionally skip tests via # decorators. Base.setUp(self) if self.child: # Set the clang modules cache path. assert(self.getDebugInfo() == 'default') mod_cache = os.path.join(self.getBuildDir(), "module-cache") self.runCmd('settings set symbols.clang-modules-cache-path "%s"' % mod_cache) # Disable Spotlight lookup. The testsuite creates # different binaries with the same UUID, because they only # differ in the debug info, which is not being hashed. self.runCmd('settings set symbols.enable-external-lookup false') if "LLDB_MAX_LAUNCH_COUNT" in os.environ: self.maxLaunchCount = int(os.environ["LLDB_MAX_LAUNCH_COUNT"]) if "LLDB_TIME_WAIT_NEXT_LAUNCH" in os.environ: self.timeWaitNextLaunch = float( os.environ["LLDB_TIME_WAIT_NEXT_LAUNCH"]) # We want our debugger to be synchronous. self.dbg.SetAsync(False) # Retrieve the associated command interpreter instance. self.ci = self.dbg.GetCommandInterpreter() if not self.ci: raise Exception('Could not get the command interpreter') # And the result object. self.res = lldb.SBCommandReturnObject() def registerSharedLibrariesWithTarget(self, target, shlibs): '''If we are remotely running the test suite, register the shared libraries with the target so they get uploaded, otherwise do nothing Any modules in the target that have their remote install file specification set will get uploaded to the remote host. This function registers the local copies of the shared libraries with the target and sets their remote install locations so they will be uploaded when the target is run. ''' if not shlibs or not self.platformContext: return None shlib_environment_var = self.platformContext.shlib_environment_var shlib_prefix = self.platformContext.shlib_prefix shlib_extension = '.' + self.platformContext.shlib_extension working_dir = self.get_process_working_directory() environment = ['%s=%s' % (shlib_environment_var, working_dir)] # Add any shared libraries to our target if remote so they get # uploaded into the working directory on the remote side for name in shlibs: # The path can be a full path to a shared library, or a make file name like "Foo" for # "libFoo.dylib" or "libFoo.so", or "Foo.so" for "Foo.so" or "libFoo.so", or just a # basename like "libFoo.so". So figure out which one it is and resolve the local copy # of the shared library accordingly if os.path.isfile(name): local_shlib_path = name # name is the full path to the local shared library else: # Check relative names local_shlib_path = os.path.join( self.getBuildDir(), shlib_prefix + name + shlib_extension) if not os.path.exists(local_shlib_path): local_shlib_path = os.path.join( self.getBuildDir(), name + shlib_extension) if not os.path.exists(local_shlib_path): local_shlib_path = os.path.join(self.getBuildDir(), name) # Make sure we found the local shared library in the above code self.assertTrue(os.path.exists(local_shlib_path)) # Add the shared library to our target shlib_module = target.AddModule(local_shlib_path, None, None, None) if lldb.remote_platform: # We must set the remote install location if we want the shared library # to get uploaded to the remote target remote_shlib_path = lldbutil.append_to_process_working_directory(self, os.path.basename(local_shlib_path)) shlib_module.SetRemoteInstallFileSpec( lldb.SBFileSpec(remote_shlib_path, False)) return environment # utility methods that tests can use to access the current objects def target(self): if not self.dbg: raise Exception('Invalid debugger instance') return self.dbg.GetSelectedTarget() def process(self): if not self.dbg: raise Exception('Invalid debugger instance') return self.dbg.GetSelectedTarget().GetProcess() def thread(self): if not self.dbg: raise Exception('Invalid debugger instance') return self.dbg.GetSelectedTarget().GetProcess().GetSelectedThread() def frame(self): if not self.dbg: raise Exception('Invalid debugger instance') return self.dbg.GetSelectedTarget().GetProcess( ).GetSelectedThread().GetSelectedFrame() def get_process_working_directory(self): '''Get the working directory that should be used when launching processes for local or remote processes.''' if lldb.remote_platform: # Remote tests set the platform working directory up in # TestBase.setUp() return lldb.remote_platform.GetWorkingDirectory() else: # local tests change directory into each test subdirectory return self.getBuildDir() def tearDown(self): #import traceback # traceback.print_stack() # Ensure all the references to SB objects have gone away so that we can # be sure that all test-specific resources have been freed before we # attempt to delete the targets. gc.collect() # Delete the target(s) from the debugger as a general cleanup step. # This includes terminating the process for each target, if any. # We'd like to reuse the debugger for our next test without incurring # the initialization overhead. targets = [] for target in self.dbg: if target: targets.append(target) process = target.GetProcess() if process: rc = self.invoke(process, "Kill") self.assertTrue(rc.Success(), PROCESS_KILLED) for target in targets: self.dbg.DeleteTarget(target) # Do this last, to make sure it's in reverse order from how we setup. Base.tearDown(self) # This must be the last statement, otherwise teardown hooks or other # lines might depend on this still being active. del self.dbg def switch_to_thread_with_stop_reason(self, stop_reason): """ Run the 'thread list' command, and select the thread with stop reason as 'stop_reason'. If no such thread exists, no select action is done. """ from .lldbutil import stop_reason_to_str self.runCmd('thread list') output = self.res.GetOutput() thread_line_pattern = re.compile( "^[ *] thread #([0-9]+):.*stop reason = %s" % stop_reason_to_str(stop_reason)) for line in output.splitlines(): matched = thread_line_pattern.match(line) if matched: self.runCmd('thread select %s' % matched.group(1)) def runCmd(self, cmd, msg=None, check=True, trace=False, inHistory=False): """ Ask the command interpreter to handle the command and then check its return status. """ # Fail fast if 'cmd' is not meaningful. if not cmd or len(cmd) == 0: raise Exception("Bad 'cmd' parameter encountered") trace = (True if traceAlways else trace) if cmd.startswith("target create "): cmd = cmd.replace("target create ", "file ") running = (cmd.startswith("run") or cmd.startswith("process launch")) for i in range(self.maxLaunchCount if running else 1): self.ci.HandleCommand(cmd, self.res, inHistory) with recording(self, trace) as sbuf: print("runCmd:", cmd, file=sbuf) if not check: print("check of return status not required", file=sbuf) if self.res.Succeeded(): print("output:", self.res.GetOutput(), file=sbuf) else: print("runCmd failed!", file=sbuf) print(self.res.GetError(), file=sbuf) if self.res.Succeeded(): break elif running: # For process launch, wait some time before possible next try. time.sleep(self.timeWaitNextLaunch) with recording(self, trace) as sbuf: print("Command '" + cmd + "' failed!", file=sbuf) if check: self.assertTrue(self.res.Succeeded(), msg if msg else CMD_MSG(cmd)) def match( self, str, patterns, msg=None, trace=False, error=False, matching=True, exe=True): """run command in str, and match the result against regexp in patterns returning the match object for the first matching pattern Otherwise, all the arguments have the same meanings as for the expect function""" trace = (True if traceAlways else trace) if exe: # First run the command. If we are expecting error, set check=False. # Pass the assert message along since it provides more semantic # info. self.runCmd( str, msg=msg, trace=( True if trace else False), check=not error) # Then compare the output against expected strings. output = self.res.GetError() if error else self.res.GetOutput() # If error is True, the API client expects the command to fail! if error: self.assertFalse(self.res.Succeeded(), "Command '" + str + "' is expected to fail!") else: # No execution required, just compare str against the golden input. output = str with recording(self, trace) as sbuf: print("looking at:", output, file=sbuf) # The heading says either "Expecting" or "Not expecting". heading = "Expecting" if matching else "Not expecting" for pattern in patterns: # Match Objects always have a boolean value of True. match_object = re.search(pattern, output) matched = bool(match_object) with recording(self, trace) as sbuf: print("%s pattern: %s" % (heading, pattern), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) if matched: break self.assertTrue(matched if matching else not matched, msg if msg else EXP_MSG(str, output, exe)) return match_object def expect( self, str, msg=None, patterns=None, startstr=None, endstr=None, substrs=None, trace=False, error=False, matching=True, exe=True, inHistory=False): """ Similar to runCmd; with additional expect style output matching ability. Ask the command interpreter to handle the command and then check its return status. The 'msg' parameter specifies an informational assert message. We expect the output from running the command to start with 'startstr', matches the substrings contained in 'substrs', and regexp matches the patterns contained in 'patterns'. If the keyword argument error is set to True, it signifies that the API client is expecting the command to fail. In this case, the error stream from running the command is retrieved and compared against the golden input, instead. If the keyword argument matching is set to False, it signifies that the API client is expecting the output of the command not to match the golden input. Finally, the required argument 'str' represents the lldb command to be sent to the command interpreter. In case the keyword argument 'exe' is set to False, the 'str' is treated as a string to be matched/not-matched against the golden input. """ trace = (True if traceAlways else trace) if exe: # First run the command. If we are expecting error, set check=False. # Pass the assert message along since it provides more semantic # info. self.runCmd( str, msg=msg, trace=( True if trace else False), check=not error, inHistory=inHistory) # Then compare the output against expected strings. output = self.res.GetError() if error else self.res.GetOutput() # If error is True, the API client expects the command to fail! if error: self.assertFalse(self.res.Succeeded(), "Command '" + str + "' is expected to fail!") else: # No execution required, just compare str against the golden input. if isinstance(str, lldb.SBCommandReturnObject): output = str.GetOutput() else: output = str with recording(self, trace) as sbuf: print("looking at:", output, file=sbuf) if output is None: output = "" # The heading says either "Expecting" or "Not expecting". heading = "Expecting" if matching else "Not expecting" # Start from the startstr, if specified. # If there's no startstr, set the initial state appropriately. matched = output.startswith(startstr) if startstr else ( True if matching else False) if startstr: with recording(self, trace) as sbuf: print("%s start string: %s" % (heading, startstr), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) # Look for endstr, if specified. keepgoing = matched if matching else not matched if endstr: matched = output.endswith(endstr) with recording(self, trace) as sbuf: print("%s end string: %s" % (heading, endstr), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) # Look for sub strings, if specified. keepgoing = matched if matching else not matched if substrs and keepgoing: for substr in substrs: matched = output.find(substr) != -1 with recording(self, trace) as sbuf: print("%s sub string: %s" % (heading, substr), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) keepgoing = matched if matching else not matched if not keepgoing: break # Search for regular expression patterns, if specified. keepgoing = matched if matching else not matched if patterns and keepgoing: for pattern in patterns: # Match Objects always have a boolean value of True. matched = bool(re.search(pattern, output)) with recording(self, trace) as sbuf: print("%s pattern: %s" % (heading, pattern), file=sbuf) print("Matched" if matched else "Not matched", file=sbuf) keepgoing = matched if matching else not matched if not keepgoing: break self.assertTrue(matched if matching else not matched, msg if msg else EXP_MSG(str, output, exe)) def invoke(self, obj, name, trace=False): """Use reflection to call a method dynamically with no argument.""" trace = (True if traceAlways else trace) method = getattr(obj, name) import inspect self.assertTrue(inspect.ismethod(method), name + "is a method name of object: " + str(obj)) result = method() with recording(self, trace) as sbuf: print(str(method) + ":", result, file=sbuf) return result def build( self, architecture=None, compiler=None, dictionary=None): """Platform specific way to build the default binaries.""" module = builder_module() dictionary = lldbplatformutil.finalize_build_dictionary(dictionary) if self.getDebugInfo() is None: return self.buildDefault(architecture, compiler, dictionary) elif self.getDebugInfo() == "dsym": return self.buildDsym(architecture, compiler, dictionary) elif self.getDebugInfo() == "dwarf": return self.buildDwarf(architecture, compiler, dictionary) elif self.getDebugInfo() == "dwo": return self.buildDwo(architecture, compiler, dictionary) elif self.getDebugInfo() == "gmodules": return self.buildGModules(architecture, compiler, dictionary) else: self.fail("Can't build for debug info: %s" % self.getDebugInfo()) def run_platform_command(self, cmd): platform = self.dbg.GetSelectedPlatform() shell_command = lldb.SBPlatformShellCommand(cmd) err = platform.Run(shell_command) return (err, shell_command.GetStatus(), shell_command.GetOutput()) # ================================================= # Misc. helper methods for debugging test execution # ================================================= def DebugSBValue(self, val): """Debug print a SBValue object, if traceAlways is True.""" from .lldbutil import value_type_to_str if not traceAlways: return err = sys.stderr err.write(val.GetName() + ":\n") err.write('\t' + "TypeName -> " + val.GetTypeName() + '\n') err.write('\t' + "ByteSize -> " + str(val.GetByteSize()) + '\n') err.write('\t' + "NumChildren -> " + str(val.GetNumChildren()) + '\n') err.write('\t' + "Value -> " + str(val.GetValue()) + '\n') err.write('\t' + "ValueAsUnsigned -> " + str(val.GetValueAsUnsigned()) + '\n') err.write( '\t' + "ValueType -> " + value_type_to_str( val.GetValueType()) + '\n') err.write('\t' + "Summary -> " + str(val.GetSummary()) + '\n') err.write('\t' + "IsPointerType -> " + str(val.TypeIsPointerType()) + '\n') err.write('\t' + "Location -> " + val.GetLocation() + '\n') def DebugSBType(self, type): """Debug print a SBType object, if traceAlways is True.""" if not traceAlways: return err = sys.stderr err.write(type.GetName() + ":\n") err.write('\t' + "ByteSize -> " + str(type.GetByteSize()) + '\n') err.write('\t' + "IsPointerType -> " + str(type.IsPointerType()) + '\n') err.write('\t' + "IsReferenceType -> " + str(type.IsReferenceType()) + '\n') def DebugPExpect(self, child): """Debug the spwaned pexpect object.""" if not traceAlways: return print(child) @classmethod def RemoveTempFile(cls, file): if os.path.exists(file): remove_file(file) # On Windows, the first attempt to delete a recently-touched file can fail # because of a race with antimalware scanners. This function will detect a # failure and retry. def remove_file(file, num_retries=1, sleep_duration=0.5): for i in range(num_retries + 1): try: os.remove(file) return True except: time.sleep(sleep_duration) continue return False Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lldbutil.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lldbutil.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lldbutil.py (revision 337147) @@ -1,1353 +1,1353 @@ """ This LLDB module contains miscellaneous utilities. Some of the test suite takes advantage of the utility functions defined here. They can also be useful for general purpose lldb scripting. """ from __future__ import print_function from __future__ import absolute_import # System modules import collections import errno import os import re import sys import time # Third-party modules from six import StringIO as SixStringIO import six # LLDB modules import lldb # =================================================== # Utilities for locating/checking executable programs # =================================================== def is_exe(fpath): """Returns True if fpath is an executable.""" return os.path.isfile(fpath) and os.access(fpath, os.X_OK) def which(program): """Returns the full path to a program; None otherwise.""" fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file return None def mkdir_p(path): try: os.makedirs(path) except OSError as e: if e.errno != errno.EEXIST: raise if not os.path.isdir(path): raise OSError(errno.ENOTDIR, "%s is not a directory"%path) # =================================================== # Disassembly for an SBFunction or an SBSymbol object # =================================================== def disassemble(target, function_or_symbol): """Disassemble the function or symbol given a target. It returns the disassembly content in a string object. """ buf = SixStringIO() insts = function_or_symbol.GetInstructions(target) for i in insts: print(i, file=buf) return buf.getvalue() # ========================================================== # Integer (byte size 1, 2, 4, and 8) to bytearray conversion # ========================================================== def int_to_bytearray(val, bytesize): """Utility function to convert an integer into a bytearray. It returns the bytearray in the little endian format. It is easy to get the big endian format, just do ba.reverse() on the returned object. """ import struct if bytesize == 1: return bytearray([val]) # Little endian followed by a format character. template = "<%c" if bytesize == 2: fmt = template % 'h' elif bytesize == 4: fmt = template % 'i' elif bytesize == 4: fmt = template % 'q' else: return None packed = struct.pack(fmt, val) return bytearray(packed) def bytearray_to_int(bytes, bytesize): """Utility function to convert a bytearray into an integer. It interprets the bytearray in the little endian format. For a big endian bytearray, just do ba.reverse() on the object before passing it in. """ import struct if bytesize == 1: return bytes[0] # Little endian followed by a format character. template = "<%c" if bytesize == 2: fmt = template % 'h' elif bytesize == 4: fmt = template % 'i' elif bytesize == 4: fmt = template % 'q' else: return None unpacked = struct.unpack_from(fmt, bytes) return unpacked[0] # ============================================================== # Get the description of an lldb object or None if not available # ============================================================== def get_description(obj, option=None): """Calls lldb_obj.GetDescription() and returns a string, or None. For SBTarget, SBBreakpointLocation, and SBWatchpoint lldb objects, an extra option can be passed in to describe the detailed level of description desired: o lldb.eDescriptionLevelBrief o lldb.eDescriptionLevelFull o lldb.eDescriptionLevelVerbose """ method = getattr(obj, 'GetDescription') if not method: return None tuple = (lldb.SBTarget, lldb.SBBreakpointLocation, lldb.SBWatchpoint) if isinstance(obj, tuple): if option is None: option = lldb.eDescriptionLevelBrief stream = lldb.SBStream() if option is None: success = method(stream) else: success = method(stream, option) if not success: return None return stream.GetData() # ================================================= # Convert some enum value to its string counterpart # ================================================= def state_type_to_str(enum): """Returns the stateType string given an enum.""" if enum == lldb.eStateInvalid: return "invalid" elif enum == lldb.eStateUnloaded: return "unloaded" elif enum == lldb.eStateConnected: return "connected" elif enum == lldb.eStateAttaching: return "attaching" elif enum == lldb.eStateLaunching: return "launching" elif enum == lldb.eStateStopped: return "stopped" elif enum == lldb.eStateRunning: return "running" elif enum == lldb.eStateStepping: return "stepping" elif enum == lldb.eStateCrashed: return "crashed" elif enum == lldb.eStateDetached: return "detached" elif enum == lldb.eStateExited: return "exited" elif enum == lldb.eStateSuspended: return "suspended" else: raise Exception("Unknown StateType enum") def stop_reason_to_str(enum): """Returns the stopReason string given an enum.""" if enum == lldb.eStopReasonInvalid: return "invalid" elif enum == lldb.eStopReasonNone: return "none" elif enum == lldb.eStopReasonTrace: return "trace" elif enum == lldb.eStopReasonBreakpoint: return "breakpoint" elif enum == lldb.eStopReasonWatchpoint: return "watchpoint" elif enum == lldb.eStopReasonExec: return "exec" elif enum == lldb.eStopReasonSignal: return "signal" elif enum == lldb.eStopReasonException: return "exception" elif enum == lldb.eStopReasonPlanComplete: return "plancomplete" elif enum == lldb.eStopReasonThreadExiting: return "threadexiting" else: raise Exception("Unknown StopReason enum") def symbol_type_to_str(enum): """Returns the symbolType string given an enum.""" if enum == lldb.eSymbolTypeInvalid: return "invalid" elif enum == lldb.eSymbolTypeAbsolute: return "absolute" elif enum == lldb.eSymbolTypeCode: return "code" elif enum == lldb.eSymbolTypeData: return "data" elif enum == lldb.eSymbolTypeTrampoline: return "trampoline" elif enum == lldb.eSymbolTypeRuntime: return "runtime" elif enum == lldb.eSymbolTypeException: return "exception" elif enum == lldb.eSymbolTypeSourceFile: return "sourcefile" elif enum == lldb.eSymbolTypeHeaderFile: return "headerfile" elif enum == lldb.eSymbolTypeObjectFile: return "objectfile" elif enum == lldb.eSymbolTypeCommonBlock: return "commonblock" elif enum == lldb.eSymbolTypeBlock: return "block" elif enum == lldb.eSymbolTypeLocal: return "local" elif enum == lldb.eSymbolTypeParam: return "param" elif enum == lldb.eSymbolTypeVariable: return "variable" elif enum == lldb.eSymbolTypeVariableType: return "variabletype" elif enum == lldb.eSymbolTypeLineEntry: return "lineentry" elif enum == lldb.eSymbolTypeLineHeader: return "lineheader" elif enum == lldb.eSymbolTypeScopeBegin: return "scopebegin" elif enum == lldb.eSymbolTypeScopeEnd: return "scopeend" elif enum == lldb.eSymbolTypeAdditional: return "additional" elif enum == lldb.eSymbolTypeCompiler: return "compiler" elif enum == lldb.eSymbolTypeInstrumentation: return "instrumentation" elif enum == lldb.eSymbolTypeUndefined: return "undefined" def value_type_to_str(enum): """Returns the valueType string given an enum.""" if enum == lldb.eValueTypeInvalid: return "invalid" elif enum == lldb.eValueTypeVariableGlobal: return "global_variable" elif enum == lldb.eValueTypeVariableStatic: return "static_variable" elif enum == lldb.eValueTypeVariableArgument: return "argument_variable" elif enum == lldb.eValueTypeVariableLocal: return "local_variable" elif enum == lldb.eValueTypeRegister: return "register" elif enum == lldb.eValueTypeRegisterSet: return "register_set" elif enum == lldb.eValueTypeConstResult: return "constant_result" else: raise Exception("Unknown ValueType enum") # ================================================== # Get stopped threads due to each stop reason. # ================================================== def sort_stopped_threads(process, breakpoint_threads=None, crashed_threads=None, watchpoint_threads=None, signal_threads=None, exiting_threads=None, other_threads=None): """ Fills array *_threads with threads stopped for the corresponding stop reason. """ for lst in [breakpoint_threads, watchpoint_threads, signal_threads, exiting_threads, other_threads]: if lst is not None: lst[:] = [] for thread in process: dispatched = False for (reason, list) in [(lldb.eStopReasonBreakpoint, breakpoint_threads), (lldb.eStopReasonException, crashed_threads), (lldb.eStopReasonWatchpoint, watchpoint_threads), (lldb.eStopReasonSignal, signal_threads), (lldb.eStopReasonThreadExiting, exiting_threads), (None, other_threads)]: if not dispatched and list is not None: if thread.GetStopReason() == reason or reason is None: list.append(thread) dispatched = True # ================================================== # Utility functions for setting breakpoints # ================================================== def run_break_set_by_file_and_line( test, file_name, line_number, extra_options=None, num_expected_locations=1, loc_exact=False, module_name=None): """Set a breakpoint by file and line, returning the breakpoint number. If extra_options is not None, then we append it to the breakpoint set command. If num_expected_locations is -1, we check that we got AT LEAST one location. If num_expected_locations is -2, we don't check the actual number at all. Otherwise, we check that num_expected_locations equals the number of locations. If loc_exact is true, we check that there is one location, and that location must be at the input file and line number.""" if file_name is None: command = 'breakpoint set -l %d' % (line_number) else: command = 'breakpoint set -f "%s" -l %d' % (file_name, line_number) if module_name: command += " --shlib '%s'" % (module_name) if extra_options: command += " " + extra_options break_results = run_break_set_command(test, command) if num_expected_locations == 1 and loc_exact: check_breakpoint_result( test, break_results, num_locations=num_expected_locations, file_name=file_name, line_number=line_number, module_name=module_name) else: check_breakpoint_result( test, break_results, num_locations=num_expected_locations) return get_bpno_from_match(break_results) def run_break_set_by_symbol( test, symbol, extra_options=None, num_expected_locations=-1, sym_exact=False, module_name=None): """Set a breakpoint by symbol name. Common options are the same as run_break_set_by_file_and_line. If sym_exact is true, then the output symbol must match the input exactly, otherwise we do a substring match.""" command = 'breakpoint set -n "%s"' % (symbol) if module_name: command += " --shlib '%s'" % (module_name) if extra_options: command += " " + extra_options break_results = run_break_set_command(test, command) if num_expected_locations == 1 and sym_exact: check_breakpoint_result( test, break_results, num_locations=num_expected_locations, symbol_name=symbol, module_name=module_name) else: check_breakpoint_result( test, break_results, num_locations=num_expected_locations) return get_bpno_from_match(break_results) def run_break_set_by_selector( test, selector, extra_options=None, num_expected_locations=-1, module_name=None): """Set a breakpoint by selector. Common options are the same as run_break_set_by_file_and_line.""" command = 'breakpoint set -S "%s"' % (selector) if module_name: command += ' --shlib "%s"' % (module_name) if extra_options: command += " " + extra_options break_results = run_break_set_command(test, command) if num_expected_locations == 1: check_breakpoint_result( test, break_results, num_locations=num_expected_locations, symbol_name=selector, symbol_match_exact=False, module_name=module_name) else: check_breakpoint_result( test, break_results, num_locations=num_expected_locations) return get_bpno_from_match(break_results) def run_break_set_by_regexp( test, regexp, extra_options=None, num_expected_locations=-1): """Set a breakpoint by regular expression match on symbol name. Common options are the same as run_break_set_by_file_and_line.""" command = 'breakpoint set -r "%s"' % (regexp) if extra_options: command += " " + extra_options break_results = run_break_set_command(test, command) check_breakpoint_result( test, break_results, num_locations=num_expected_locations) return get_bpno_from_match(break_results) def run_break_set_by_source_regexp( test, regexp, extra_options=None, num_expected_locations=-1): """Set a breakpoint by source regular expression. Common options are the same as run_break_set_by_file_and_line.""" command = 'breakpoint set -p "%s"' % (regexp) if extra_options: command += " " + extra_options break_results = run_break_set_command(test, command) check_breakpoint_result( test, break_results, num_locations=num_expected_locations) return get_bpno_from_match(break_results) def run_break_set_command(test, command): """Run the command passed in - it must be some break set variant - and analyze the result. Returns a dictionary of information gleaned from the command-line results. Will assert if the breakpoint setting fails altogether. Dictionary will contain: bpno - breakpoint of the newly created breakpoint, -1 on error. num_locations - number of locations set for the breakpoint. If there is only one location, the dictionary MAY contain: file - source file name line_no - source line number symbol - symbol name inline_symbol - inlined symbol name offset - offset from the original symbol module - module address - address at which the breakpoint was set.""" patterns = [ r"^Breakpoint (?P[0-9]+): (?P[0-9]+) locations\.$", r"^Breakpoint (?P[0-9]+): (?Pno) locations \(pending\)\.", r"^Breakpoint (?P[0-9]+): where = (?P.*)`(?P[+\-]{0,1}[^+]+)( \+ (?P[0-9]+)){0,1}( \[inlined\] (?P.*)){0,1} at (?P[^:]+):(?P[0-9]+), address = (?P
0x[0-9a-fA-F]+)$", r"^Breakpoint (?P[0-9]+): where = (?P.*)`(?P.*)( \+ (?P[0-9]+)){0,1}, address = (?P
0x[0-9a-fA-F]+)$"] match_object = test.match(command, patterns) break_results = match_object.groupdict() # We always insert the breakpoint number, setting it to -1 if we couldn't find it # Also, make sure it gets stored as an integer. if not 'bpno' in break_results: break_results['bpno'] = -1 else: break_results['bpno'] = int(break_results['bpno']) # We always insert the number of locations # If ONE location is set for the breakpoint, then the output doesn't mention locations, but it has to be 1... # We also make sure it is an integer. if not 'num_locations' in break_results: num_locations = 1 else: num_locations = break_results['num_locations'] if num_locations == 'no': num_locations = 0 else: num_locations = int(break_results['num_locations']) break_results['num_locations'] = num_locations if 'line_no' in break_results: break_results['line_no'] = int(break_results['line_no']) return break_results def get_bpno_from_match(break_results): return int(break_results['bpno']) def check_breakpoint_result( test, break_results, file_name=None, line_number=-1, symbol_name=None, symbol_match_exact=True, module_name=None, offset=-1, num_locations=-1): out_num_locations = break_results['num_locations'] if num_locations == -1: test.assertTrue(out_num_locations > 0, "Expecting one or more locations, got none.") elif num_locations != -2: test.assertTrue( num_locations == out_num_locations, "Expecting %d locations, got %d." % (num_locations, out_num_locations)) if file_name: out_file_name = "" if 'file' in break_results: out_file_name = break_results['file'] test.assertTrue( file_name.endswith(out_file_name), "Breakpoint file name '%s' doesn't match resultant name '%s'." % (file_name, out_file_name)) if line_number != -1: out_line_number = -1 if 'line_no' in break_results: out_line_number = break_results['line_no'] test.assertTrue( line_number == out_line_number, "Breakpoint line number %s doesn't match resultant line %s." % (line_number, out_line_number)) if symbol_name: out_symbol_name = "" # Look first for the inlined symbol name, otherwise use the symbol # name: if 'inline_symbol' in break_results and break_results['inline_symbol']: out_symbol_name = break_results['inline_symbol'] elif 'symbol' in break_results: out_symbol_name = break_results['symbol'] if symbol_match_exact: test.assertTrue( symbol_name == out_symbol_name, "Symbol name '%s' doesn't match resultant symbol '%s'." % (symbol_name, out_symbol_name)) else: test.assertTrue( out_symbol_name.find(symbol_name) != - 1, "Symbol name '%s' isn't in resultant symbol '%s'." % (symbol_name, out_symbol_name)) if module_name: out_module_name = None if 'module' in break_results: out_module_name = break_results['module'] test.assertTrue( module_name.find(out_module_name) != - 1, "Symbol module name '%s' isn't in expected module name '%s'." % (out_module_name, module_name)) # ================================================== # Utility functions related to Threads and Processes # ================================================== def get_stopped_threads(process, reason): """Returns the thread(s) with the specified stop reason in a list. The list can be empty if no such thread exists. """ threads = [] for t in process: if t.GetStopReason() == reason: threads.append(t) return threads def get_stopped_thread(process, reason): """A convenience function which returns the first thread with the given stop reason or None. Example usages: 1. Get the stopped thread due to a breakpoint condition ... from lldbutil import get_stopped_thread thread = get_stopped_thread(process, lldb.eStopReasonPlanComplete) self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition") ... 2. Get the thread stopped due to a breakpoint ... from lldbutil import get_stopped_thread thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint") ... """ threads = get_stopped_threads(process, reason) if len(threads) == 0: return None return threads[0] def get_threads_stopped_at_breakpoint_id(process, bpid): """ For a stopped process returns the thread stopped at the breakpoint passed in bkpt""" stopped_threads = [] threads = [] stopped_threads = get_stopped_threads(process, lldb.eStopReasonBreakpoint) if len(stopped_threads) == 0: return threads for thread in stopped_threads: # Make sure we've hit our breakpoint... break_id = thread.GetStopReasonDataAtIndex(0) if break_id == bpid: threads.append(thread) return threads def get_threads_stopped_at_breakpoint(process, bkpt): return get_threads_stopped_at_breakpoint_id(process, bkpt.GetID()) def get_one_thread_stopped_at_breakpoint_id( process, bpid, require_exactly_one=True): threads = get_threads_stopped_at_breakpoint_id(process, bpid) if len(threads) == 0: return None if require_exactly_one and len(threads) != 1: return None return threads[0] def get_one_thread_stopped_at_breakpoint( process, bkpt, require_exactly_one=True): return get_one_thread_stopped_at_breakpoint_id( process, bkpt.GetID(), require_exactly_one) def is_thread_crashed(test, thread): """In the test suite we dereference a null pointer to simulate a crash. The way this is reported depends on the platform.""" if test.platformIsDarwin(): return thread.GetStopReason( ) == lldb.eStopReasonException and "EXC_BAD_ACCESS" in thread.GetStopDescription(100) elif test.getPlatform() == "linux": return thread.GetStopReason() == lldb.eStopReasonSignal and thread.GetStopReasonDataAtIndex( 0) == thread.GetProcess().GetUnixSignals().GetSignalNumberFromName("SIGSEGV") else: return "invalid address" in thread.GetStopDescription(100) def get_crashed_threads(test, process): threads = [] if process.GetState() != lldb.eStateStopped: return threads for thread in process: if is_thread_crashed(test, thread): threads.append(thread) return threads # Helper functions for run_to_{source,name}_breakpoint: def run_to_breakpoint_make_target(test, exe_name, in_cwd): if in_cwd: exe = test.getBuildArtifact(exe_name) - + # Create the target target = test.dbg.CreateTarget(exe) test.assertTrue(target, "Target: %s is not valid."%(exe_name)) return target def run_to_breakpoint_do_run(test, target, bkpt, launch_info): # Launch the process, and do not stop at the entry point. if not launch_info: launch_info = lldb.SBLaunchInfo(None) launch_info.SetWorkingDirectory(test.get_process_working_directory()) error = lldb.SBError() process = target.Launch(launch_info, error) - test.assertTrue(process, - "Could not create a valid process for %s: %s"%(target.GetExecutable().GetFilename(), + test.assertTrue(process, + "Could not create a valid process for %s: %s"%(target.GetExecutable().GetFilename(), error.GetCString())) # Frame #0 should be at our breakpoint. threads = get_threads_stopped_at_breakpoint( process, bkpt) test.assertTrue(len(threads) == 1, "Expected 1 thread to stop at breakpoint, %d did."%(len(threads))) thread = threads[0] return (target, process, thread, bkpt) -def run_to_name_breakpoint (test, bkpt_name, launch_info = None, +def run_to_name_breakpoint (test, bkpt_name, launch_info = None, exe_name = "a.out", bkpt_module = None, in_cwd = True): """Start up a target, using exe_name as the executable, and run it to a breakpoint set by name on bkpt_name restricted to bkpt_module. If you want to pass in launch arguments or environment variables, you can optionally pass in an SBLaunchInfo. If you do that, remember to set the working directory as well. If your executable isn't called a.out, you can pass that in. And if your executable isn't in the CWD, pass in the absolute path to the executable in exe_name, and set in_cwd to False. If you need to restrict the breakpoint to a particular module, pass the module name (a string not a FileSpec) in bkpt_module. If nothing is passed in setting will be unrestricted. If the target isn't valid, the breakpoint isn't found, or hit, the function will cause a testsuite failure. If successful it returns a tuple with the target process and thread that hit the breakpoint, and the breakpoint that we set for you. """ target = run_to_breakpoint_make_target(test, exe_name, in_cwd) breakpoint = target.BreakpointCreateByName(bkpt_name, bkpt_module) test.assertTrue(breakpoint.GetNumLocations() > 0, "No locations found for name breakpoint: '%s'."%(bkpt_name)) return run_to_breakpoint_do_run(test, target, breakpoint, launch_info) def run_to_source_breakpoint(test, bkpt_pattern, source_spec, launch_info = None, exe_name = "a.out", bkpt_module = None, in_cwd = True): """Start up a target, using exe_name as the executable, and run it to a breakpoint set by source regex bkpt_pattern. The rest of the behavior is the same as run_to_name_breakpoint. """ target = run_to_breakpoint_make_target(test, exe_name, in_cwd) # Set the breakpoints breakpoint = target.BreakpointCreateBySourceRegex( bkpt_pattern, source_spec, bkpt_module) - test.assertTrue(breakpoint.GetNumLocations() > 0, + test.assertTrue(breakpoint.GetNumLocations() > 0, 'No locations found for source breakpoint: "%s", file: "%s", dir: "%s"'%(bkpt_pattern, source_spec.GetFilename(), source_spec.GetDirectory())) return run_to_breakpoint_do_run(test, target, breakpoint, launch_info) def continue_to_breakpoint(process, bkpt): """ Continues the process, if it stops, returns the threads stopped at bkpt; otherwise, returns None""" process.Continue() if process.GetState() != lldb.eStateStopped: return None else: return get_threads_stopped_at_breakpoint(process, bkpt) def get_caller_symbol(thread): """ Returns the symbol name for the call site of the leaf function. """ depth = thread.GetNumFrames() if depth <= 1: return None caller = thread.GetFrameAtIndex(1).GetSymbol() if caller: return caller.GetName() else: return None def get_function_names(thread): """ Returns a sequence of function names from the stack frames of this thread. """ def GetFuncName(i): return thread.GetFrameAtIndex(i).GetFunctionName() return list(map(GetFuncName, list(range(thread.GetNumFrames())))) def get_symbol_names(thread): """ Returns a sequence of symbols for this thread. """ def GetSymbol(i): return thread.GetFrameAtIndex(i).GetSymbol().GetName() return list(map(GetSymbol, list(range(thread.GetNumFrames())))) def get_pc_addresses(thread): """ Returns a sequence of pc addresses for this thread. """ def GetPCAddress(i): return thread.GetFrameAtIndex(i).GetPCAddress() return list(map(GetPCAddress, list(range(thread.GetNumFrames())))) def get_filenames(thread): """ Returns a sequence of file names from the stack frames of this thread. """ def GetFilename(i): return thread.GetFrameAtIndex( i).GetLineEntry().GetFileSpec().GetFilename() return list(map(GetFilename, list(range(thread.GetNumFrames())))) def get_line_numbers(thread): """ Returns a sequence of line numbers from the stack frames of this thread. """ def GetLineNumber(i): return thread.GetFrameAtIndex(i).GetLineEntry().GetLine() return list(map(GetLineNumber, list(range(thread.GetNumFrames())))) def get_module_names(thread): """ Returns a sequence of module names from the stack frames of this thread. """ def GetModuleName(i): return thread.GetFrameAtIndex( i).GetModule().GetFileSpec().GetFilename() return list(map(GetModuleName, list(range(thread.GetNumFrames())))) def get_stack_frames(thread): """ Returns a sequence of stack frames for this thread. """ def GetStackFrame(i): return thread.GetFrameAtIndex(i) return list(map(GetStackFrame, list(range(thread.GetNumFrames())))) def print_stacktrace(thread, string_buffer=False): """Prints a simple stack trace of this thread.""" output = SixStringIO() if string_buffer else sys.stdout target = thread.GetProcess().GetTarget() depth = thread.GetNumFrames() mods = get_module_names(thread) funcs = get_function_names(thread) symbols = get_symbol_names(thread) files = get_filenames(thread) lines = get_line_numbers(thread) addrs = get_pc_addresses(thread) if thread.GetStopReason() != lldb.eStopReasonInvalid: desc = "stop reason=" + stop_reason_to_str(thread.GetStopReason()) else: desc = "" print( "Stack trace for thread id={0:#x} name={1} queue={2} ".format( thread.GetThreadID(), thread.GetName(), thread.GetQueueName()) + desc, file=output) for i in range(depth): frame = thread.GetFrameAtIndex(i) function = frame.GetFunction() load_addr = addrs[i].GetLoadAddress(target) if not function: file_addr = addrs[i].GetFileAddress() start_addr = frame.GetSymbol().GetStartAddress().GetFileAddress() symbol_offset = file_addr - start_addr print( " frame #{num}: {addr:#016x} {mod}`{symbol} + {offset}".format( num=i, addr=load_addr, mod=mods[i], symbol=symbols[i], offset=symbol_offset), file=output) else: print( " frame #{num}: {addr:#016x} {mod}`{func} at {file}:{line} {args}".format( num=i, addr=load_addr, mod=mods[i], func='%s [inlined]' % funcs[i] if frame.IsInlined() else funcs[i], file=files[i], line=lines[i], args=get_args_as_string( frame, showFuncName=False) if not frame.IsInlined() else '()'), file=output) if string_buffer: return output.getvalue() def print_stacktraces(process, string_buffer=False): """Prints the stack traces of all the threads.""" output = SixStringIO() if string_buffer else sys.stdout print("Stack traces for " + str(process), file=output) for thread in process: print(print_stacktrace(thread, string_buffer=True), file=output) if string_buffer: return output.getvalue() def expect_state_changes(test, listener, process, states, timeout=5): """Listens for state changed events on the listener and makes sure they match what we expect. Stop-and-restart events (where GetRestartedFromEvent() returns true) are ignored.""" for expected_state in states: def get_next_event(): event = lldb.SBEvent() if not listener.WaitForEventForBroadcasterWithType( timeout, process.GetBroadcaster(), lldb.SBProcess.eBroadcastBitStateChanged, event): test.fail( "Timed out while waiting for a transition to state %s" % lldb.SBDebugger.StateAsCString(expected_state)) return event event = get_next_event() while (lldb.SBProcess.GetStateFromEvent(event) == lldb.eStateStopped and lldb.SBProcess.GetRestartedFromEvent(event)): # Ignore restarted event and the subsequent running event. event = get_next_event() test.assertEqual( lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning, "Restarted event followed by a running event") event = get_next_event() test.assertEqual( lldb.SBProcess.GetStateFromEvent(event), expected_state) # =================================== # Utility functions related to Frames # =================================== def get_parent_frame(frame): """ Returns the parent frame of the input frame object; None if not available. """ thread = frame.GetThread() parent_found = False for f in thread: if parent_found: return f if f.GetFrameID() == frame.GetFrameID(): parent_found = True # If we reach here, no parent has been found, return None. return None def get_args_as_string(frame, showFuncName=True): """ Returns the args of the input frame object as a string. """ # arguments => True # locals => False # statics => False # in_scope_only => True vars = frame.GetVariables(True, False, False, True) # type of SBValueList args = [] # list of strings for var in vars: args.append("(%s)%s=%s" % (var.GetTypeName(), var.GetName(), var.GetValue())) if frame.GetFunction(): name = frame.GetFunction().GetName() elif frame.GetSymbol(): name = frame.GetSymbol().GetName() else: name = "" if showFuncName: return "%s(%s)" % (name, ", ".join(args)) else: return "(%s)" % (", ".join(args)) def print_registers(frame, string_buffer=False): """Prints all the register sets of the frame.""" output = SixStringIO() if string_buffer else sys.stdout print("Register sets for " + str(frame), file=output) registerSet = frame.GetRegisters() # Return type of SBValueList. print("Frame registers (size of register set = %d):" % registerSet.GetSize(), file=output) for value in registerSet: #print(value, file=output) print("%s (number of children = %d):" % (value.GetName(), value.GetNumChildren()), file=output) for child in value: print( "Name: %s, Value: %s" % (child.GetName(), child.GetValue()), file=output) if string_buffer: return output.getvalue() def get_registers(frame, kind): """Returns the registers given the frame and the kind of registers desired. Returns None if there's no such kind. """ registerSet = frame.GetRegisters() # Return type of SBValueList. for value in registerSet: if kind.lower() in value.GetName().lower(): return value return None def get_GPRs(frame): """Returns the general purpose registers of the frame as an SBValue. The returned SBValue object is iterable. An example: ... from lldbutil import get_GPRs regs = get_GPRs(frame) for reg in regs: print("%s => %s" % (reg.GetName(), reg.GetValue())) ... """ return get_registers(frame, "general purpose") def get_FPRs(frame): """Returns the floating point registers of the frame as an SBValue. The returned SBValue object is iterable. An example: ... from lldbutil import get_FPRs regs = get_FPRs(frame) for reg in regs: print("%s => %s" % (reg.GetName(), reg.GetValue())) ... """ return get_registers(frame, "floating point") def get_ESRs(frame): """Returns the exception state registers of the frame as an SBValue. The returned SBValue object is iterable. An example: ... from lldbutil import get_ESRs regs = get_ESRs(frame) for reg in regs: print("%s => %s" % (reg.GetName(), reg.GetValue())) ... """ return get_registers(frame, "exception state") # ====================================== # Utility classes/functions for SBValues # ====================================== class BasicFormatter(object): """The basic formatter inspects the value object and prints the value.""" def format(self, value, buffer=None, indent=0): if not buffer: output = SixStringIO() else: output = buffer # If there is a summary, it suffices. val = value.GetSummary() # Otherwise, get the value. if val is None: val = value.GetValue() if val is None and value.GetNumChildren() > 0: val = "%s (location)" % value.GetLocation() print("{indentation}({type}) {name} = {value}".format( indentation=' ' * indent, type=value.GetTypeName(), name=value.GetName(), value=val), file=output) return output.getvalue() class ChildVisitingFormatter(BasicFormatter): """The child visiting formatter prints the value and its immediate children. The constructor takes a keyword arg: indent_child, which defaults to 2. """ def __init__(self, indent_child=2): """Default indentation of 2 SPC's for the children.""" self.cindent = indent_child def format(self, value, buffer=None): if not buffer: output = SixStringIO() else: output = buffer BasicFormatter.format(self, value, buffer=output) for child in value: BasicFormatter.format( self, child, buffer=output, indent=self.cindent) return output.getvalue() class RecursiveDecentFormatter(BasicFormatter): """The recursive decent formatter prints the value and the decendents. The constructor takes two keyword args: indent_level, which defaults to 0, and indent_child, which defaults to 2. The current indentation level is determined by indent_level, while the immediate children has an additional indentation by inden_child. """ def __init__(self, indent_level=0, indent_child=2): self.lindent = indent_level self.cindent = indent_child def format(self, value, buffer=None): if not buffer: output = SixStringIO() else: output = buffer BasicFormatter.format(self, value, buffer=output, indent=self.lindent) new_indent = self.lindent + self.cindent for child in value: if child.GetSummary() is not None: BasicFormatter.format( self, child, buffer=output, indent=new_indent) else: if child.GetNumChildren() > 0: rdf = RecursiveDecentFormatter(indent_level=new_indent) rdf.format(child, buffer=output) else: BasicFormatter.format( self, child, buffer=output, indent=new_indent) return output.getvalue() # =========================================================== # Utility functions for path manipulation on remote platforms # =========================================================== def join_remote_paths(*paths): # TODO: update with actual platform name for remote windows once it exists if lldb.remote_platform.GetName() == 'remote-windows': return os.path.join(*paths).replace(os.path.sep, '\\') return os.path.join(*paths).replace(os.path.sep, '/') def append_to_process_working_directory(test, *paths): remote = lldb.remote_platform if remote: return join_remote_paths(remote.GetWorkingDirectory(), *paths) return os.path.join(test.getBuildDir(), *paths) # ================================================== # Utility functions to get the correct signal number # ================================================== import signal def get_signal_number(signal_name): platform = lldb.remote_platform if platform and platform.IsValid(): signals = platform.GetUnixSignals() if signals.IsValid(): signal_number = signals.GetSignalNumberFromName(signal_name) if signal_number > 0: return signal_number # No remote platform; fall back to using local python signals. return getattr(signal, signal_name) class PrintableRegex(object): def __init__(self, text): self.regex = re.compile(text) self.text = text def match(self, str): return self.regex.match(str) def __str__(self): return "%s" % (self.text) def __repr__(self): return "re.compile(%s) -> %s" % (self.text, self.regex) def skip_if_callable(test, mycallable, reason): if six.callable(mycallable): if mycallable(test): test.skipTest(reason) return True return False def skip_if_library_missing(test, target, library): def find_library(target, library): for module in target.modules: filename = module.file.GetFilename() if isinstance(library, str): if library == filename: return False elif hasattr(library, 'match'): if library.match(filename): return False return True def find_library_callable(test): return find_library(target, library) return skip_if_callable( test, find_library_callable, "could not find library matching '%s' in target %s" % (library, target)) def read_file_on_target(test, remote): if lldb.remote_platform: local = test.getBuildArtifact("file_from_target") error = lldb.remote_platform.Get(lldb.SBFileSpec(remote, False), lldb.SBFileSpec(local, True)) test.assertTrue(error.Success(), "Reading file {0} failed: {1}".format(remote, error)) else: local = remote with open(local, 'r') as f: return f.read() def read_file_from_process_wd(test, name): path = append_to_process_working_directory(test, name) return read_file_on_target(test, path) def wait_for_file_on_target(testcase, file_path, max_attempts=6): for i in range(max_attempts): err, retcode, msg = testcase.run_platform_command("ls %s" % file_path) if err.Success() and retcode == 0: break if i < max_attempts: # Exponential backoff! import time time.sleep(pow(2, i) * 0.25) else: testcase.fail( "File %s not found even after %d attempts." % (file_path, max_attempts)) return read_file_on_target(testcase, file_path) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-app-in-bundle/TestFindAppInBundle.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-app-in-bundle/TestFindAppInBundle.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-app-in-bundle/TestFindAppInBundle.py (revision 337147) @@ -1,66 +1,66 @@ """ Make sure we can find the binary inside an app bundle. """ from __future__ import print_function import os import time import re import lldb from lldbsuite.test.decorators import * import lldbsuite.test.lldbutil as lldbutil import lldbsuite.test.lldbplatformutil as lldbplatformutil from lldbsuite.test.lldbtest import * @decorators.skipUnlessDarwin class FindAppInMacOSAppBundle(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True def test_find_app_in_bundle(self): """There can be many tests in a test case - describe this test here.""" self.build() self.main_source_file = lldb.SBFileSpec("main.c") self.find_app_in_bundle_test() def setUp(self): # Call super's setUp(). TestBase.setUp(self) def find_app_in_bundle_test(self): """This reads in the .app, makes sure we get the right binary and can run it.""" # This function starts a process, "a.out" by default, sets a source # breakpoint, runs to it, and returns the thread, process & target. # It optionally takes an SBLaunchOption argument if you want to pass # arguments or environment variables. exe = self.getBuildArtifact("TestApp.app") error = lldb.SBError() target = self.dbg.CreateTarget(exe, None, None, False, error) self.assertTrue(error.Success(), "Could not create target: %s"%(error.GetCString())) self.assertTrue(target.IsValid(), "Target: TestApp.app is not valid.") exe_module_spec = target.GetExecutable() self.assertTrue(exe_module_spec.GetFilename(), "TestApp") bkpt = target.BreakpointCreateBySourceRegex("Set a breakpoint here", self.main_source_file) self.assertTrue(bkpt.GetNumLocations() == 1, "Couldn't set a breakpoint in the main app") if lldbplatformutil.getPlatform() == "macosx": launch_info = lldb.SBLaunchInfo(None) launch_info.SetWorkingDirectory(self.get_process_working_directory()) error = lldb.SBError() process = target.Launch(launch_info, error) - + self.assertTrue(process.IsValid(), "Could not create a valid process for TestApp: %s"%(error.GetCString())) - + # Frame #0 should be at our breakpoint. threads = lldbutil.get_threads_stopped_at_breakpoint(process, bkpt) - + self.assertTrue(len(threads) == 1, "Expected 1 thread to stop at breakpoint, %d did."%(len(threads))) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/TestBundleWithDotInFilename.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/TestBundleWithDotInFilename.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/TestBundleWithDotInFilename.py (revision 337147) @@ -1,73 +1,73 @@ """Test that a dSYM can be found when a binary is in a bundle hnd has dots in the filename.""" from __future__ import print_function #import unittest2 import os.path from time import sleep import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil exe_name = 'find-bundle-with-dots-in-fn' # must match Makefile class BundleWithDotInFilenameTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) @skipIfRemote @skipUnlessDarwin # This test is explicitly a dSYM test, it doesn't need to run for any other config, but # the following doesn't work, fixme. # @skipIf(debug_info=no_match(["dsym"]), bugnumber="This test is looking explicitly for a dSYM") def setUp(self): TestBase.setUp(self) self.source = 'main.c' def tearDown(self): # Destroy process before TestBase.tearDown() self.dbg.GetSelectedTarget().GetProcess().Destroy() # Call super's tearDown(). TestBase.tearDown(self) def test_attach_and_check_dsyms(self): """Test attach to binary, see if the bundle dSYM is found""" exe = self.getBuildArtifact(exe_name) self.build() os.chdir(self.getBuildDir()); popen = self.spawnSubprocess(exe) self.addTearDownHook(self.cleanupSubprocesses) # Give the inferior time to start up, dlopen a bundle, remove the bundle it linked in sleep(5) # Since the library that was dlopen()'ed is now removed, lldb will need to find the - # binary & dSYM via target.exec-search-paths + # binary & dSYM via target.exec-search-paths settings_str = "settings set target.exec-search-paths " + self.get_process_working_directory() + "/hide.app" self.runCmd(settings_str) self.runCmd("process attach -p " + str(popen.pid)) target = self.dbg.GetSelectedTarget() self.assertTrue(target.IsValid(), 'Should have a valid Target after attaching to process') setup_complete = target.FindFirstGlobalVariable("setup_is_complete") self.assertTrue(setup_complete.GetValueAsUnsigned() == 1, 'Check that inferior process has completed setup') # Find the bundle module, see if we found the dSYM too (they're both in "hide.app") i = 0 while i < target.GetNumModules(): mod = target.GetModuleAtIndex(i) if mod.GetFileSpec().GetFilename() == 'com.apple.sbd': dsym_name = mod.GetSymbolFileSpec().GetFilename() self.assertTrue (dsym_name == 'com.apple.sbd', "Check that we found the dSYM for the bundle that was loaded") i=i+1 os.chdir(self.getSourceDir()); if __name__ == '__main__': unittest.main() Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/TestDeepBundle.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/TestDeepBundle.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/TestDeepBundle.py (revision 337147) @@ -1,74 +1,74 @@ """Test that a dSYM can be found when a binary is in a deep bundle with multiple pathname components.""" from __future__ import print_function #import unittest2 import os.path from time import sleep import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil exe_name = 'deep-bundle' # must match Makefile class DeepBundleTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) @skipIfRemote @skipUnlessDarwin # This test is explicitly a dSYM test, it doesn't need to run for any other config, but # the following doesn't work, fixme. # @skipIf(debug_info=no_match(["dsym"]), bugnumber="This test is looking explicitly for a dSYM") def setUp(self): TestBase.setUp(self) self.source = 'main.c' def tearDown(self): # Destroy process before TestBase.tearDown() self.dbg.GetSelectedTarget().GetProcess().Destroy() # Call super's tearDown(). TestBase.tearDown(self) def test_attach_and_check_dsyms(self): """Test attach to binary, see if the framework dSYM is found""" exe = self.getBuildArtifact(exe_name) self.build() popen = self.spawnSubprocess(exe, [self.getBuildDir()]) self.addTearDownHook(self.cleanupSubprocesses) # Give the inferior time to start up, dlopen a bundle, remove the bundle it linked in sleep(5) # Since the library that was dlopen()'ed is now removed, lldb will need to find the - # binary & dSYM via target.exec-search-paths + # binary & dSYM via target.exec-search-paths settings_str = "settings set target.exec-search-paths " + self.get_process_working_directory() + "/hide.app" self.runCmd(settings_str) self.runCmd("process attach -p " + str(popen.pid)) target = self.dbg.GetSelectedTarget() self.assertTrue(target.IsValid(), 'Should have a valid Target after attaching to process') setup_complete = target.FindFirstGlobalVariable("setup_is_complete") self.assertTrue(setup_complete.GetValueAsUnsigned() == 1, 'Check that inferior process has completed setup') # Find the bundle module, see if we found the dSYM too (they're both in "hide.app") i = 0 found_module = False while i < target.GetNumModules(): mod = target.GetModuleAtIndex(i) if mod.GetFileSpec().GetFilename() == 'MyFramework': found_module = True dsym_name = mod.GetSymbolFileSpec().GetFilename() self.assertTrue (dsym_name == 'MyFramework', "Check that we found the dSYM for the bundle that was loaded") i=i+1 - + self.assertTrue(found_module, "Check that we found the framework loaded in lldb's image list") if __name__ == '__main__': unittest.main() Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/thread-names/TestInterruptThreadNames.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/thread-names/TestInterruptThreadNames.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/thread-names/TestInterruptThreadNames.py (revision 337147) @@ -1,139 +1,139 @@ -"""Test that we get thread names when interrupting a process.""" +"""Test that we get thread names when interrupting a process.""" from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestInterruptThreadNames(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) @skipUnlessDarwin @add_test_categories(['pyapi']) def test_with_python_api(self): """Test that we get thread names when interrupting a process.""" self.build() exe = self.getBuildArtifact("a.out") target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) launch_info = lldb.SBLaunchInfo(None) error = lldb.SBError() lldb.debugger.SetAsync(True) process = target.Launch(launch_info, error) self.assertTrue(process, PROCESS_IS_VALID) listener = lldb.debugger.GetListener() broadcaster = process.GetBroadcaster() rc = broadcaster.AddListener(listener, lldb.SBProcess.eBroadcastBitStateChanged) self.assertTrue(rc != 0, "Unable to add listener to process") self.assertTrue(self.wait_for_running(process, listener), "Check that process is up and running") inferior_set_up = self.wait_until_program_setup_complete(process, listener) self.assertTrue(inferior_set_up.IsValid() and inferior_set_up.GetValueAsSigned() == 1, "Check that the program was able to create its threads within the allotted time") self.check_number_of_threads(process) main_thread = lldb.SBThread() second_thread = lldb.SBThread() third_thread = lldb.SBThread() for idx in range(0, process.GetNumThreads()): t = process.GetThreadAtIndex(idx) if t.GetName() == "main thread": main_thread = t if t.GetName() == "second thread": second_thread = t if t.GetName() == "third thread": third_thread = t self.check_expected_threads_present(main_thread, second_thread, third_thread) process.Kill() - # The process will set a global variable 'threads_up_and_running' to 1 when + # The process will set a global variable 'threads_up_and_running' to 1 when # it has has completed its setup. Sleep for one second, pause the program, # check to see if the global has that value, and continue if it does not. def wait_until_program_setup_complete(self, process, listener): inferior_set_up = lldb.SBValue() retry = 5 while retry > 0: arch = self.getArchitecture() # when running the testsuite against a remote arm device, it may take # a little longer for the process to start up. Use a "can't possibly take # longer than this" value. if arch == 'arm64' or arch == 'armv7': time.sleep(10) else: time.sleep(1) process.SendAsyncInterrupt() self.assertTrue(self.wait_for_stop(process, listener), "Check that process is paused") inferior_set_up = process.GetTarget().CreateValueFromExpression("threads_up_and_running", "threads_up_and_running") if inferior_set_up.IsValid() and inferior_set_up.GetValueAsSigned() == 1: retry = 0 else: process.Continue() retry = retry - 1 return inferior_set_up # Listen to the process events until we get an event saying that the process is # running. Retry up to five times in case we get other events that are not # what we're looking for. def wait_for_running(self, process, listener): retry_count = 5 if process.GetState() == lldb.eStateRunning: return True while retry_count > 0: event = lldb.SBEvent() listener.WaitForEvent(2, event) if event.GetType() == lldb.SBProcess.eBroadcastBitStateChanged: if process.GetState() == lldb.eStateRunning: return True retry_count = retry_count - 1 return False # Listen to the process events until we get an event saying the process is - # stopped. Retry up to five times in case we get other events that we are + # stopped. Retry up to five times in case we get other events that we are # not looking for. def wait_for_stop(self, process, listener): retry_count = 5 if process.GetState() == lldb.eStateStopped or process.GetState() == lldb.eStateCrashed or process.GetState() == lldb.eStateDetached or process.GetState() == lldb.eStateExited: return True while retry_count > 0: event = lldb.SBEvent() listener.WaitForEvent(2, event) if event.GetType() == lldb.SBProcess.eBroadcastBitStateChanged: if process.GetState() == lldb.eStateStopped or process.GetState() == lldb.eStateCrashed or process.GetState() == lldb.eStateDetached or process.GetState() == lldb.eStateExited: return True if process.GetState() == lldb.eStateCrashed or process.GetState() == lldb.eStateDetached or process.GetState() == lldb.eStateExited: return False retry_count = retry_count - 1 return False def check_number_of_threads(self, process): self.assertTrue( process.GetNumThreads() == 3, "Check that the process has three threads when sitting at the stopper() breakpoint") def check_expected_threads_present(self, main_thread, second_thread, third_thread): self.assertTrue( main_thread.IsValid() and second_thread.IsValid() and third_thread.IsValid(), "Got all three expected threads") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/name_lookup/TestNameLookup.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/name_lookup/TestNameLookup.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/name_lookup/TestNameLookup.py (revision 337147) @@ -1,67 +1,67 @@ """ Test SBTarget APIs. """ from __future__ import print_function import unittest2 import os import time import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestNameLookup(TestBase): mydir = TestBase.compute_mydir(__file__) @add_test_categories(['pyapi']) @expectedFailureAll(oslist=["windows"], bugnumber='llvm.org/pr21765') def test_target(self): """Exercise SBTarget.FindFunctions() with various name masks. - + A previous regression caused mangled names to not be able to be looked up. This test verifies that using a mangled name with eFunctionNameTypeFull works and that using a function basename with eFunctionNameTypeFull works for all C++ functions that are at the global namespace level.""" self.build(); exe = self.getBuildArtifact("a.out") # Create a target by the debugger. target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) exe_module = target.FindModule(target.GetExecutable()) - + c_name_to_symbol = {} cpp_name_to_symbol = {} mangled_to_symbol = {} num_symbols = exe_module.GetNumSymbols(); for i in range(num_symbols): symbol = exe_module.GetSymbolAtIndex(i); name = symbol.GetName() if name and 'unique_function_name' in name and '__PRETTY_FUNCTION__' not in name: mangled = symbol.GetMangledName() if mangled: mangled_to_symbol[mangled] = symbol if name: cpp_name_to_symbol[name] = symbol elif name: c_name_to_symbol[name] = symbol # Make sure each mangled name turns up exactly one match when looking up - # functions by full name and using the mangled name as the name in the + # functions by full name and using the mangled name as the name in the # lookup self.assertGreaterEqual(len(mangled_to_symbol), 6) for mangled in mangled_to_symbol.keys(): symbol_contexts = target.FindFunctions(mangled, lldb.eFunctionNameTypeFull) self.assertTrue(symbol_contexts.GetSize() == 1) for symbol_context in symbol_contexts: self.assertTrue(symbol_context.GetFunction().IsValid()) self.assertTrue(symbol_context.GetSymbol().IsValid()) - - + + Index: vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/sbdata/TestSBData.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/sbdata/TestSBData.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/python_api/sbdata/TestSBData.py (revision 337147) @@ -1,557 +1,557 @@ """Test the SBData APIs.""" from __future__ import print_function from math import fabs import os import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class SBDataAPICase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Find the line number to break on inside main.cpp. self.line = line_number('main.cpp', '// set breakpoint here') @add_test_categories(['pyapi']) def test_byte_order_and_address_byte_size(self): - """Test the SBData::SetData() to ensure the byte order and address + """Test the SBData::SetData() to ensure the byte order and address byte size are obeyed""" addr_data = b'\x11\x22\x33\x44\x55\x66\x77\x88' error = lldb.SBError() data = lldb.SBData() data.SetData(error, addr_data, lldb.eByteOrderBig, 4) addr = data.GetAddress(error, 0) self.assertTrue(addr == 0x11223344); data.SetData(error, addr_data, lldb.eByteOrderBig, 8) addr = data.GetAddress(error, 0) self.assertTrue(addr == 0x1122334455667788); data.SetData(error, addr_data, lldb.eByteOrderLittle, 4) addr = data.GetAddress(error, 0) self.assertTrue(addr == 0x44332211); data.SetData(error, addr_data, lldb.eByteOrderLittle, 8) addr = data.GetAddress(error, 0) self.assertTrue(addr == 0x8877665544332211); @add_test_categories(['pyapi']) def test_with_run_command(self): """Test the SBData APIs.""" self.build() self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) lldbutil.run_break_set_by_file_and_line( self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) self.runCmd("run", RUN_SUCCEEDED) # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) target = self.dbg.GetSelectedTarget() process = target.GetProcess() thread = lldbutil.get_stopped_thread( process, lldb.eStopReasonBreakpoint) self.assertIsNotNone(thread) frame = thread.GetSelectedFrame() if self.TraceOn(): print(frame) foobar = frame.FindVariable('foobar') self.assertTrue(foobar.IsValid()) if self.TraceOn(): print(foobar) data = foobar.GetPointeeData(0, 2) if self.TraceOn(): print(data) offset = 0 error = lldb.SBError() self.assert_data(data.GetUnsignedInt32, offset, 1) offset += 4 low = data.GetSignedInt16(error, offset) self.assertTrue(error.Success()) offset += 2 high = data.GetSignedInt16(error, offset) self.assertTrue(error.Success()) offset += 2 self.assertTrue( (low == 9 and high == 0) or ( low == 0 and high == 9), 'foo[0].b == 9') self.assertTrue( fabs( data.GetFloat( error, offset) - 3.14) < 1, 'foo[0].c == 3.14') self.assertTrue(error.Success()) offset += 4 self.assert_data(data.GetUnsignedInt32, offset, 8) offset += 4 self.assert_data(data.GetUnsignedInt32, offset, 5) offset += 4 self.runCmd("n") offset = 16 self.assert_data(data.GetUnsignedInt32, offset, 5) data = foobar.GetPointeeData(1, 1) offset = 0 self.assert_data(data.GetSignedInt32, offset, 8) offset += 4 self.assert_data(data.GetSignedInt32, offset, 7) offset += 8 self.assertTrue( data.GetUnsignedInt32( error, offset) == 0, 'do not read beyond end') self.assertTrue(not error.Success()) error.Clear() # clear the error for the next test star_foobar = foobar.Dereference() self.assertTrue(star_foobar.IsValid()) data = star_foobar.GetData() if self.TraceOn(): print(data) offset = 0 self.assert_data(data.GetUnsignedInt32, offset, 1) offset += 4 self.assert_data(data.GetUnsignedInt32, offset, 9) foobar_addr = star_foobar.GetLoadAddress() foobar_addr += 12 # http://llvm.org/bugs/show_bug.cgi?id=11579 # lldb::SBValue::CreateValueFromAddress does not verify SBType::GetPointerType succeeds # This should not crash LLDB. nothing = foobar.CreateValueFromAddress( "nothing", foobar_addr, star_foobar.GetType().GetBasicType( lldb.eBasicTypeInvalid)) new_foobar = foobar.CreateValueFromAddress( "f00", foobar_addr, star_foobar.GetType()) self.assertTrue(new_foobar.IsValid()) if self.TraceOn(): print(new_foobar) data = new_foobar.GetData() if self.TraceOn(): print(data) self.assertTrue(data.uint32[0] == 8, 'then foo[1].a == 8') self.assertTrue(data.uint32[1] == 7, 'then foo[1].b == 7') # exploiting that sizeof(uint32) == sizeof(float) self.assertTrue(fabs(data.float[2] - 3.14) < 1, 'foo[1].c == 3.14') self.runCmd("n") offset = 0 self.assert_data(data.GetUnsignedInt32, offset, 8) offset += 4 self.assert_data(data.GetUnsignedInt32, offset, 7) offset += 4 self.assertTrue( fabs( data.GetFloat( error, offset) - 3.14) < 1, 'foo[1].c == 3.14') self.assertTrue(error.Success()) data = new_foobar.GetData() if self.TraceOn(): print(data) offset = 0 self.assert_data(data.GetUnsignedInt32, offset, 8) offset += 4 self.assert_data(data.GetUnsignedInt32, offset, 7) offset += 4 self.assertTrue( fabs( data.GetFloat( error, offset) - 6.28) < 1, 'foo[1].c == 6.28') self.assertTrue(error.Success()) self.runCmd("n") barfoo = frame.FindVariable('barfoo') data = barfoo.GetData() if self.TraceOn(): print(barfoo) if self.TraceOn(): print(data) offset = 0 self.assert_data(data.GetUnsignedInt32, offset, 1) offset += 4 self.assert_data(data.GetUnsignedInt32, offset, 2) offset += 4 self.assertTrue( fabs( data.GetFloat( error, offset) - 3) < 1, 'barfoo[0].c == 3') self.assertTrue(error.Success()) offset += 4 self.assert_data(data.GetUnsignedInt32, offset, 4) offset += 4 self.assert_data(data.GetUnsignedInt32, offset, 5) offset += 4 self.assertTrue( fabs( data.GetFloat( error, offset) - 6) < 1, 'barfoo[1].c == 6') self.assertTrue(error.Success()) new_object = barfoo.CreateValueFromData( "new_object", data, barfoo.GetType().GetBasicType( lldb.eBasicTypeInt)) if self.TraceOn(): print(new_object) self.assertTrue(new_object.GetValue() == "1", 'new_object == 1') if data.GetByteOrder() == lldb.eByteOrderBig: data.SetData( error, '\0\0\0A', data.GetByteOrder(), data.GetAddressByteSize()) else: data.SetData( error, 'A\0\0\0', data.GetByteOrder(), data.GetAddressByteSize()) self.assertTrue(error.Success()) data2 = lldb.SBData() data2.SetData( error, 'BCD', data.GetByteOrder(), data.GetAddressByteSize()) self.assertTrue(error.Success()) data.Append(data2) if self.TraceOn(): print(data) # this breaks on EBCDIC offset = 0 self.assert_data(data.GetUnsignedInt32, offset, 65) offset += 4 self.assert_data(data.GetUnsignedInt8, offset, 66) offset += 1 self.assert_data(data.GetUnsignedInt8, offset, 67) offset += 1 self.assert_data(data.GetUnsignedInt8, offset, 68) offset += 1 # check the new API calls introduced per LLVM llvm.org/prenhancement request # 11619 (Allow creating SBData values from arrays or primitives in # Python) hello_str = "hello!" data2 = lldb.SBData.CreateDataFromCString( process.GetByteOrder(), process.GetAddressByteSize(), hello_str) self.assertTrue(len(data2.uint8) == len(hello_str)) self.assertTrue(data2.uint8[0] == 104, 'h == 104') self.assertTrue(data2.uint8[1] == 101, 'e == 101') self.assertTrue(data2.uint8[2] == 108, 'l == 108') self.assert_data(data2.GetUnsignedInt8, 3, 108) # l self.assertTrue(data2.uint8[4] == 111, 'o == 111') self.assert_data(data2.GetUnsignedInt8, 5, 33) # ! uint_lists = [[1, 2, 3, 4, 5], [int(i) for i in [1, 2, 3, 4, 5]]] int_lists = [[2, -2], [int(i) for i in [2, -2]]] for l in uint_lists: data2 = lldb.SBData.CreateDataFromUInt64Array( process.GetByteOrder(), process.GetAddressByteSize(), l) self.assert_data(data2.GetUnsignedInt64, 0, 1) self.assert_data(data2.GetUnsignedInt64, 8, 2) self.assert_data(data2.GetUnsignedInt64, 16, 3) self.assert_data(data2.GetUnsignedInt64, 24, 4) self.assert_data(data2.GetUnsignedInt64, 32, 5) self.assertTrue( data2.uint64s == [ 1, 2, 3, 4, 5], 'read_data_helper failure: data2 == [1,2,3,4,5]') for l in int_lists: data2 = lldb.SBData.CreateDataFromSInt32Array( process.GetByteOrder(), process.GetAddressByteSize(), l) self.assertTrue( data2.sint32[ 0:2] == [ 2, -2], 'signed32 data2 = [2,-2]') data2.Append( lldb.SBData.CreateDataFromSInt64Array( process.GetByteOrder(), process.GetAddressByteSize(), int_lists[0])) self.assert_data(data2.GetSignedInt32, 0, 2) self.assert_data(data2.GetSignedInt32, 4, -2) self.assertTrue( data2.sint64[ 1:3] == [ 2, -2], 'signed64 data2 = [2,-2]') for l in int_lists: data2 = lldb.SBData.CreateDataFromSInt64Array( process.GetByteOrder(), process.GetAddressByteSize(), l) self.assert_data(data2.GetSignedInt64, 0, 2) self.assert_data(data2.GetSignedInt64, 8, -2) self.assertTrue( data2.sint64[ 0:2] == [ 2, -2], 'signed64 data2 = [2,-2]') for l in uint_lists: data2 = lldb.SBData.CreateDataFromUInt32Array( process.GetByteOrder(), process.GetAddressByteSize(), l) self.assert_data(data2.GetUnsignedInt32, 0, 1) self.assert_data(data2.GetUnsignedInt32, 4, 2) self.assert_data(data2.GetUnsignedInt32, 8, 3) self.assert_data(data2.GetUnsignedInt32, 12, 4) self.assert_data(data2.GetUnsignedInt32, 16, 5) bool_list = [True, True, False, False, True, False] data2 = lldb.SBData.CreateDataFromSInt32Array( process.GetByteOrder(), process.GetAddressByteSize(), bool_list) self.assertTrue( data2.sint32[ 0:6] == [ 1, 1, 0, 0, 1, 0], 'signed32 data2 = [1, 1, 0, 0, 1, 0]') data2 = lldb.SBData.CreateDataFromUInt32Array( process.GetByteOrder(), process.GetAddressByteSize(), bool_list) self.assertTrue( data2.uint32[ 0:6] == [ 1, 1, 0, 0, 1, 0], 'unsigned32 data2 = [1, 1, 0, 0, 1, 0]') data2 = lldb.SBData.CreateDataFromSInt64Array( process.GetByteOrder(), process.GetAddressByteSize(), bool_list) self.assertTrue( data2.sint64[ 0:6] == [ 1, 1, 0, 0, 1, 0], 'signed64 data2 = [1, 1, 0, 0, 1, 0]') data2 = lldb.SBData.CreateDataFromUInt64Array( process.GetByteOrder(), process.GetAddressByteSize(), bool_list) self.assertTrue( data2.uint64[ 0:6] == [ 1, 1, 0, 0, 1, 0], 'signed64 data2 = [1, 1, 0, 0, 1, 0]') data2 = lldb.SBData.CreateDataFromDoubleArray( process.GetByteOrder(), process.GetAddressByteSize(), [ 3.14, 6.28, 2.71]) self.assertTrue( fabs( data2.GetDouble( error, 0) - 3.14) < 0.5, 'double data2[0] = 3.14') self.assertTrue(error.Success()) self.assertTrue( fabs( data2.GetDouble( error, 8) - 6.28) < 0.5, 'double data2[1] = 6.28') self.assertTrue(error.Success()) self.assertTrue( fabs( data2.GetDouble( error, 16) - 2.71) < 0.5, 'double data2[2] = 2.71') self.assertTrue(error.Success()) data2 = lldb.SBData() data2.SetDataFromCString(hello_str) self.assertTrue(len(data2.uint8) == len(hello_str)) self.assert_data(data2.GetUnsignedInt8, 0, 104) self.assert_data(data2.GetUnsignedInt8, 1, 101) self.assert_data(data2.GetUnsignedInt8, 2, 108) self.assert_data(data2.GetUnsignedInt8, 3, 108) self.assert_data(data2.GetUnsignedInt8, 4, 111) self.assert_data(data2.GetUnsignedInt8, 5, 33) data2.SetDataFromUInt64Array([1, 2, 3, 4, 5]) self.assert_data(data2.GetUnsignedInt64, 0, 1) self.assert_data(data2.GetUnsignedInt64, 8, 2) self.assert_data(data2.GetUnsignedInt64, 16, 3) self.assert_data(data2.GetUnsignedInt64, 24, 4) self.assert_data(data2.GetUnsignedInt64, 32, 5) self.assertTrue( data2.uint64[0] == 1, 'read_data_helper failure: set data2[0] = 1') self.assertTrue( data2.uint64[1] == 2, 'read_data_helper failure: set data2[1] = 2') self.assertTrue( data2.uint64[2] == 3, 'read_data_helper failure: set data2[2] = 3') self.assertTrue( data2.uint64[3] == 4, 'read_data_helper failure: set data2[3] = 4') self.assertTrue( data2.uint64[4] == 5, 'read_data_helper failure: set data2[4] = 5') self.assertTrue( data2.uint64[ 0:2] == [ 1, 2], 'read_data_helper failure: set data2[0:2] = [1,2]') data2.SetDataFromSInt32Array([2, -2]) self.assert_data(data2.GetSignedInt32, 0, 2) self.assert_data(data2.GetSignedInt32, 4, -2) data2.SetDataFromSInt64Array([2, -2]) self.assert_data(data2.GetSignedInt64, 0, 2) self.assert_data(data2.GetSignedInt64, 8, -2) data2.SetDataFromUInt32Array([1, 2, 3, 4, 5]) self.assert_data(data2.GetUnsignedInt32, 0, 1) self.assert_data(data2.GetUnsignedInt32, 4, 2) self.assert_data(data2.GetUnsignedInt32, 8, 3) self.assert_data(data2.GetUnsignedInt32, 12, 4) self.assert_data(data2.GetUnsignedInt32, 16, 5) self.assertTrue( data2.uint32[0] == 1, 'read_data_helper failure: set 32-bit data2[0] = 1') self.assertTrue( data2.uint32[1] == 2, 'read_data_helper failure: set 32-bit data2[1] = 2') self.assertTrue( data2.uint32[2] == 3, 'read_data_helper failure: set 32-bit data2[2] = 3') self.assertTrue( data2.uint32[3] == 4, 'read_data_helper failure: set 32-bit data2[3] = 4') self.assertTrue( data2.uint32[4] == 5, 'read_data_helper failure: set 32-bit data2[4] = 5') data2.SetDataFromDoubleArray([3.14, 6.28, 2.71]) self.assertTrue(fabs(data2.GetDouble(error, 0) - 3.14) < 0.5, 'set double data2[0] = 3.14') self.assertTrue(fabs(data2.GetDouble(error, 8) - 6.28) < 0.5, 'set double data2[1] = 6.28') self.assertTrue(fabs(data2.GetDouble(error, 16) - 2.71) < 0.5, 'set double data2[2] = 2.71') self.assertTrue( fabs( data2.double[0] - 3.14) < 0.5, 'read_data_helper failure: set double data2[0] = 3.14') self.assertTrue( fabs( data2.double[1] - 6.28) < 0.5, 'read_data_helper failure: set double data2[1] = 6.28') self.assertTrue( fabs( data2.double[2] - 2.71) < 0.5, 'read_data_helper failure: set double data2[2] = 2.71') def assert_data(self, func, arg, expected): """ Asserts func(SBError error, arg) == expected. """ error = lldb.SBError() result = func(error, arg) if not error.Success(): stream = lldb.SBStream() error.GetDescription(stream) self.assertTrue( error.Success(), "%s(error, %s) did not succeed: %s" % (func.__name__, arg, stream.GetData())) self.assertTrue( expected == result, "%s(error, %s) == %s != %s" % (func.__name__, arg, result, expected)) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/sample_test/TestSampleTest.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/sample_test/TestSampleTest.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/sample_test/TestSampleTest.py (revision 337147) @@ -1,50 +1,50 @@ """ Describe the purpose of the test class here. """ from __future__ import print_function import os import time import re import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.lldbtest import * class RenameThisSampleTestTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) - # If your test case doesn't stress debug info, the + # If your test case doesn't stress debug info, the # set this to true. That way it won't be run once for # each debug info format. NO_DEBUG_INFO_TESTCASE = True def test_sample_rename_this(self): """There can be many tests in a test case - describe this test here.""" self.build() self.main_source_file = lldb.SBFileSpec("main.c") self.sample_test() def setUp(self): # Call super's setUp(). TestBase.setUp(self) def sample_test(self): """You might use the test implementation in several ways, say so here.""" # This function starts a process, "a.out" by default, sets a source # breakpoint, runs to it, and returns the thread, process & target. # It optionally takes an SBLaunchOption argument if you want to pass # arguments or environment variables. (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, - "Set a breakpoint here", self.main_source_file) + "Set a breakpoint here", self.main_source_file) frame = thread.GetFrameAtIndex(0) test_var = frame.FindVariable("test_var") self.assertTrue(test_var.GetError().Success(), "Failed to fetch test_var") test_value = test_var.GetValueAsUnsigned() self.assertEqual(test_value, 10, "Got the right value for test_var") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/settings/TestSettings.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/settings/TestSettings.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/settings/TestSettings.py (revision 337147) @@ -1,564 +1,564 @@ """ Test lldb settings command. """ from __future__ import print_function import os import time import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class SettingsCommandTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) NO_DEBUG_INFO_TESTCASE = True def test_apropos_should_also_search_settings_description(self): """Test that 'apropos' command should also search descriptions for the settings variables.""" self.expect("apropos 'environment variable'", substrs=["target.env-vars", "environment variables", "executable's environment"]) def test_append_target_env_vars(self): """Test that 'append target.run-args' works.""" # Append the env-vars. self.runCmd('settings append target.env-vars MY_ENV_VAR=YES') # And add hooks to restore the settings during tearDown(). self.addTearDownHook( lambda: self.runCmd("settings clear target.env-vars")) # Check it immediately! self.expect('settings show target.env-vars', substrs=['MY_ENV_VAR=YES']) def test_insert_before_and_after_target_run_args(self): """Test that 'insert-before/after target.run-args' works.""" # Set the run-args first. self.runCmd('settings set target.run-args a b c') # And add hooks to restore the settings during tearDown(). self.addTearDownHook( lambda: self.runCmd("settings clear target.run-args")) # Now insert-before the index-0 element with '__a__'. self.runCmd('settings insert-before target.run-args 0 __a__') # And insert-after the index-1 element with '__A__'. self.runCmd('settings insert-after target.run-args 1 __A__') # Check it immediately! self.expect('settings show target.run-args', substrs=['target.run-args', '[0]: "__a__"', '[1]: "a"', '[2]: "__A__"', '[3]: "b"', '[4]: "c"']) def test_replace_target_run_args(self): """Test that 'replace target.run-args' works.""" # Set the run-args and then replace the index-0 element. self.runCmd('settings set target.run-args a b c') # And add hooks to restore the settings during tearDown(). self.addTearDownHook( lambda: self.runCmd("settings clear target.run-args")) # Now replace the index-0 element with 'A', instead. self.runCmd('settings replace target.run-args 0 A') # Check it immediately! self.expect('settings show target.run-args', substrs=['target.run-args (arguments) =', '[0]: "A"', '[1]: "b"', '[2]: "c"']) def test_set_prompt(self): """Test that 'set prompt' actually changes the prompt.""" # Set prompt to 'lldb2'. self.runCmd("settings set prompt 'lldb2 '") # Immediately test the setting. self.expect("settings show prompt", SETTING_MSG("prompt"), startstr='prompt (string) = "lldb2 "') # The overall display should also reflect the new setting. self.expect("settings show", SETTING_MSG("prompt"), substrs=['prompt (string) = "lldb2 "']) # Use '-r' option to reset to the original default prompt. self.runCmd("settings clear prompt") def test_set_term_width(self): """Test that 'set term-width' actually changes the term-width.""" self.runCmd("settings set term-width 70") # Immediately test the setting. self.expect("settings show term-width", SETTING_MSG("term-width"), startstr="term-width (int) = 70") # The overall display should also reflect the new setting. self.expect("settings show", SETTING_MSG("term-width"), substrs=["term-width (int) = 70"]) # rdar://problem/10712130 def test_set_frame_format(self): """Test that 'set frame-format' with a backtick char in the format string works as well as fullpath.""" self.build() exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) def cleanup(): self.runCmd( "settings set frame-format %s" % self.format_string, check=False) # Execute the cleanup function during test case tear down. self.addTearDownHook(cleanup) self.runCmd("settings show frame-format") m = re.match( '^frame-format \(format-string\) = "(.*)\"$', self.res.GetOutput()) self.assertTrue(m, "Bad settings string") self.format_string = m.group(1) # Change the default format to print function.name rather than # function.name-with-args format_string = "frame #${frame.index}: ${frame.pc}{ ${module.file.basename}`${function.name}{${function.pc-offset}}}{ at ${line.file.fullpath}:${line.number}}{, lang=${language}}\n" self.runCmd("settings set frame-format %s" % format_string) # Immediately test the setting. self.expect("settings show frame-format", SETTING_MSG("frame-format"), substrs=[format_string]) self.runCmd("breakpoint set -n main") self.runCmd("process launch --working-dir '{0}'".format(self.get_process_working_directory()), RUN_SUCCEEDED) self.expect("thread backtrace", substrs=["`main", self.getSourceDir()]) def test_set_auto_confirm(self): """Test that after 'set auto-confirm true', manual confirmation should not kick in.""" self.build() exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) self.runCmd("settings set auto-confirm true") # Immediately test the setting. self.expect("settings show auto-confirm", SETTING_MSG("auto-confirm"), startstr="auto-confirm (boolean) = true") # Now 'breakpoint delete' should just work fine without confirmation # prompt from the command interpreter. self.runCmd("breakpoint set -n main") self.expect("breakpoint delete", startstr="All breakpoints removed") # Restore the original setting of auto-confirm. self.runCmd("settings clear auto-confirm") self.expect("settings show auto-confirm", SETTING_MSG("auto-confirm"), startstr="auto-confirm (boolean) = false") @skipIf(archs=no_match(['x86_64', 'i386', 'i686'])) def test_disassembler_settings(self): """Test that user options for the disassembler take effect.""" self.build() exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # AT&T syntax self.runCmd("settings set target.x86-disassembly-flavor att") self.runCmd("settings set target.use-hex-immediates false") self.expect("disassemble -n numberfn", substrs=["$90"]) self.runCmd("settings set target.use-hex-immediates true") self.runCmd("settings set target.hex-immediate-style c") self.expect("disassemble -n numberfn", substrs=["$0x5a"]) self.runCmd("settings set target.hex-immediate-style asm") self.expect("disassemble -n numberfn", substrs=["$5ah"]) # Intel syntax self.runCmd("settings set target.x86-disassembly-flavor intel") self.runCmd("settings set target.use-hex-immediates false") self.expect("disassemble -n numberfn", substrs=["90"]) self.runCmd("settings set target.use-hex-immediates true") self.runCmd("settings set target.hex-immediate-style c") self.expect("disassemble -n numberfn", substrs=["0x5a"]) self.runCmd("settings set target.hex-immediate-style asm") self.expect("disassemble -n numberfn", substrs=["5ah"]) @skipIfDarwinEmbedded # debugserver on ios etc can't write files def test_run_args_and_env_vars(self): """Test that run-args and env-vars are passed to the launched process.""" self.build() exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # Set the run-args and the env-vars. # And add hooks to restore the settings during tearDown(). self.runCmd('settings set target.run-args A B C') self.addTearDownHook( lambda: self.runCmd("settings clear target.run-args")) self.runCmd('settings set target.env-vars ["MY_ENV_VAR"]=YES') self.addTearDownHook( lambda: self.runCmd("settings clear target.env-vars")) self.runCmd("process launch --working-dir '{0}'".format(self.get_process_working_directory()), RUN_SUCCEEDED) # Read the output file produced by running the program. output = lldbutil.read_file_from_process_wd(self, "output2.txt") self.expect( output, exe=False, substrs=[ "argv[1] matches", "argv[2] matches", "argv[3] matches", "Environment variable 'MY_ENV_VAR' successfully passed."]) @skipIfRemote # it doesn't make sense to send host env to remote target def test_pass_host_env_vars(self): """Test that the host env vars are passed to the launched process.""" self.build() exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # By default, inherit-env is 'true'. self.expect( 'settings show target.inherit-env', "Default inherit-env is 'true'", startstr="target.inherit-env (boolean) = true") # Set some host environment variables now. os.environ["MY_HOST_ENV_VAR1"] = "VAR1" os.environ["MY_HOST_ENV_VAR2"] = "VAR2" # This is the function to unset the two env variables set above. def unset_env_variables(): os.environ.pop("MY_HOST_ENV_VAR1") os.environ.pop("MY_HOST_ENV_VAR2") self.addTearDownHook(unset_env_variables) self.runCmd("process launch --working-dir '{0}'".format(self.get_process_working_directory()), RUN_SUCCEEDED) # Read the output file produced by running the program. output = lldbutil.read_file_from_process_wd(self, "output1.txt") self.expect( output, exe=False, substrs=[ "The host environment variable 'MY_HOST_ENV_VAR1' successfully passed.", "The host environment variable 'MY_HOST_ENV_VAR2' successfully passed."]) @skipIfDarwinEmbedded # debugserver on ios etc can't write files def test_set_error_output_path(self): """Test that setting target.error/output-path for the launched process works.""" self.build() exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # Set the error-path and output-path and verify both are set. self.runCmd("settings set target.error-path '{0}'".format( lldbutil.append_to_process_working_directory(self, "stderr.txt"))) self.runCmd("settings set target.output-path '{0}".format( lldbutil.append_to_process_working_directory(self, "stdout.txt"))) # And add hooks to restore the original settings during tearDown(). self.addTearDownHook( lambda: self.runCmd("settings clear target.output-path")) self.addTearDownHook( lambda: self.runCmd("settings clear target.error-path")) self.expect("settings show target.error-path", SETTING_MSG("target.error-path"), substrs=['target.error-path (file)', 'stderr.txt"']) self.expect("settings show target.output-path", SETTING_MSG("target.output-path"), substrs=['target.output-path (file)', 'stdout.txt"']) self.runCmd("process launch --working-dir '{0}'".format(self.get_process_working_directory()), RUN_SUCCEEDED) output = lldbutil.read_file_from_process_wd(self, "stderr.txt") message = "This message should go to standard error." if lldbplatformutil.hasChattyStderr(self): self.expect(output, exe=False, substrs=[message]) else: self.expect(output, exe=False, startstr=message) output = lldbutil.read_file_from_process_wd(self, "stdout.txt") self.expect(output, exe=False, startstr="This message should go to standard out.") def test_print_dictionary_setting(self): self.runCmd("settings clear target.env-vars") self.runCmd("settings set target.env-vars [\"MY_VAR\"]=some-value") self.expect("settings show target.env-vars", substrs=["MY_VAR=some-value"]) self.runCmd("settings clear target.env-vars") def test_print_array_setting(self): self.runCmd("settings clear target.run-args") self.runCmd("settings set target.run-args gobbledy-gook") self.expect("settings show target.run-args", substrs=['[0]: "gobbledy-gook"']) self.runCmd("settings clear target.run-args") def test_settings_with_quotes(self): self.runCmd("settings clear target.run-args") self.runCmd("settings set target.run-args a b c") self.expect("settings show target.run-args", substrs=['[0]: "a"', '[1]: "b"', '[2]: "c"']) self.runCmd("settings set target.run-args 'a b c'") self.expect("settings show target.run-args", substrs=['[0]: "a b c"']) self.runCmd("settings clear target.run-args") self.runCmd("settings clear target.env-vars") self.runCmd( 'settings set target.env-vars ["MY_FILE"]="this is a file name with spaces.txt"') self.expect("settings show target.env-vars", substrs=['MY_FILE=this is a file name with spaces.txt']) self.runCmd("settings clear target.env-vars") # Test and make sure that setting "format-string" settings obeys quotes # if they are provided self.runCmd("settings set thread-format 'abc def' ") self.expect("settings show thread-format", 'thread-format (format-string) = "abc def"') self.runCmd('settings set thread-format "abc def" ') self.expect("settings show thread-format", 'thread-format (format-string) = "abc def"') # Make sure when no quotes are provided that we maintain any trailing # spaces self.runCmd('settings set thread-format abc def ') self.expect("settings show thread-format", 'thread-format (format-string) = "abc def "') self.runCmd('settings clear thread-format') def test_settings_with_trailing_whitespace(self): # boolean # Set to known value self.runCmd("settings set target.skip-prologue true") # Set to new value with trailing whitespace self.runCmd("settings set target.skip-prologue false ") # Make sure the setting was correctly set to "false" self.expect( "settings show target.skip-prologue", SETTING_MSG("target.skip-prologue"), startstr="target.skip-prologue (boolean) = false") self.runCmd("settings clear target.skip-prologue", check=False) # integer self.runCmd("settings set term-width 70") # Set to known value # Set to new value with trailing whitespaces self.runCmd("settings set term-width 60 \t") self.expect("settings show term-width", SETTING_MSG("term-width"), startstr="term-width (int) = 60") self.runCmd("settings clear term-width", check=False) # string self.runCmd("settings set target.arg0 abc") # Set to known value # Set to new value with trailing whitespaces self.runCmd("settings set target.arg0 cde\t ") self.expect("settings show target.arg0", SETTING_MSG("target.arg0"), startstr='target.arg0 (string) = "cde"') self.runCmd("settings clear target.arg0", check=False) # file path1 = self.getBuildArtifact("path1.txt") path2 = self.getBuildArtifact("path2.txt") self.runCmd( "settings set target.output-path %s" % path1) # Set to known value self.expect( "settings show target.output-path", SETTING_MSG("target.output-path"), startstr='target.output-path (file) = ', substrs=[path1]) self.runCmd("settings set target.output-path %s " % path2) # Set to new value with trailing whitespaces self.expect( "settings show target.output-path", SETTING_MSG("target.output-path"), startstr='target.output-path (file) = ', substrs=[path2]) self.runCmd("settings clear target.output-path", check=False) # enum # Set to known value self.runCmd("settings set stop-disassembly-display never") # Set to new value with trailing whitespaces self.runCmd("settings set stop-disassembly-display always ") self.expect( "settings show stop-disassembly-display", SETTING_MSG("stop-disassembly-display"), startstr='stop-disassembly-display (enum) = always') self.runCmd("settings clear stop-disassembly-display", check=False) # language # Set to known value self.runCmd("settings set target.language c89") # Set to new value with trailing whitespace self.runCmd("settings set target.language go ") self.expect( "settings show target.language", SETTING_MSG("target.language"), startstr="target.language (language) = go") self.runCmd("settings clear target.language", check=False) # arguments self.runCmd("settings set target.run-args 1 2 3") # Set to known value # Set to new value with trailing whitespaces self.runCmd("settings set target.run-args 3 4 5 ") self.expect( "settings show target.run-args", SETTING_MSG("target.run-args"), substrs=[ 'target.run-args (arguments) =', '[0]: "3"', '[1]: "4"', '[2]: "5"']) self.runCmd("settings set target.run-args 1 2 3") # Set to known value # Set to new value with trailing whitespaces self.runCmd("settings set target.run-args 3 \ \ ") self.expect( "settings show target.run-args", SETTING_MSG("target.run-args"), substrs=[ 'target.run-args (arguments) =', '[0]: "3"', '[1]: " "', '[2]: " "']) self.runCmd("settings clear target.run-args", check=False) # dictionaries self.runCmd("settings clear target.env-vars") # Set to known value # Set to new value with trailing whitespaces self.runCmd("settings set target.env-vars A=B C=D\t ") self.expect( "settings show target.env-vars", SETTING_MSG("target.env-vars"), substrs=[ 'target.env-vars (dictionary of strings) =', 'A=B', 'C=D']) self.runCmd("settings clear target.env-vars", check=False) # regex # Set to known value self.runCmd("settings clear target.process.thread.step-avoid-regexp") # Set to new value with trailing whitespaces self.runCmd( "settings set target.process.thread.step-avoid-regexp foo\\ ") self.expect( "settings show target.process.thread.step-avoid-regexp", SETTING_MSG("target.process.thread.step-avoid-regexp"), substrs=['target.process.thread.step-avoid-regexp (regex) = foo\\ ']) self.runCmd( "settings clear target.process.thread.step-avoid-regexp", check=False) # format-string self.runCmd("settings clear disassembly-format") # Set to known value # Set to new value with trailing whitespaces self.runCmd("settings set disassembly-format foo ") self.expect("settings show disassembly-format", SETTING_MSG("disassembly-format"), substrs=['disassembly-format (format-string) = "foo "']) self.runCmd("settings clear disassembly-format", check=False) def test_all_settings_exist(self): self.expect("settings show", substrs=["auto-confirm", "frame-format", "notify-void", "prompt", "script-lang", "stop-disassembly-count", "stop-disassembly-display", "stop-line-count-after", "stop-line-count-before", "stop-show-column", "term-width", "thread-format", "use-external-editor", "target.default-arch", "target.move-to-nearest-code", "target.expr-prefix", "target.language", "target.prefer-dynamic-value", "target.enable-synthetic-value", "target.skip-prologue", "target.source-map", "target.exec-search-paths", "target.max-children-count", "target.max-string-summary-length", "target.breakpoints-use-platform-avoid-list", "target.run-args", "target.env-vars", "target.inherit-env", "target.input-path", "target.output-path", "target.error-path", "target.disable-aslr", "target.disable-stdio", "target.x86-disassembly-flavor", "target.use-hex-immediates", "target.hex-immediate-style", "target.process.disable-memory-cache", "target.process.extra-startup-command", "target.process.thread.step-avoid-regexp", "target.process.thread.trace-thread"]) # settings under an ".experimental" domain should have two properties: # 1. If the name does not exist with "experimental" in the name path, # the name lookup should try to find it without "experimental". So - # a previously-experimental setting that has been promoted to a + # a previously-experimental setting that has been promoted to a # "real" setting will still be set by the original name. # 2. Changing a setting with .experimental., name, where the setting # does not exist either with ".experimental." or without, should # not generate an error. So if an experimental setting is removed, # people who may have that in their ~/.lldbinit files should not see # any errors. def test_experimental_settings(self): cmdinterp = self.dbg.GetCommandInterpreter() result = lldb.SBCommandReturnObject() # Set target.arg0 to a known value, check that we can retrieve it via # the actual name and via .experimental. self.expect('settings set target.arg0 first-value') self.expect('settings show target.arg0', substrs=['first-value']) self.expect('settings show target.experimental.arg0', substrs=['first-value'], error=False) # Set target.arg0 to a new value via a target.experimental.arg0 name, # verify that we can read it back via both .experimental., and not. self.expect('settings set target.experimental.arg0 second-value', error=False) self.expect('settings show target.arg0', substrs=['second-value']) self.expect('settings show target.experimental.arg0', substrs=['second-value'], error=False) # showing & setting an undefined .experimental. setting should generate no errors. self.expect('settings show target.experimental.setting-which-does-not-exist', patterns=['^\s$'], error=False) self.expect('settings set target.experimental.setting-which-does-not-exist true', error=False) # A domain component before .experimental. which does not exist should give an error # But the code does not yet do that. # self.expect('settings set target.setting-which-does-not-exist.experimental.arg0 true', error=True) # finally, confirm that trying to set a setting that does not exist still fails. # (SHOWING a setting that does not exist does not currently yield an error.) self.expect('settings set target.setting-which-does-not-exist true', error=True) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/TestMiStartupOptions.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/TestMiStartupOptions.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/startup_options/TestMiStartupOptions.py (revision 337147) @@ -1,325 +1,325 @@ """ Test lldb-mi startup options. """ from __future__ import print_function import lldbmi_testcase from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil import os class MiStartupOptionsTestCase(lldbmi_testcase.MiTestCaseBase): mydir = TestBase.compute_mydir(__file__) @skipIfRemote # We do not currently support remote debugging via the MI. @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @skipIfDarwin def test_lldbmi_executable_option_file(self): """Test that 'lldb-mi --interpreter %s' loads executable file.""" self.spawnLldbMi(args="%s" % self.myexe) # Test that the executable is loaded when file was specified self.expect("-file-exec-and-symbols \"%s\"" % self.myexe) self.expect("\^done") # Test that lldb-mi is ready when executable was loaded self.expect(self.child_prompt, exactly=True) # Run to main self.runCmd("-break-insert -f main") self.expect("\^done,bkpt={number=\"1\"") self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Continue self.runCmd("-exec-continue") self.expect("\^running") self.expect("\*stopped,reason=\"exited-normally\"") @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @skipIfDarwin def test_lldbmi_executable_option_unknown_file(self): """Test that 'lldb-mi --interpreter %s' fails on unknown executable file.""" # Prepare path to executable path = "unknown_file" self.spawnLldbMi(args="%s" % path) # Test that the executable isn't loaded when unknown file was specified self.expect("-file-exec-and-symbols \"%s\"" % path) self.expect( "\^error,msg=\"Command 'file-exec-and-symbols'. Target binary '%s' is invalid. error: unable to find executable for '%s'\"" % (path, path)) # Test that lldb-mi is ready when executable was loaded self.expect(self.child_prompt, exactly=True) @skipIfRemote # We do not currently support remote debugging via the MI. @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @skipIfDarwin def test_lldbmi_executable_option_absolute_path(self): """Test that 'lldb-mi --interpreter %s' loads executable which is specified via absolute path.""" # Prepare path to executable self.spawnLldbMi(args="%s" % self.myexe) # Test that the executable is loaded when file was specified using # absolute path self.expect("-file-exec-and-symbols \"%s\"" % self.myexe) self.expect("\^done") # Test that lldb-mi is ready when executable was loaded self.expect(self.child_prompt, exactly=True) # Run self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"exited-normally\"") @skipIfRemote # We do not currently support remote debugging via the MI. @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @skipIfDarwin def test_lldbmi_executable_option_relative_path(self): """Test that 'lldb-mi --interpreter %s' loads executable which is specified via relative path.""" # Prepare path to executable path = os.path.relpath(self.myexe, self.getBuildDir()) self.spawnLldbMi(args="%s" % path) # Test that the executable is loaded when file was specified using # relative path self.expect("-file-exec-and-symbols \"%s\"" % path) self.expect("\^done") # Test that lldb-mi is ready when executable was loaded self.expect(self.child_prompt, exactly=True) # Run self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"exited-normally\"") @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @skipIfDarwin def test_lldbmi_executable_option_unknown_path(self): """Test that 'lldb-mi --interpreter %s' fails on executable file which is specified via unknown path.""" # Prepare path to executable path = "unknown_dir" + self.myexe self.spawnLldbMi(args="%s" % path) # Test that the executable isn't loaded when file was specified using # unknown path self.expect("-file-exec-and-symbols \"%s\"" % path) self.expect( "\^error,msg=\"Command 'file-exec-and-symbols'. Target binary '%s' is invalid. error: unable to find executable for '%s'\"" % (path, path)) # Test that lldb-mi is ready when executable was loaded self.expect(self.child_prompt, exactly=True) def copyScript(self, sourceFile): """copy the script to builddir and replace a.out with the full path""" destFile = os.path.join(os.path.dirname(self.myexe), sourceFile+'.script') with open(sourceFile, 'r') as src: with open(destFile, 'w+') as dest: dest.write(src.read().replace("a.out", self.myexe)) return destFile - + @skipIfRemote # We do not currently support remote debugging via the MI. @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots @skipIfDarwin def test_lldbmi_source_option_start_script(self): """Test that 'lldb-mi --interpreter' can execute user's commands after initial commands were executed.""" # Prepared source file sourceFile = self.copyScript("start_script") self.spawnLldbMi(args="--source %s" % sourceFile) # After '-file-exec-and-symbols a.out' self.expect("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # After '-break-insert -f main' self.expect("-break-insert -f main") self.expect("\^done,bkpt={number=\"1\"") # After '-exec-run' self.expect("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # After '-break-insert main.cpp:BP_return' line = line_number('main.cpp', '//BP_return') self.expect("-break-insert main.cpp:%d" % line) self.expect("\^done,bkpt={number=\"2\"") # After '-exec-continue' self.expect("-exec-continue") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Test that lldb-mi is ready after execution of --source start_script self.expect(self.child_prompt, exactly=True) # Try to evaluate 'a' expression self.runCmd("-data-evaluate-expression a") self.expect("\^done,value=\"10\"") self.expect(self.child_prompt, exactly=True) os.unlink(sourceFile) @skipIfRemote # We do not currently support remote debugging via the MI. @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots @skipIfDarwin def test_lldbmi_source_option_start_script_exit(self): """Test that 'lldb-mi --interpreter' can execute a prepared file which passed via --source option.""" # Prepared source file sourceFile = self.copyScript("start_script_exit") self.spawnLldbMi(args="--source %s" % sourceFile) # After '-file-exec-and-symbols a.out' self.expect("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # After '-break-insert -f main' self.expect("-break-insert -f main") self.expect("\^done,bkpt={number=\"1\"") # After '-exec-run' self.expect("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # After '-break-insert main.cpp:BP_return' line = line_number('main.cpp', '//BP_return') self.expect("-break-insert main.cpp:%d" % line) self.expect("\^done,bkpt={number=\"2\"") # After '-exec-continue' self.expect("-exec-continue") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # After '-data-evaluate-expression a' self.expect("-data-evaluate-expression a") self.expect("\^done,value=\"10\"") # After '-gdb-exit' self.expect("-gdb-exit") self.expect("\^exit") self.expect("\*stopped,reason=\"exited-normally\"") os.unlink(sourceFile) @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @skipIfDarwin def test_lldbmi_source_option_start_script_error(self): """Test that 'lldb-mi --interpreter' stops execution of initial commands in case of error.""" # Prepared source file sourceFile = self.copyScript("start_script_error") self.spawnLldbMi(args="--source %s" % sourceFile) # After '-file-exec-and-symbols a.out' self.expect("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # After '-break-ins -f main' self.expect("-break-ins -f main") self.expect("\^error") # Test that lldb-mi is ready after execution of --source start_script self.expect(self.child_prompt, exactly=True) os.unlink(sourceFile) @skipIfRemote # We do not currently support remote debugging via the MI. @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @skipIfDarwin def test_lldbmi_log_option(self): """Test that 'lldb-mi --log' creates a log file in the current directory.""" logDirectory = self.getBuildDir() self.spawnLldbMi(args="%s --log" % self.myexe) # Test that the executable is loaded when file was specified self.expect("-file-exec-and-symbols \"%s\"" % self.myexe) self.expect("\^done") # Test that lldb-mi is ready when executable was loaded self.expect(self.child_prompt, exactly=True) # Run self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"exited-normally\"") # Check log file is created import glob import os logFile = glob.glob(logDirectory + "/lldb-mi-*.log") if not logFile: self.fail("log file not found") # Delete log for f in logFile: os.remove(f) @skipIfRemote # We do not currently support remote debugging via the MI. @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @skipIfDarwin def test_lldbmi_log_directory_option(self): """Test that 'lldb-mi --log --log-dir' creates a log file in the directory specified by --log-dir.""" # Create log in temp directory import tempfile logDirectory = tempfile.gettempdir() self.spawnLldbMi( args="%s --log --log-dir=%s" % (self.myexe, logDirectory)) # Test that the executable is loaded when file was specified self.expect("-file-exec-and-symbols \"%s\"" % self.myexe) self.expect("\^done") # Test that lldb-mi is ready when executable was loaded self.expect(self.child_prompt, exactly=True) # Run self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"exited-normally\"") # Check log file is created import glob import os logFile = glob.glob(logDirectory + "/lldb-mi-*.log") if not logFile: self.fail("log file not found") # Delete log for f in logFile: os.remove(f) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_qThreadStopInfo.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_qThreadStopInfo.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_qThreadStopInfo.py (revision 337147) @@ -1,182 +1,182 @@ from __future__ import print_function import sys import unittest2 import gdbremote_testcase from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestGdbRemote_qThreadStopInfo(gdbremote_testcase.GdbRemoteTestCaseBase): mydir = TestBase.compute_mydir(__file__) THREAD_COUNT = 5 @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet - @skipIfDarwinEmbedded # + @skipIfDarwinEmbedded # def gather_stop_replies_via_qThreadStopInfo(self, thread_count): # Set up the inferior args. inferior_args = [] for i in range(thread_count - 1): inferior_args.append("thread:new") inferior_args.append("sleep:10") procs = self.prep_debug_monitor_and_inferior( inferior_args=inferior_args) # Assumes test_sequence has anything added needed to setup the initial state. # (Like optionally enabling QThreadsInStopReply.) self.test_sequence.add_log_lines([ "read packet: $c#63" ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Give threads time to start up, then break. time.sleep(1) self.reset_test_sequence() self.test_sequence.add_log_lines( [ "read packet: {}".format( chr(3)), { "direction": "send", "regex": r"^\$T([0-9a-fA-F]+)([^#]+)#[0-9a-fA-F]{2}$", "capture": { 1: "stop_result", 2: "key_vals_text"}}, ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Wait until all threads have started. threads = self.wait_for_thread_count(thread_count, timeout_seconds=3) self.assertIsNotNone(threads) self.assertEqual(len(threads), thread_count) # Grab stop reply for each thread via qThreadStopInfo{tid:hex}. stop_replies = {} thread_dicts = {} for thread in threads: # Run the qThreadStopInfo command. self.reset_test_sequence() self.test_sequence.add_log_lines( [ "read packet: $qThreadStopInfo{:x}#00".format(thread), { "direction": "send", "regex": r"^\$T([0-9a-fA-F]+)([^#]+)#[0-9a-fA-F]{2}$", "capture": { 1: "stop_result", 2: "key_vals_text"}}, ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Parse stop reply contents. key_vals_text = context.get("key_vals_text") self.assertIsNotNone(key_vals_text) kv_dict = self.parse_key_val_dict(key_vals_text) self.assertIsNotNone(kv_dict) # Verify there is a thread and that it matches the expected thread # id. kv_thread = kv_dict.get("thread") self.assertIsNotNone(kv_thread) kv_thread_id = int(kv_thread, 16) self.assertEqual(kv_thread_id, thread) # Grab the stop id reported. stop_result_text = context.get("stop_result") self.assertIsNotNone(stop_result_text) stop_replies[kv_thread_id] = int(stop_result_text, 16) # Hang on to the key-val dictionary for the thread. thread_dicts[kv_thread_id] = kv_dict return (stop_replies, thread_dicts) def qThreadStopInfo_works_for_multiple_threads(self, thread_count): (stop_replies, _) = self.gather_stop_replies_via_qThreadStopInfo(thread_count) self.assertEqual(len(stop_replies), thread_count) @debugserver_test def test_qThreadStopInfo_works_for_multiple_threads_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qThreadStopInfo_works_for_multiple_threads(self.THREAD_COUNT) @llgs_test def test_qThreadStopInfo_works_for_multiple_threads_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qThreadStopInfo_works_for_multiple_threads(self.THREAD_COUNT) def qThreadStopInfo_only_reports_one_thread_stop_reason_during_interrupt( self, thread_count): (stop_replies, _) = self.gather_stop_replies_via_qThreadStopInfo(thread_count) self.assertIsNotNone(stop_replies) no_stop_reason_count = sum( 1 for stop_reason in list( stop_replies.values()) if stop_reason == 0) with_stop_reason_count = sum( 1 for stop_reason in list( stop_replies.values()) if stop_reason != 0) # All but one thread should report no stop reason. self.assertEqual(no_stop_reason_count, thread_count - 1) # Only one thread should should indicate a stop reason. self.assertEqual(with_stop_reason_count, 1) @debugserver_test def test_qThreadStopInfo_only_reports_one_thread_stop_reason_during_interrupt_debugserver( self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qThreadStopInfo_only_reports_one_thread_stop_reason_during_interrupt( self.THREAD_COUNT) @llgs_test def test_qThreadStopInfo_only_reports_one_thread_stop_reason_during_interrupt_llgs( self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qThreadStopInfo_only_reports_one_thread_stop_reason_during_interrupt( self.THREAD_COUNT) def qThreadStopInfo_has_valid_thread_names( self, thread_count, expected_thread_name): (_, thread_dicts) = self.gather_stop_replies_via_qThreadStopInfo(thread_count) self.assertIsNotNone(thread_dicts) for thread_dict in list(thread_dicts.values()): name = thread_dict.get("name") self.assertIsNotNone(name) self.assertEqual(name, expected_thread_name) @unittest2.skip("MacOSX doesn't have a default thread name") @debugserver_test def test_qThreadStopInfo_has_valid_thread_names_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qThreadStopInfo_has_valid_thread_names(self.THREAD_COUNT, "a.out") # test requires OS with set, equal thread names by default. @skipUnlessPlatform(["linux"]) @llgs_test def test_qThreadStopInfo_has_valid_thread_names_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qThreadStopInfo_has_valid_thread_names(self.THREAD_COUNT, "a.out") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py (revision 337146) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py (revision 337147) @@ -1,1571 +1,1571 @@ """ Test case for testing the gdbremote protocol. Tests run against debugserver and lldb-server (llgs). lldb-server tests run where the lldb-server exe is available. This class will be broken into smaller test case classes by gdb remote packet functional areas. For now it contains the initial set of tests implemented. """ from __future__ import print_function import unittest2 import gdbremote_testcase import lldbgdbserverutils import platform import signal from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test.lldbdwarf import * from lldbsuite.test import lldbutil class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase, DwarfOpcodeParser): mydir = TestBase.compute_mydir(__file__) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_exe_starts_debugserver(self): self.init_debugserver_test() server = self.connect_to_debug_monitor() @llgs_test def test_exe_starts_llgs(self): self.init_llgs_test() server = self.connect_to_debug_monitor() def start_no_ack_mode(self): server = self.connect_to_debug_monitor() self.assertIsNotNone(server) self.add_no_ack_remote_stream() self.expect_gdbremote_sequence() @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_start_no_ack_mode_debugserver(self): self.init_debugserver_test() self.start_no_ack_mode() @llgs_test def test_start_no_ack_mode_llgs(self): self.init_llgs_test() self.start_no_ack_mode() def thread_suffix_supported(self): server = self.connect_to_debug_monitor() self.assertIsNotNone(server) self.add_no_ack_remote_stream() self.test_sequence.add_log_lines( ["lldb-server < 26> read packet: $QThreadSuffixSupported#e4", "lldb-server < 6> send packet: $OK#9a"], True) self.expect_gdbremote_sequence() @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_thread_suffix_supported_debugserver(self): self.init_debugserver_test() self.thread_suffix_supported() @llgs_test def test_thread_suffix_supported_llgs(self): self.init_llgs_test() self.thread_suffix_supported() def list_threads_in_stop_reply_supported(self): server = self.connect_to_debug_monitor() self.assertIsNotNone(server) self.add_no_ack_remote_stream() self.test_sequence.add_log_lines( ["lldb-server < 27> read packet: $QListThreadsInStopReply#21", "lldb-server < 6> send packet: $OK#9a"], True) self.expect_gdbremote_sequence() @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_list_threads_in_stop_reply_supported_debugserver(self): self.init_debugserver_test() self.list_threads_in_stop_reply_supported() @llgs_test def test_list_threads_in_stop_reply_supported_llgs(self): self.init_llgs_test() self.list_threads_in_stop_reply_supported() def c_packet_works(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.test_sequence.add_log_lines( ["read packet: $c#63", "send packet: $W00#00"], True) self.expect_gdbremote_sequence() @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_c_packet_works_debugserver(self): self.init_debugserver_test() self.build() self.c_packet_works() @llgs_test def test_c_packet_works_llgs(self): self.init_llgs_test() self.build() self.c_packet_works() def inferior_print_exit(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # build launch args launch_args += ["hello, world"] self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.test_sequence.add_log_lines( ["read packet: $vCont;c#a8", {"type": "output_match", "regex": self.maybe_strict_output_regex(r"hello, world\r\n")}, "send packet: $W00#00"], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_inferior_print_exit_debugserver(self): self.init_debugserver_test() self.build() self.inferior_print_exit() @llgs_test @expectedFlakeyLinux("llvm.org/pr25652") def test_inferior_print_exit_llgs(self): self.init_llgs_test() self.build() self.inferior_print_exit() def first_launch_stop_reply_thread_matches_first_qC(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # build launch args launch_args += ["hello, world"] self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.test_sequence.add_log_lines(["read packet: $qC#00", {"direction": "send", "regex": r"^\$QC([0-9a-fA-F]+)#", "capture": {1: "thread_id"}}, "read packet: $?#00", {"direction": "send", "regex": r"^\$T[0-9a-fA-F]{2}thread:([0-9a-fA-F]+)", "expect_captures": {1: "thread_id"}}], True) self.expect_gdbremote_sequence() @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_first_launch_stop_reply_thread_matches_first_qC_debugserver(self): self.init_debugserver_test() self.build() self.first_launch_stop_reply_thread_matches_first_qC() @llgs_test def test_first_launch_stop_reply_thread_matches_first_qC_llgs(self): self.init_llgs_test() self.build() self.first_launch_stop_reply_thread_matches_first_qC() def attach_commandline_continue_app_exits(self): procs = self.prep_debug_monitor_and_inferior() self.test_sequence.add_log_lines( ["read packet: $vCont;c#a8", "send packet: $W00#00"], True) self.expect_gdbremote_sequence() # Wait a moment for completed and now-detached inferior process to # clear. time.sleep(1) if not lldb.remote_platform: # Process should be dead now. Reap results. poll_result = procs["inferior"].poll() self.assertIsNotNone(poll_result) # Where possible, verify at the system level that the process is not # running. self.assertFalse( lldbgdbserverutils.process_is_running( procs["inferior"].pid, False)) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_attach_commandline_continue_app_exits_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_attach() self.attach_commandline_continue_app_exits() @llgs_test def test_attach_commandline_continue_app_exits_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_attach() self.attach_commandline_continue_app_exits() def qRegisterInfo_returns_one_valid_result(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # Build the expected protocol stream self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.test_sequence.add_log_lines( ["read packet: $qRegisterInfo0#00", {"direction": "send", "regex": r"^\$(.+);#[0-9A-Fa-f]{2}", "capture": {1: "reginfo_0"}}], True) # Run the stream context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) reg_info_packet = context.get("reginfo_0") self.assertIsNotNone(reg_info_packet) self.assert_valid_reg_info( lldbgdbserverutils.parse_reg_info_response(reg_info_packet)) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_qRegisterInfo_returns_one_valid_result_debugserver(self): self.init_debugserver_test() self.build() self.qRegisterInfo_returns_one_valid_result() @llgs_test def test_qRegisterInfo_returns_one_valid_result_llgs(self): self.init_llgs_test() self.build() self.qRegisterInfo_returns_one_valid_result() def qRegisterInfo_returns_all_valid_results(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # Build the expected protocol stream. self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.add_register_info_collection_packets() # Run the stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Validate that each register info returned validates. for reg_info in self.parse_register_info_packets(context): self.assert_valid_reg_info(reg_info) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_qRegisterInfo_returns_all_valid_results_debugserver(self): self.init_debugserver_test() self.build() self.qRegisterInfo_returns_all_valid_results() @llgs_test def test_qRegisterInfo_returns_all_valid_results_llgs(self): self.init_llgs_test() self.build() self.qRegisterInfo_returns_all_valid_results() def qRegisterInfo_contains_required_generics(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # Build the expected protocol stream self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.add_register_info_collection_packets() # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather register info entries. reg_infos = self.parse_register_info_packets(context) # Collect all generic registers found. generic_regs = { reg_info['generic']: 1 for reg_info in reg_infos if 'generic' in reg_info} # Ensure we have a program counter register. self.assertTrue('pc' in generic_regs) # Ensure we have a frame pointer register. PPC64le's FP is the same as SP if self.getArchitecture() != 'powerpc64le': self.assertTrue('fp' in generic_regs) # Ensure we have a stack pointer register. self.assertTrue('sp' in generic_regs) # Ensure we have a flags register. self.assertTrue('flags' in generic_regs) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_qRegisterInfo_contains_required_generics_debugserver(self): self.init_debugserver_test() self.build() self.qRegisterInfo_contains_required_generics() @llgs_test def test_qRegisterInfo_contains_required_generics_llgs(self): self.init_llgs_test() self.build() self.qRegisterInfo_contains_required_generics() def qRegisterInfo_contains_at_least_one_register_set(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # Build the expected protocol stream self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.add_register_info_collection_packets() # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather register info entries. reg_infos = self.parse_register_info_packets(context) # Collect all register sets found. register_sets = { reg_info['set']: 1 for reg_info in reg_infos if 'set' in reg_info} self.assertTrue(len(register_sets) >= 1) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_qRegisterInfo_contains_at_least_one_register_set_debugserver( self): self.init_debugserver_test() self.build() self.qRegisterInfo_contains_at_least_one_register_set() @llgs_test def test_qRegisterInfo_contains_at_least_one_register_set_llgs(self): self.init_llgs_test() self.build() self.qRegisterInfo_contains_at_least_one_register_set() def targetHasAVX(self): triple = self.dbg.GetSelectedPlatform().GetTriple() # TODO other platforms, please implement this function if not re.match(".*-.*-linux", triple): return True # Need to do something different for non-Linux/Android targets if lldb.remote_platform: self.runCmd('platform get-file "/proc/cpuinfo" "cpuinfo"') cpuinfo_path = "cpuinfo" self.addTearDownHook(lambda: os.unlink("cpuinfo")) else: cpuinfo_path = "/proc/cpuinfo" f = open(cpuinfo_path, 'r') cpuinfo = f.read() f.close() return " avx " in cpuinfo def qRegisterInfo_contains_avx_registers(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # Build the expected protocol stream self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.add_register_info_collection_packets() # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather register info entries. reg_infos = self.parse_register_info_packets(context) # Collect all generics found. register_sets = { reg_info['set']: 1 for reg_info in reg_infos if 'set' in reg_info} self.assertEqual( self.targetHasAVX(), "Advanced Vector Extensions" in register_sets) @llgs_test def test_qRegisterInfo_contains_avx_registers_llgs(self): self.init_llgs_test() self.build() self.qRegisterInfo_contains_avx_registers() def qThreadInfo_contains_thread(self): procs = self.prep_debug_monitor_and_inferior() self.add_threadinfo_collection_packets() # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather threadinfo entries. threads = self.parse_threadinfo_packets(context) self.assertIsNotNone(threads) # We should have exactly one thread. self.assertEqual(len(threads), 1) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_qThreadInfo_contains_thread_launch_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qThreadInfo_contains_thread() @llgs_test def test_qThreadInfo_contains_thread_launch_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qThreadInfo_contains_thread() @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_qThreadInfo_contains_thread_attach_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_attach() self.qThreadInfo_contains_thread() @llgs_test def test_qThreadInfo_contains_thread_attach_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_attach() self.qThreadInfo_contains_thread() def qThreadInfo_matches_qC(self): procs = self.prep_debug_monitor_and_inferior() self.add_threadinfo_collection_packets() self.test_sequence.add_log_lines( ["read packet: $qC#00", {"direction": "send", "regex": r"^\$QC([0-9a-fA-F]+)#", "capture": {1: "thread_id"}} ], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather threadinfo entries. threads = self.parse_threadinfo_packets(context) self.assertIsNotNone(threads) # We should have exactly one thread from threadinfo. self.assertEqual(len(threads), 1) # We should have a valid thread_id from $QC. QC_thread_id_hex = context.get("thread_id") self.assertIsNotNone(QC_thread_id_hex) QC_thread_id = int(QC_thread_id_hex, 16) # Those two should be the same. self.assertEqual(threads[0], QC_thread_id) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_qThreadInfo_matches_qC_launch_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qThreadInfo_matches_qC() @llgs_test def test_qThreadInfo_matches_qC_launch_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qThreadInfo_matches_qC() @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_qThreadInfo_matches_qC_attach_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_attach() self.qThreadInfo_matches_qC() @llgs_test def test_qThreadInfo_matches_qC_attach_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_attach() self.qThreadInfo_matches_qC() def p_returns_correct_data_size_for_each_qRegisterInfo(self): procs = self.prep_debug_monitor_and_inferior() self.add_register_info_collection_packets() # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather register info entries. reg_infos = self.parse_register_info_packets(context) self.assertIsNotNone(reg_infos) self.assertTrue(len(reg_infos) > 0) inferior_exe_path = self.getBuildArtifact("a.out") Target = self.dbg.CreateTarget(inferior_exe_path) byte_order = Target.GetByteOrder() # Read value for each register. reg_index = 0 for reg_info in reg_infos: # Skip registers that don't have a register set. For x86, these are # the DRx registers, which have no LLDB-kind register number and thus # cannot be read via normal # NativeRegisterContext::ReadRegister(reg_info,...) calls. if not "set" in reg_info: continue # Clear existing packet expectations. self.reset_test_sequence() # Run the register query self.test_sequence.add_log_lines( ["read packet: $p{0:x}#00".format(reg_index), {"direction": "send", "regex": r"^\$([0-9a-fA-F]+)#", "capture": {1: "p_response"}}], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Verify the response length. p_response = context.get("p_response") self.assertIsNotNone(p_response) if "dynamic_size_dwarf_expr_bytes" in reg_info: self.updateRegInfoBitsize(reg_info, byte_order) self.assertEqual(len(p_response), 2 * int(reg_info["bitsize"]) / 8) # Increment loop reg_index += 1 @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_p_returns_correct_data_size_for_each_qRegisterInfo_launch_debugserver( self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.p_returns_correct_data_size_for_each_qRegisterInfo() @llgs_test def test_p_returns_correct_data_size_for_each_qRegisterInfo_launch_llgs( self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.p_returns_correct_data_size_for_each_qRegisterInfo() @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_p_returns_correct_data_size_for_each_qRegisterInfo_attach_debugserver( self): self.init_debugserver_test() self.build() self.set_inferior_startup_attach() self.p_returns_correct_data_size_for_each_qRegisterInfo() @llgs_test def test_p_returns_correct_data_size_for_each_qRegisterInfo_attach_llgs( self): self.init_llgs_test() self.build() self.set_inferior_startup_attach() self.p_returns_correct_data_size_for_each_qRegisterInfo() def Hg_switches_to_3_threads(self): # Startup the inferior with three threads (main + 2 new ones). procs = self.prep_debug_monitor_and_inferior( inferior_args=["thread:new", "thread:new"]) # Let the inferior process have a few moments to start up the thread # when launched. (The launch scenario has no time to run, so threads # won't be there yet.) self.run_process_then_stop(run_seconds=1) # Wait at most x seconds for 3 threads to be present. threads = self.wait_for_thread_count(3, timeout_seconds=5) self.assertEqual(len(threads), 3) # verify we can $H to each thead, and $qC matches the thread we set. for thread in threads: # Change to each thread, verify current thread id. self.reset_test_sequence() self.test_sequence.add_log_lines( ["read packet: $Hg{0:x}#00".format(thread), # Set current thread. "send packet: $OK#00", "read packet: $qC#00", {"direction": "send", "regex": r"^\$QC([0-9a-fA-F]+)#", "capture": {1: "thread_id"}}], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Verify the thread id. self.assertIsNotNone(context.get("thread_id")) self.assertEqual(int(context.get("thread_id"), 16), thread) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_Hg_switches_to_3_threads_launch_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.Hg_switches_to_3_threads() @llgs_test def test_Hg_switches_to_3_threads_launch_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.Hg_switches_to_3_threads() @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_Hg_switches_to_3_threads_attach_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_attach() self.Hg_switches_to_3_threads() @llgs_test def test_Hg_switches_to_3_threads_attach_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_attach() self.Hg_switches_to_3_threads() def Hc_then_Csignal_signals_correct_thread(self, segfault_signo): # NOTE only run this one in inferior-launched mode: we can't grab inferior stdout when running attached, # and the test requires getting stdout from the exe. NUM_THREADS = 3 # Startup the inferior with three threads (main + NUM_THREADS-1 worker threads). # inferior_args=["thread:print-ids"] inferior_args = ["thread:segfault"] for i in range(NUM_THREADS - 1): # if i > 0: # Give time between thread creation/segfaulting for the handler to work. # inferior_args.append("sleep:1") inferior_args.append("thread:new") inferior_args.append("sleep:10") # Launch/attach. (In our case, this should only ever be launched since # we need inferior stdout/stderr). procs = self.prep_debug_monitor_and_inferior( inferior_args=inferior_args) self.test_sequence.add_log_lines(["read packet: $c#63"], True) context = self.expect_gdbremote_sequence() # Let the inferior process have a few moments to start up the thread when launched. # context = self.run_process_then_stop(run_seconds=1) # Wait at most x seconds for all threads to be present. # threads = self.wait_for_thread_count(NUM_THREADS, timeout_seconds=5) # self.assertEquals(len(threads), NUM_THREADS) signaled_tids = {} print_thread_ids = {} # Switch to each thread, deliver a signal, and verify signal delivery for i in range(NUM_THREADS - 1): # Run until SIGSEGV comes in. self.reset_test_sequence() self.test_sequence.add_log_lines([{"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "signo", 2: "thread_id"}}], True) context = self.expect_gdbremote_sequence(timeout_seconds=10) self.assertIsNotNone(context) signo = context.get("signo") self.assertEqual(int(signo, 16), segfault_signo) # Ensure we haven't seen this tid yet. thread_id = int(context.get("thread_id"), 16) self.assertFalse(thread_id in signaled_tids) signaled_tids[thread_id] = 1 # Send SIGUSR1 to the thread that signaled the SIGSEGV. self.reset_test_sequence() self.test_sequence.add_log_lines( [ # Set the continue thread. # Set current thread. "read packet: $Hc{0:x}#00".format(thread_id), "send packet: $OK#00", # Continue sending the signal number to the continue thread. # The commented out packet is a way to do this same operation without using # a $Hc (but this test is testing $Hc, so we'll stick with the former). "read packet: $C{0:x}#00".format(lldbutil.get_signal_number('SIGUSR1')), # "read packet: $vCont;C{0:x}:{1:x};c#00".format(lldbutil.get_signal_number('SIGUSR1'), thread_id), # FIXME: Linux does not report the thread stop on the delivered signal (SIGUSR1 here). MacOSX debugserver does. # But MacOSX debugserver isn't guaranteeing the thread the signal handler runs on, so currently its an XFAIL. # Need to rectify behavior here. The linux behavior is more intuitive to me since we're essentially swapping out # an about-to-be-delivered signal (for which we already sent a stop packet) to a different signal. # {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }, # "read packet: $c#63", {"type": "output_match", "regex": r"^received SIGUSR1 on thread id: ([0-9a-fA-F]+)\r\nthread ([0-9a-fA-F]+): past SIGSEGV\r\n", "capture": {1: "print_thread_id", 2: "post_handle_thread_id"}}, ], True) # Run the sequence. context = self.expect_gdbremote_sequence(timeout_seconds=10) self.assertIsNotNone(context) # Ensure the stop signal is the signal we delivered. # stop_signo = context.get("stop_signo") # self.assertIsNotNone(stop_signo) # self.assertEquals(int(stop_signo,16), lldbutil.get_signal_number('SIGUSR1')) # Ensure the stop thread is the thread to which we delivered the signal. # stop_thread_id = context.get("stop_thread_id") # self.assertIsNotNone(stop_thread_id) # self.assertEquals(int(stop_thread_id,16), thread_id) # Ensure we haven't seen this thread id yet. The inferior's # self-obtained thread ids are not guaranteed to match the stub # tids (at least on MacOSX). print_thread_id = context.get("print_thread_id") self.assertIsNotNone(print_thread_id) print_thread_id = int(print_thread_id, 16) self.assertFalse(print_thread_id in print_thread_ids) # Now remember this print (i.e. inferior-reflected) thread id and # ensure we don't hit it again. print_thread_ids[print_thread_id] = 1 # Ensure post signal-handle thread id matches the thread that # initially raised the SIGSEGV. post_handle_thread_id = context.get("post_handle_thread_id") self.assertIsNotNone(post_handle_thread_id) post_handle_thread_id = int(post_handle_thread_id, 16) self.assertEqual(post_handle_thread_id, print_thread_id) @unittest2.expectedFailure() @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_Hc_then_Csignal_signals_correct_thread_launch_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() # Darwin debugserver translates some signals like SIGSEGV into some gdb # expectations about fixed signal numbers. self.Hc_then_Csignal_signals_correct_thread(self.TARGET_EXC_BAD_ACCESS) @llgs_test def test_Hc_then_Csignal_signals_correct_thread_launch_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.Hc_then_Csignal_signals_correct_thread( lldbutil.get_signal_number('SIGSEGV')) def m_packet_reads_memory(self): # This is the memory we will write into the inferior and then ensure we # can read back with $m. MEMORY_CONTENTS = "Test contents 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz" # Start up the inferior. procs = self.prep_debug_monitor_and_inferior( inferior_args=[ "set-message:%s" % MEMORY_CONTENTS, "get-data-address-hex:g_message", "sleep:5"]) # Run the process self.test_sequence.add_log_lines( [ # Start running after initial stop. "read packet: $c#63", # Match output line that prints the memory address of the message buffer within the inferior. # Note we require launch-only testing so we can get inferior otuput. {"type": "output_match", "regex": self.maybe_strict_output_regex(r"data address: 0x([0-9a-fA-F]+)\r\n"), "capture": {1: "message_address"}}, # Now stop the inferior. "read packet: {}".format(chr(3)), # And wait for the stop notification. {"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "stop_signo", 2: "stop_thread_id"}}], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Grab the message address. self.assertIsNotNone(context.get("message_address")) message_address = int(context.get("message_address"), 16) # Grab contents from the inferior. self.reset_test_sequence() self.test_sequence.add_log_lines( ["read packet: $m{0:x},{1:x}#00".format(message_address, len(MEMORY_CONTENTS)), {"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$", "capture": {1: "read_contents"}}], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Ensure what we read from inferior memory is what we wrote. self.assertIsNotNone(context.get("read_contents")) read_contents = context.get("read_contents").decode("hex") self.assertEqual(read_contents, MEMORY_CONTENTS) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_m_packet_reads_memory_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.m_packet_reads_memory() @llgs_test def test_m_packet_reads_memory_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.m_packet_reads_memory() def qMemoryRegionInfo_is_supported(self): # Start up the inferior. procs = self.prep_debug_monitor_and_inferior() # Ask if it supports $qMemoryRegionInfo. self.test_sequence.add_log_lines( ["read packet: $qMemoryRegionInfo#00", "send packet: $OK#00" ], True) self.expect_gdbremote_sequence() @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_qMemoryRegionInfo_is_supported_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_is_supported() @llgs_test def test_qMemoryRegionInfo_is_supported_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_is_supported() def qMemoryRegionInfo_reports_code_address_as_executable(self): # Start up the inferior. procs = self.prep_debug_monitor_and_inferior( inferior_args=["get-code-address-hex:hello", "sleep:5"]) # Run the process self.test_sequence.add_log_lines( [ # Start running after initial stop. "read packet: $c#63", # Match output line that prints the memory address of the message buffer within the inferior. # Note we require launch-only testing so we can get inferior otuput. {"type": "output_match", "regex": self.maybe_strict_output_regex(r"code address: 0x([0-9a-fA-F]+)\r\n"), "capture": {1: "code_address"}}, # Now stop the inferior. "read packet: {}".format(chr(3)), # And wait for the stop notification. {"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "stop_signo", 2: "stop_thread_id"}}], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Grab the code address. self.assertIsNotNone(context.get("code_address")) code_address = int(context.get("code_address"), 16) # Grab memory region info from the inferior. self.reset_test_sequence() self.add_query_memory_region_packets(code_address) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) mem_region_dict = self.parse_memory_region_packet(context) # Ensure there are no errors reported. self.assertFalse("error" in mem_region_dict) # Ensure code address is readable and executable. self.assertTrue("permissions" in mem_region_dict) self.assertTrue("r" in mem_region_dict["permissions"]) self.assertTrue("x" in mem_region_dict["permissions"]) # Ensure the start address and size encompass the address we queried. self.assert_address_within_memory_region(code_address, mem_region_dict) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_qMemoryRegionInfo_reports_code_address_as_executable_debugserver( self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_code_address_as_executable() @llgs_test def test_qMemoryRegionInfo_reports_code_address_as_executable_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_code_address_as_executable() def qMemoryRegionInfo_reports_stack_address_as_readable_writeable(self): # Start up the inferior. procs = self.prep_debug_monitor_and_inferior( inferior_args=["get-stack-address-hex:", "sleep:5"]) # Run the process self.test_sequence.add_log_lines( [ # Start running after initial stop. "read packet: $c#63", # Match output line that prints the memory address of the message buffer within the inferior. # Note we require launch-only testing so we can get inferior otuput. {"type": "output_match", "regex": self.maybe_strict_output_regex(r"stack address: 0x([0-9a-fA-F]+)\r\n"), "capture": {1: "stack_address"}}, # Now stop the inferior. "read packet: {}".format(chr(3)), # And wait for the stop notification. {"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "stop_signo", 2: "stop_thread_id"}}], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Grab the address. self.assertIsNotNone(context.get("stack_address")) stack_address = int(context.get("stack_address"), 16) # Grab memory region info from the inferior. self.reset_test_sequence() self.add_query_memory_region_packets(stack_address) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) mem_region_dict = self.parse_memory_region_packet(context) # Ensure there are no errors reported. self.assertFalse("error" in mem_region_dict) # Ensure address is readable and executable. self.assertTrue("permissions" in mem_region_dict) self.assertTrue("r" in mem_region_dict["permissions"]) self.assertTrue("w" in mem_region_dict["permissions"]) # Ensure the start address and size encompass the address we queried. self.assert_address_within_memory_region( stack_address, mem_region_dict) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_qMemoryRegionInfo_reports_stack_address_as_readable_writeable_debugserver( self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_stack_address_as_readable_writeable() @llgs_test def test_qMemoryRegionInfo_reports_stack_address_as_readable_writeable_llgs( self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_stack_address_as_readable_writeable() def qMemoryRegionInfo_reports_heap_address_as_readable_writeable(self): # Start up the inferior. procs = self.prep_debug_monitor_and_inferior( inferior_args=["get-heap-address-hex:", "sleep:5"]) # Run the process self.test_sequence.add_log_lines( [ # Start running after initial stop. "read packet: $c#63", # Match output line that prints the memory address of the message buffer within the inferior. # Note we require launch-only testing so we can get inferior otuput. {"type": "output_match", "regex": self.maybe_strict_output_regex(r"heap address: 0x([0-9a-fA-F]+)\r\n"), "capture": {1: "heap_address"}}, # Now stop the inferior. "read packet: {}".format(chr(3)), # And wait for the stop notification. {"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "stop_signo", 2: "stop_thread_id"}}], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Grab the address. self.assertIsNotNone(context.get("heap_address")) heap_address = int(context.get("heap_address"), 16) # Grab memory region info from the inferior. self.reset_test_sequence() self.add_query_memory_region_packets(heap_address) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) mem_region_dict = self.parse_memory_region_packet(context) # Ensure there are no errors reported. self.assertFalse("error" in mem_region_dict) # Ensure address is readable and executable. self.assertTrue("permissions" in mem_region_dict) self.assertTrue("r" in mem_region_dict["permissions"]) self.assertTrue("w" in mem_region_dict["permissions"]) # Ensure the start address and size encompass the address we queried. self.assert_address_within_memory_region(heap_address, mem_region_dict) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_qMemoryRegionInfo_reports_heap_address_as_readable_writeable_debugserver( self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_heap_address_as_readable_writeable() @llgs_test def test_qMemoryRegionInfo_reports_heap_address_as_readable_writeable_llgs( self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_heap_address_as_readable_writeable() def breakpoint_set_and_remove_work(self, want_hardware=False): # Start up the inferior. procs = self.prep_debug_monitor_and_inferior( inferior_args=[ "get-code-address-hex:hello", "sleep:1", "call-function:hello"]) # Run the process self.add_register_info_collection_packets() self.add_process_info_collection_packets() self.test_sequence.add_log_lines( [ # Start running after initial stop. "read packet: $c#63", # Match output line that prints the memory address of the function call entry point. # Note we require launch-only testing so we can get inferior otuput. {"type": "output_match", "regex": self.maybe_strict_output_regex(r"code address: 0x([0-9a-fA-F]+)\r\n"), "capture": {1: "function_address"}}, # Now stop the inferior. "read packet: {}".format(chr(3)), # And wait for the stop notification. {"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "stop_signo", 2: "stop_thread_id"}}], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather process info - we need endian of target to handle register # value conversions. process_info = self.parse_process_info_response(context) endian = process_info.get("endian") self.assertIsNotNone(endian) # Gather register info entries. reg_infos = self.parse_register_info_packets(context) (pc_lldb_reg_index, pc_reg_info) = self.find_pc_reg_info(reg_infos) self.assertIsNotNone(pc_lldb_reg_index) self.assertIsNotNone(pc_reg_info) # Grab the function address. self.assertIsNotNone(context.get("function_address")) function_address = int(context.get("function_address"), 16) # Get current target architecture target_arch = self.getArchitecture() # Set the breakpoint. if (target_arch == "arm") or (target_arch == "aarch64"): # TODO: Handle case when setting breakpoint in thumb code BREAKPOINT_KIND = 4 else: BREAKPOINT_KIND = 1 # Set default packet type to Z0 (software breakpoint) - z_packet_type = 0 + z_packet_type = 0 # If hardware breakpoint is requested set packet type to Z1 if want_hardware == True: z_packet_type = 1 self.reset_test_sequence() self.add_set_breakpoint_packets( function_address, z_packet_type, do_continue=True, breakpoint_kind=BREAKPOINT_KIND) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Verify the stop signal reported was the breakpoint signal number. stop_signo = context.get("stop_signo") self.assertIsNotNone(stop_signo) self.assertEqual(int(stop_signo, 16), lldbutil.get_signal_number('SIGTRAP')) # Ensure we did not receive any output. If the breakpoint was not set, we would # see output (from a launched process with captured stdio) printing a hello, world message. # That would indicate the breakpoint didn't take. self.assertEqual(len(context["O_content"]), 0) # Verify that the PC for the main thread is where we expect it - right at the breakpoint address. # This acts as a another validation on the register reading code. self.reset_test_sequence() self.test_sequence.add_log_lines( [ # Print the PC. This should match the breakpoint address. "read packet: $p{0:x}#00".format(pc_lldb_reg_index), # Capture $p results. {"direction": "send", "regex": r"^\$([0-9a-fA-F]+)#", "capture": {1: "p_response"}}, ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Verify the PC is where we expect. Note response is in endianness of # the inferior. p_response = context.get("p_response") self.assertIsNotNone(p_response) # Convert from target endian to int. returned_pc = lldbgdbserverutils.unpack_register_hex_unsigned( endian, p_response) self.assertEqual(returned_pc, function_address) # Verify that a breakpoint remove and continue gets us the expected # output. self.reset_test_sequence() # Add breakpoint remove packets self.add_remove_breakpoint_packets( function_address, z_packet_type, breakpoint_kind=BREAKPOINT_KIND) self.test_sequence.add_log_lines( [ # Continue running. "read packet: $c#63", # We should now receive the output from the call. {"type": "output_match", "regex": r"^hello, world\r\n$"}, # And wait for program completion. {"direction": "send", "regex": r"^\$W00(.*)#[0-9a-fA-F]{2}$"}, ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_software_breakpoint_set_and_remove_work_debugserver(self): self.init_debugserver_test() if self.getArchitecture() == "arm": # TODO: Handle case when setting breakpoint in thumb code self.build(dictionary={'CFLAGS_EXTRAS': '-marm'}) else: self.build() self.set_inferior_startup_launch() self.breakpoint_set_and_remove_work(want_hardware=False) @llgs_test @expectedFlakeyLinux("llvm.org/pr25652") def test_software_breakpoint_set_and_remove_work_llgs(self): self.init_llgs_test() if self.getArchitecture() == "arm": # TODO: Handle case when setting breakpoint in thumb code self.build(dictionary={'CFLAGS_EXTRAS': '-marm'}) else: self.build() self.set_inferior_startup_launch() self.breakpoint_set_and_remove_work(want_hardware=False) @debugserver_test @skipUnlessPlatform(oslist=['linux']) @expectedFailureAndroid @skipIf(archs=no_match(['arm', 'aarch64'])) @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_hardware_breakpoint_set_and_remove_work_debugserver(self): self.init_debugserver_test() if self.getArchitecture() == "arm": # TODO: Handle case when setting breakpoint in thumb code self.build(dictionary={'CFLAGS_EXTRAS': '-marm'}) else: self.build() self.set_inferior_startup_launch() self.breakpoint_set_and_remove_work(want_hardware=True) @llgs_test @skipUnlessPlatform(oslist=['linux']) @expectedFailureAndroid @skipIf(archs=no_match(['arm', 'aarch64'])) def test_hardware_breakpoint_set_and_remove_work_llgs(self): self.init_llgs_test() if self.getArchitecture() == "arm": # TODO: Handle case when setting breakpoint in thumb code self.build(dictionary={'CFLAGS_EXTRAS': '-marm'}) else: self.build() self.set_inferior_startup_launch() self.breakpoint_set_and_remove_work(want_hardware=True) def qSupported_returns_known_stub_features(self): # Start up the stub and start/prep the inferior. procs = self.prep_debug_monitor_and_inferior() self.add_qSupported_packets() # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Retrieve the qSupported features. supported_dict = self.parse_qSupported_response(context) self.assertIsNotNone(supported_dict) self.assertTrue(len(supported_dict) > 0) @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_qSupported_returns_known_stub_features_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qSupported_returns_known_stub_features() @llgs_test def test_qSupported_returns_known_stub_features_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qSupported_returns_known_stub_features() def written_M_content_reads_back_correctly(self): TEST_MESSAGE = "Hello, memory" # Start up the stub and start/prep the inferior. procs = self.prep_debug_monitor_and_inferior( inferior_args=[ "set-message:xxxxxxxxxxxxxX", "get-data-address-hex:g_message", "sleep:1", "print-message:"]) self.test_sequence.add_log_lines( [ # Start running after initial stop. "read packet: $c#63", # Match output line that prints the memory address of the message buffer within the inferior. # Note we require launch-only testing so we can get inferior otuput. {"type": "output_match", "regex": self.maybe_strict_output_regex(r"data address: 0x([0-9a-fA-F]+)\r\n"), "capture": {1: "message_address"}}, # Now stop the inferior. "read packet: {}".format(chr(3)), # And wait for the stop notification. {"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "stop_signo", 2: "stop_thread_id"}}], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Grab the message address. self.assertIsNotNone(context.get("message_address")) message_address = int(context.get("message_address"), 16) # Hex-encode the test message, adding null termination. hex_encoded_message = TEST_MESSAGE.encode("hex") # Write the message to the inferior. Verify that we can read it with the hex-encoded (m) # and binary (x) memory read packets. self.reset_test_sequence() self.test_sequence.add_log_lines( ["read packet: $M{0:x},{1:x}:{2}#00".format(message_address, len(TEST_MESSAGE), hex_encoded_message), "send packet: $OK#00", "read packet: $m{0:x},{1:x}#00".format(message_address, len(TEST_MESSAGE)), "send packet: ${0}#00".format(hex_encoded_message), "read packet: $x{0:x},{1:x}#00".format(message_address, len(TEST_MESSAGE)), "send packet: ${0}#00".format(TEST_MESSAGE), "read packet: $m{0:x},4#00".format(message_address), "send packet: ${0}#00".format(hex_encoded_message[0:8]), "read packet: $x{0:x},4#00".format(message_address), "send packet: ${0}#00".format(TEST_MESSAGE[0:4]), "read packet: $c#63", {"type": "output_match", "regex": r"^message: (.+)\r\n$", "capture": {1: "printed_message"}}, "send packet: $W00#00", ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Ensure what we read from inferior memory is what we wrote. printed_message = context.get("printed_message") self.assertIsNotNone(printed_message) self.assertEqual(printed_message, TEST_MESSAGE + "X") @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_written_M_content_reads_back_correctly_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.written_M_content_reads_back_correctly() @llgs_test @expectedFlakeyLinux("llvm.org/pr25652") def test_written_M_content_reads_back_correctly_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.written_M_content_reads_back_correctly() def P_writes_all_gpr_registers(self): # Start inferior debug session, grab all register info. procs = self.prep_debug_monitor_and_inferior(inferior_args=["sleep:2"]) self.add_register_info_collection_packets() self.add_process_info_collection_packets() context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Process register infos. reg_infos = self.parse_register_info_packets(context) self.assertIsNotNone(reg_infos) self.add_lldb_register_index(reg_infos) # Process endian. process_info = self.parse_process_info_response(context) endian = process_info.get("endian") self.assertIsNotNone(endian) # Pull out the register infos that we think we can bit flip # successfully,. gpr_reg_infos = [ reg_info for reg_info in reg_infos if self.is_bit_flippable_register(reg_info)] self.assertTrue(len(gpr_reg_infos) > 0) # Write flipped bit pattern of existing value to each register. (successful_writes, failed_writes) = self.flip_all_bits_in_each_register_value( gpr_reg_infos, endian) # print("successful writes: {}, failed writes: {}".format(successful_writes, failed_writes)) self.assertTrue(successful_writes > 0) # Note: as of this moment, a hefty number of the GPR writes are failing with E32 (everything except rax-rdx, rdi, rsi, rbp). # Come back to this. I have the test rigged to verify that at least some # of the bit-flip writes work. @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_P_writes_all_gpr_registers_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.P_writes_all_gpr_registers() @llgs_test def test_P_writes_all_gpr_registers_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.P_writes_all_gpr_registers() def P_and_p_thread_suffix_work(self): # Startup the inferior with three threads. procs = self.prep_debug_monitor_and_inferior( inferior_args=["thread:new", "thread:new"]) self.add_thread_suffix_request_packets() self.add_register_info_collection_packets() self.add_process_info_collection_packets() context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) process_info = self.parse_process_info_response(context) self.assertIsNotNone(process_info) endian = process_info.get("endian") self.assertIsNotNone(endian) reg_infos = self.parse_register_info_packets(context) self.assertIsNotNone(reg_infos) self.add_lldb_register_index(reg_infos) reg_index = self.select_modifiable_register(reg_infos) self.assertIsNotNone(reg_index) reg_byte_size = int(reg_infos[reg_index]["bitsize"]) / 8 self.assertTrue(reg_byte_size > 0) # Run the process a bit so threads can start up, and collect register # info. context = self.run_process_then_stop(run_seconds=1) self.assertIsNotNone(context) # Wait for 3 threads to be present. threads = self.wait_for_thread_count(3, timeout_seconds=5) self.assertEqual(len(threads), 3) expected_reg_values = [] register_increment = 1 next_value = None # Set the same register in each of 3 threads to a different value. # Verify each one has the unique value. for thread in threads: # If we don't have a next value yet, start it with the initial read # value + 1 if not next_value: # Read pre-existing register value. self.reset_test_sequence() self.test_sequence.add_log_lines( ["read packet: $p{0:x};thread:{1:x}#00".format(reg_index, thread), {"direction": "send", "regex": r"^\$([0-9a-fA-F]+)#", "capture": {1: "p_response"}}, ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Set the next value to use for writing as the increment plus # current value. p_response = context.get("p_response") self.assertIsNotNone(p_response) next_value = lldbgdbserverutils.unpack_register_hex_unsigned( endian, p_response) # Set new value using P and thread suffix. self.reset_test_sequence() self.test_sequence.add_log_lines( [ "read packet: $P{0:x}={1};thread:{2:x}#00".format( reg_index, lldbgdbserverutils.pack_register_hex( endian, next_value, byte_size=reg_byte_size), thread), "send packet: $OK#00", ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Save the value we set. expected_reg_values.append(next_value) # Increment value for next thread to use (we want them all # different so we can verify they wrote to each thread correctly # next.) next_value += register_increment # Revisit each thread and verify they have the expected value set for # the register we wrote. thread_index = 0 for thread in threads: # Read pre-existing register value. self.reset_test_sequence() self.test_sequence.add_log_lines( ["read packet: $p{0:x};thread:{1:x}#00".format(reg_index, thread), {"direction": "send", "regex": r"^\$([0-9a-fA-F]+)#", "capture": {1: "p_response"}}, ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Get the register value. p_response = context.get("p_response") self.assertIsNotNone(p_response) read_value = lldbgdbserverutils.unpack_register_hex_unsigned( endian, p_response) # Make sure we read back what we wrote. self.assertEqual(read_value, expected_reg_values[thread_index]) thread_index += 1 # Note: as of this moment, a hefty number of the GPR writes are failing # with E32 (everything except rax-rdx, rdi, rsi, rbp). @debugserver_test @skipIfDarwinEmbedded # lldb-server tests not updated to work on ios etc yet def test_P_and_p_thread_suffix_work_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.P_and_p_thread_suffix_work() @llgs_test def test_P_and_p_thread_suffix_work_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.P_and_p_thread_suffix_work() Index: vendor/lldb/dist/source/Commands/CommandCompletions.cpp =================================================================== --- vendor/lldb/dist/source/Commands/CommandCompletions.cpp (revision 337146) +++ vendor/lldb/dist/source/Commands/CommandCompletions.cpp (revision 337147) @@ -1,529 +1,537 @@ //===-- CommandCompletions.cpp ----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes #include #if defined(__APPLE__) || defined(__linux__) #include #endif // C++ Includes // Other libraries and framework includes #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSet.h" // Project includes #include "lldb/Core/FileSpecList.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Host/FileSystem.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Variable.h" #include "lldb/Target/Target.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/TildeExpressionResolver.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" using namespace lldb_private; CommandCompletions::CommonCompletionElement CommandCompletions::g_common_completions[] = { {eCustomCompletion, nullptr}, {eSourceFileCompletion, CommandCompletions::SourceFiles}, {eDiskFileCompletion, CommandCompletions::DiskFiles}, {eDiskDirectoryCompletion, CommandCompletions::DiskDirectories}, {eSymbolCompletion, CommandCompletions::Symbols}, {eModuleCompletion, CommandCompletions::Modules}, {eSettingsNameCompletion, CommandCompletions::SettingsNames}, {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames}, {eArchitectureCompletion, CommandCompletions::ArchitectureNames}, {eVariablePathCompletion, CommandCompletions::VariablePath}, {eNoCompletion, nullptr} // This one has to be last in the list. }; bool CommandCompletions::InvokeCommonCompletionCallbacks( CommandInterpreter &interpreter, uint32_t completion_mask, CompletionRequest &request, SearchFilter *searcher) { bool handled = false; if (completion_mask & eCustomCompletion) return false; for (int i = 0;; i++) { if (g_common_completions[i].type == eNoCompletion) break; else if ((g_common_completions[i].type & completion_mask) == g_common_completions[i].type && g_common_completions[i].callback != nullptr) { handled = true; g_common_completions[i].callback(interpreter, request, searcher); } } return handled; } int CommandCompletions::SourceFiles(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { request.SetWordComplete(true); // Find some way to switch "include support files..." SourceFileCompleter completer(interpreter, false, request); if (searcher == nullptr) { lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); SearchFilterForUnconstrainedSearches null_searcher(target_sp); completer.DoCompletion(&null_searcher); } else { completer.DoCompletion(searcher); } - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } static int DiskFilesOrDirectories(const llvm::Twine &partial_name, bool only_directories, StringList &matches, TildeExpressionResolver &Resolver) { matches.Clear(); llvm::SmallString<256> CompletionBuffer; llvm::SmallString<256> Storage; partial_name.toVector(CompletionBuffer); if (CompletionBuffer.size() >= PATH_MAX) - return 0; + return matches.GetSize(); namespace fs = llvm::sys::fs; namespace path = llvm::sys::path; llvm::StringRef SearchDir; llvm::StringRef PartialItem; if (CompletionBuffer.startswith("~")) { llvm::StringRef Buffer(CompletionBuffer); size_t FirstSep = Buffer.find_if([](char c) { return path::is_separator(c); }); llvm::StringRef Username = Buffer.take_front(FirstSep); llvm::StringRef Remainder; if (FirstSep != llvm::StringRef::npos) Remainder = Buffer.drop_front(FirstSep + 1); llvm::SmallString Resolved; if (!Resolver.ResolveExact(Username, Resolved)) { // We couldn't resolve it as a full username. If there were no slashes // then this might be a partial username. We try to resolve it as such // but after that, we're done regardless of any matches. if (FirstSep == llvm::StringRef::npos) { llvm::StringSet<> MatchSet; Resolver.ResolvePartial(Username, MatchSet); for (const auto &S : MatchSet) { Resolved = S.getKey(); path::append(Resolved, path::get_separator()); matches.AppendString(Resolved); } } return matches.GetSize(); } // If there was no trailing slash, then we're done as soon as we resolve // the expression to the correct directory. Otherwise we need to continue // looking for matches within that directory. if (FirstSep == llvm::StringRef::npos) { // Make sure it ends with a separator. path::append(CompletionBuffer, path::get_separator()); matches.AppendString(CompletionBuffer); - return 1; + return matches.GetSize(); } // We want to keep the form the user typed, so we special case this to // search in the fully resolved directory, but CompletionBuffer keeps the // unmodified form that the user typed. Storage = Resolved; llvm::StringRef RemainderDir = path::parent_path(Remainder); if (!RemainderDir.empty()) { // Append the remaining path to the resolved directory. Storage.append(path::get_separator()); Storage.append(RemainderDir); } SearchDir = Storage; } else { SearchDir = path::parent_path(CompletionBuffer); } size_t FullPrefixLen = CompletionBuffer.size(); PartialItem = path::filename(CompletionBuffer); if (PartialItem == ".") PartialItem = llvm::StringRef(); if (SearchDir.empty()) { llvm::sys::fs::current_path(Storage); SearchDir = Storage; } assert(!PartialItem.contains(path::get_separator())); // SearchDir now contains the directory to search in, and Prefix contains the // text we want to match against items in that directory. std::error_code EC; fs::directory_iterator Iter(SearchDir, EC, false); fs::directory_iterator End; for (; Iter != End && !EC; Iter.increment(EC)) { auto &Entry = *Iter; auto Name = path::filename(Entry.path()); // Omit ".", ".." if (Name == "." || Name == ".." || !Name.startswith(PartialItem)) continue; // We have a match. llvm::ErrorOr st = Entry.status(); if (!st) continue; // If it's a symlink, then we treat it as a directory as long as the target // is a directory. bool is_dir = fs::is_directory(*st); if (fs::is_symlink_file(*st)) { fs::file_status target_st; if (!fs::status(Entry.path(), target_st)) is_dir = fs::is_directory(target_st); } if (only_directories && !is_dir) continue; // Shrink it back down so that it just has the original prefix the user // typed and remove the part of the name which is common to the located // item and what the user typed. CompletionBuffer.resize(FullPrefixLen); Name = Name.drop_front(PartialItem.size()); CompletionBuffer.append(Name); if (is_dir) { path::append(CompletionBuffer, path::get_separator()); } matches.AppendString(CompletionBuffer); } return matches.GetSize(); } +static int DiskFilesOrDirectories(CompletionRequest &request, + bool only_directories) { + request.SetWordComplete(false); + StandardTildeExpressionResolver resolver; + StringList matches; + DiskFilesOrDirectories(request.GetCursorArgumentPrefix(), only_directories, + matches, resolver); + request.AddCompletions(matches); + return request.GetNumberOfMatches(); +} + int CommandCompletions::DiskFiles(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { - request.SetWordComplete(false); - StandardTildeExpressionResolver Resolver; - return DiskFiles(request.GetCursorArgumentPrefix(), request.GetMatches(), - Resolver); + return DiskFilesOrDirectories(request, /*only_dirs*/ false); } int CommandCompletions::DiskFiles(const llvm::Twine &partial_file_name, StringList &matches, TildeExpressionResolver &Resolver) { return DiskFilesOrDirectories(partial_file_name, false, matches, Resolver); } int CommandCompletions::DiskDirectories(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { - request.SetWordComplete(false); - StandardTildeExpressionResolver Resolver; - return DiskDirectories(request.GetCursorArgumentPrefix(), - request.GetMatches(), Resolver); + return DiskFilesOrDirectories(request, /*only_dirs*/ true); } int CommandCompletions::DiskDirectories(const llvm::Twine &partial_file_name, StringList &matches, TildeExpressionResolver &Resolver) { return DiskFilesOrDirectories(partial_file_name, true, matches, Resolver); } int CommandCompletions::Modules(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { request.SetWordComplete(true); ModuleCompleter completer(interpreter, request); if (searcher == nullptr) { lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); SearchFilterForUnconstrainedSearches null_searcher(target_sp); completer.DoCompletion(&null_searcher); } else { completer.DoCompletion(searcher); } - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } int CommandCompletions::Symbols(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { request.SetWordComplete(true); SymbolCompleter completer(interpreter, request); if (searcher == nullptr) { lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); SearchFilterForUnconstrainedSearches null_searcher(target_sp); completer.DoCompletion(&null_searcher); } else { completer.DoCompletion(searcher); } - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } int CommandCompletions::SettingsNames(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { // Cache the full setting name list static StringList g_property_names; if (g_property_names.GetSize() == 0) { // Generate the full setting name list on demand lldb::OptionValuePropertiesSP properties_sp( interpreter.GetDebugger().GetValueProperties()); if (properties_sp) { StreamString strm; properties_sp->DumpValue(nullptr, strm, OptionValue::eDumpOptionName); const std::string &str = strm.GetString(); g_property_names.SplitIntoLines(str.c_str(), str.size()); } } size_t exact_matches_idx = SIZE_MAX; - const size_t num_matches = - g_property_names.AutoComplete(request.GetCursorArgumentPrefix(), - request.GetMatches(), exact_matches_idx); + StringList matches; + g_property_names.AutoComplete(request.GetCursorArgumentPrefix(), matches, + exact_matches_idx); request.SetWordComplete(exact_matches_idx != SIZE_MAX); - return num_matches; + request.AddCompletions(matches); + return request.GetNumberOfMatches(); } int CommandCompletions::PlatformPluginNames(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { - const uint32_t num_matches = PluginManager::AutoCompletePlatformName( - request.GetCursorArgumentPrefix(), request.GetMatches()); + StringList new_matches; + std::size_t num_matches = PluginManager::AutoCompletePlatformName( + request.GetCursorArgumentPrefix(), new_matches); request.SetWordComplete(num_matches == 1); - return num_matches; + request.AddCompletions(new_matches); + return request.GetNumberOfMatches(); } int CommandCompletions::ArchitectureNames(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { const uint32_t num_matches = ArchSpec::AutoComplete(request); request.SetWordComplete(num_matches == 1); return num_matches; } int CommandCompletions::VariablePath(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { return Variable::AutoComplete(interpreter.GetExecutionContext(), request); } CommandCompletions::Completer::Completer(CommandInterpreter &interpreter, CompletionRequest &request) : m_interpreter(interpreter), m_request(request) {} CommandCompletions::Completer::~Completer() = default; //---------------------------------------------------------------------- // SourceFileCompleter //---------------------------------------------------------------------- CommandCompletions::SourceFileCompleter::SourceFileCompleter( CommandInterpreter &interpreter, bool include_support_files, CompletionRequest &request) : CommandCompletions::Completer(interpreter, request), m_include_support_files(include_support_files), m_matching_files() { FileSpec partial_spec(m_request.GetCursorArgumentPrefix(), false); m_file_name = partial_spec.GetFilename().GetCString(); m_dir_name = partial_spec.GetDirectory().GetCString(); } Searcher::Depth CommandCompletions::SourceFileCompleter::GetDepth() { return eDepthCompUnit; } Searcher::CallbackReturn CommandCompletions::SourceFileCompleter::SearchCallback(SearchFilter &filter, SymbolContext &context, Address *addr, bool complete) { if (context.comp_unit != nullptr) { if (m_include_support_files) { FileSpecList supporting_files = context.comp_unit->GetSupportFiles(); for (size_t sfiles = 0; sfiles < supporting_files.GetSize(); sfiles++) { const FileSpec &sfile_spec = supporting_files.GetFileSpecAtIndex(sfiles); const char *sfile_file_name = sfile_spec.GetFilename().GetCString(); const char *sfile_dir_name = sfile_spec.GetFilename().GetCString(); bool match = false; if (m_file_name && sfile_file_name && strstr(sfile_file_name, m_file_name) == sfile_file_name) match = true; if (match && m_dir_name && sfile_dir_name && strstr(sfile_dir_name, m_dir_name) != sfile_dir_name) match = false; if (match) { m_matching_files.AppendIfUnique(sfile_spec); } } } else { const char *cur_file_name = context.comp_unit->GetFilename().GetCString(); const char *cur_dir_name = context.comp_unit->GetDirectory().GetCString(); bool match = false; if (m_file_name && cur_file_name && strstr(cur_file_name, m_file_name) == cur_file_name) match = true; if (match && m_dir_name && cur_dir_name && strstr(cur_dir_name, m_dir_name) != cur_dir_name) match = false; if (match) { m_matching_files.AppendIfUnique(context.comp_unit); } } } return Searcher::eCallbackReturnContinue; } size_t CommandCompletions::SourceFileCompleter::DoCompletion(SearchFilter *filter) { filter->Search(*this); // Now convert the filelist to completions: for (size_t i = 0; i < m_matching_files.GetSize(); i++) { - m_request.GetMatches().AppendString( + m_request.AddCompletion( m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString()); } - return m_request.GetMatches().GetSize(); + return m_request.GetNumberOfMatches(); } //---------------------------------------------------------------------- // SymbolCompleter //---------------------------------------------------------------------- static bool regex_chars(const char comp) { return (comp == '[' || comp == ']' || comp == '(' || comp == ')' || comp == '{' || comp == '}' || comp == '+' || comp == '.' || comp == '*' || comp == '|' || comp == '^' || comp == '$' || comp == '\\' || comp == '?'); } CommandCompletions::SymbolCompleter::SymbolCompleter( CommandInterpreter &interpreter, CompletionRequest &request) : CommandCompletions::Completer(interpreter, request) { std::string regex_str; if (!m_request.GetCursorArgumentPrefix().empty()) { regex_str.append("^"); regex_str.append(m_request.GetCursorArgumentPrefix()); } else { // Match anything since the completion string is empty regex_str.append("."); } std::string::iterator pos = find_if(regex_str.begin() + 1, regex_str.end(), regex_chars); while (pos < regex_str.end()) { pos = regex_str.insert(pos, '\\'); pos = find_if(pos + 2, regex_str.end(), regex_chars); } m_regex.Compile(regex_str); } Searcher::Depth CommandCompletions::SymbolCompleter::GetDepth() { return eDepthModule; } Searcher::CallbackReturn CommandCompletions::SymbolCompleter::SearchCallback( SearchFilter &filter, SymbolContext &context, Address *addr, bool complete) { if (context.module_sp) { SymbolContextList sc_list; const bool include_symbols = true; const bool include_inlines = true; const bool append = true; context.module_sp->FindFunctions(m_regex, include_symbols, include_inlines, append, sc_list); SymbolContext sc; // Now add the functions & symbols to the list - only add if unique: for (uint32_t i = 0; i < sc_list.GetSize(); i++) { if (sc_list.GetContextAtIndex(i, sc)) { ConstString func_name = sc.GetFunctionName(Mangled::ePreferDemangled); if (!func_name.IsEmpty()) m_match_set.insert(func_name); } } } return Searcher::eCallbackReturnContinue; } size_t CommandCompletions::SymbolCompleter::DoCompletion(SearchFilter *filter) { filter->Search(*this); collection::iterator pos = m_match_set.begin(), end = m_match_set.end(); for (pos = m_match_set.begin(); pos != end; pos++) - m_request.GetMatches().AppendString((*pos).GetCString()); + m_request.AddCompletion((*pos).GetCString()); - return m_request.GetMatches().GetSize(); + return m_request.GetNumberOfMatches(); } //---------------------------------------------------------------------- // ModuleCompleter //---------------------------------------------------------------------- CommandCompletions::ModuleCompleter::ModuleCompleter( CommandInterpreter &interpreter, CompletionRequest &request) : CommandCompletions::Completer(interpreter, request) { FileSpec partial_spec(m_request.GetCursorArgumentPrefix(), false); m_file_name = partial_spec.GetFilename().GetCString(); m_dir_name = partial_spec.GetDirectory().GetCString(); } Searcher::Depth CommandCompletions::ModuleCompleter::GetDepth() { return eDepthModule; } Searcher::CallbackReturn CommandCompletions::ModuleCompleter::SearchCallback( SearchFilter &filter, SymbolContext &context, Address *addr, bool complete) { if (context.module_sp) { const char *cur_file_name = context.module_sp->GetFileSpec().GetFilename().GetCString(); const char *cur_dir_name = context.module_sp->GetFileSpec().GetDirectory().GetCString(); bool match = false; if (m_file_name && cur_file_name && strstr(cur_file_name, m_file_name) == cur_file_name) match = true; if (match && m_dir_name && cur_dir_name && strstr(cur_dir_name, m_dir_name) != cur_dir_name) match = false; if (match) { - m_request.GetMatches().AppendString(cur_file_name); + m_request.AddCompletion(cur_file_name); } } return Searcher::eCallbackReturnContinue; } size_t CommandCompletions::ModuleCompleter::DoCompletion(SearchFilter *filter) { filter->Search(*this); - return m_request.GetMatches().GetSize(); + return m_request.GetNumberOfMatches(); } Index: vendor/lldb/dist/source/Commands/CommandObjectCommands.cpp =================================================================== --- vendor/lldb/dist/source/Commands/CommandObjectCommands.cpp (revision 337146) +++ vendor/lldb/dist/source/Commands/CommandObjectCommands.cpp (revision 337147) @@ -1,1917 +1,1917 @@ //===-- CommandObjectCommands.cpp -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes // Other libraries and framework includes #include "llvm/ADT/StringRef.h" // Project includes #include "CommandObjectCommands.h" #include "CommandObjectHelp.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/IOHandler.h" #include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/CommandHistory.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObjectRegexCommand.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Interpreter/OptionValueBoolean.h" #include "lldb/Interpreter/OptionValueString.h" #include "lldb/Interpreter/OptionValueUInt64.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/StringList.h" using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------------- // CommandObjectCommandsSource //------------------------------------------------------------------------- static OptionDefinition g_history_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger, "How many history commands to print." }, { LLDB_OPT_SET_1, false, "start-index", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger, "Index at which to start printing history commands (or end to mean tail mode)." }, { LLDB_OPT_SET_1, false, "end-index", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger, "Index at which to stop printing history commands." }, { LLDB_OPT_SET_2, false, "clear", 'C', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Clears the current command history." }, // clang-format on }; class CommandObjectCommandsHistory : public CommandObjectParsed { public: CommandObjectCommandsHistory(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "command history", "Dump the history of commands in this session.\n" "Commands in the history list can be run again " "using \"!\". \"!-\" will re-run " "the command that is commands from the end" " of the list (counting the current command).", nullptr), m_options() {} ~CommandObjectCommandsHistory() override = default; Options *GetOptions() override { return &m_options; } protected: class CommandOptions : public Options { public: CommandOptions() : Options(), m_start_idx(0), m_stop_idx(0), m_count(0), m_clear(false) { } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'c': error = m_count.SetValueFromString(option_arg, eVarSetOperationAssign); break; case 's': if (option_arg == "end") { m_start_idx.SetCurrentValue(UINT64_MAX); m_start_idx.SetOptionWasSet(); } else error = m_start_idx.SetValueFromString(option_arg, eVarSetOperationAssign); break; case 'e': error = m_stop_idx.SetValueFromString(option_arg, eVarSetOperationAssign); break; case 'C': m_clear.SetCurrentValue(true); m_clear.SetOptionWasSet(); break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_start_idx.Clear(); m_stop_idx.Clear(); m_count.Clear(); m_clear.Clear(); } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_history_options); } // Instance variables to hold the values for command options. OptionValueUInt64 m_start_idx; OptionValueUInt64 m_stop_idx; OptionValueUInt64 m_count; OptionValueBoolean m_clear; }; bool DoExecute(Args &command, CommandReturnObject &result) override { if (m_options.m_clear.GetCurrentValue() && m_options.m_clear.OptionWasSet()) { m_interpreter.GetCommandHistory().Clear(); result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); } else { if (m_options.m_start_idx.OptionWasSet() && m_options.m_stop_idx.OptionWasSet() && m_options.m_count.OptionWasSet()) { result.AppendError("--count, --start-index and --end-index cannot be " "all specified in the same invocation"); result.SetStatus(lldb::eReturnStatusFailed); } else { std::pair start_idx( m_options.m_start_idx.OptionWasSet(), m_options.m_start_idx.GetCurrentValue()); std::pair stop_idx( m_options.m_stop_idx.OptionWasSet(), m_options.m_stop_idx.GetCurrentValue()); std::pair count(m_options.m_count.OptionWasSet(), m_options.m_count.GetCurrentValue()); const CommandHistory &history(m_interpreter.GetCommandHistory()); if (start_idx.first && start_idx.second == UINT64_MAX) { if (count.first) { start_idx.second = history.GetSize() - count.second; stop_idx.second = history.GetSize() - 1; } else if (stop_idx.first) { start_idx.second = stop_idx.second; stop_idx.second = history.GetSize() - 1; } else { start_idx.second = 0; stop_idx.second = history.GetSize() - 1; } } else { if (!start_idx.first && !stop_idx.first && !count.first) { start_idx.second = 0; stop_idx.second = history.GetSize() - 1; } else if (start_idx.first) { if (count.first) { stop_idx.second = start_idx.second + count.second - 1; } else if (!stop_idx.first) { stop_idx.second = history.GetSize() - 1; } } else if (stop_idx.first) { if (count.first) { if (stop_idx.second >= count.second) start_idx.second = stop_idx.second - count.second + 1; else start_idx.second = 0; } } else /* if (count.first) */ { start_idx.second = 0; stop_idx.second = count.second - 1; } } history.Dump(result.GetOutputStream(), start_idx.second, stop_idx.second); } } return result.Succeeded(); } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectCommandsSource //------------------------------------------------------------------------- static OptionDefinition g_source_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "stop-on-error", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "If true, stop executing commands on error." }, { LLDB_OPT_SET_ALL, false, "stop-on-continue", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "If true, stop executing commands on continue." }, { LLDB_OPT_SET_ALL, false, "silent-run", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "If true don't echo commands while executing." }, // clang-format on }; class CommandObjectCommandsSource : public CommandObjectParsed { public: CommandObjectCommandsSource(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "command source", "Read and execute LLDB commands from the file .", nullptr), m_options() { CommandArgumentEntry arg; CommandArgumentData file_arg; // Define the first (and only) variant of this arg. file_arg.arg_type = eArgTypeFilename; file_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(file_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectCommandsSource() override = default; const char *GetRepeatCommand(Args ¤t_command_args, uint32_t index) override { return ""; } int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } Options *GetOptions() override { return &m_options; } protected: class CommandOptions : public Options { public: CommandOptions() : Options(), m_stop_on_error(true), m_silent_run(false), m_stop_on_continue(true) {} ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'e': error = m_stop_on_error.SetValueFromString(option_arg); break; case 'c': error = m_stop_on_continue.SetValueFromString(option_arg); break; case 's': error = m_silent_run.SetValueFromString(option_arg); break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_stop_on_error.Clear(); m_silent_run.Clear(); m_stop_on_continue.Clear(); } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_source_options); } // Instance variables to hold the values for command options. OptionValueBoolean m_stop_on_error; OptionValueBoolean m_silent_run; OptionValueBoolean m_stop_on_continue; }; bool DoExecute(Args &command, CommandReturnObject &result) override { if (command.GetArgumentCount() != 1) { result.AppendErrorWithFormat( "'%s' takes exactly one executable filename argument.\n", GetCommandName().str().c_str()); result.SetStatus(eReturnStatusFailed); return false; } FileSpec cmd_file(command[0].ref, true); ExecutionContext *exe_ctx = nullptr; // Just use the default context. // If any options were set, then use them if (m_options.m_stop_on_error.OptionWasSet() || m_options.m_silent_run.OptionWasSet() || m_options.m_stop_on_continue.OptionWasSet()) { // Use user set settings CommandInterpreterRunOptions options; options.SetStopOnContinue(m_options.m_stop_on_continue.GetCurrentValue()); options.SetStopOnError(m_options.m_stop_on_error.GetCurrentValue()); options.SetEchoCommands(!m_options.m_silent_run.GetCurrentValue()); options.SetPrintResults(!m_options.m_silent_run.GetCurrentValue()); m_interpreter.HandleCommandsFromFile(cmd_file, exe_ctx, options, result); } else { // No options were set, inherit any settings from nested "command source" // commands, or set to sane default settings... CommandInterpreterRunOptions options; m_interpreter.HandleCommandsFromFile(cmd_file, exe_ctx, options, result); } return result.Succeeded(); } CommandOptions m_options; }; #pragma mark CommandObjectCommandsAlias //------------------------------------------------------------------------- // CommandObjectCommandsAlias //------------------------------------------------------------------------- static OptionDefinition g_alias_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "help", 'h', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeHelpText, "Help text for this command" }, { LLDB_OPT_SET_ALL, false, "long-help", 'H', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeHelpText, "Long help text for this command" }, // clang-format on }; static const char *g_python_command_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" "You must define a Python function with this signature:\n" "def my_command_impl(debugger, args, result, internal_dict):\n"; class CommandObjectCommandsAlias : public CommandObjectRaw { protected: class CommandOptions : public OptionGroup { public: CommandOptions() : OptionGroup(), m_help(), m_long_help() {} ~CommandOptions() override = default; llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_alias_options); } Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, ExecutionContext *execution_context) override { Status error; const int short_option = GetDefinitions()[option_idx].short_option; std::string option_str(option_value); switch (short_option) { case 'h': m_help.SetCurrentValue(option_str); m_help.SetOptionWasSet(); break; case 'H': m_long_help.SetCurrentValue(option_str); m_long_help.SetOptionWasSet(); break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_help.Clear(); m_long_help.Clear(); } OptionValueString m_help; OptionValueString m_long_help; }; OptionGroupOptions m_option_group; CommandOptions m_command_options; public: Options *GetOptions() override { return &m_option_group; } CommandObjectCommandsAlias(CommandInterpreter &interpreter) : CommandObjectRaw( interpreter, "command alias", "Define a custom command in terms of an existing command."), m_option_group(), m_command_options() { m_option_group.Append(&m_command_options); m_option_group.Finalize(); SetHelpLong( "'alias' allows the user to create a short-cut or abbreviation for long \ commands, multi-word commands, and commands that take particular options. \ Below are some simple examples of how one might use the 'alias' command:" R"( (lldb) command alias sc script Creates the abbreviation 'sc' for the 'script' command. (lldb) command alias bp breakpoint )" " Creates the abbreviation 'bp' for the 'breakpoint' command. Since \ breakpoint commands are two-word commands, the user would still need to \ enter the second word after 'bp', e.g. 'bp enable' or 'bp delete'." R"( (lldb) command alias bpl breakpoint list Creates the abbreviation 'bpl' for the two-word command 'breakpoint list'. )" "An alias can include some options for the command, with the values either \ filled in at the time the alias is created, or specified as positional \ arguments, to be filled in when the alias is invoked. The following example \ shows how to create aliases with options:" R"( (lldb) command alias bfl breakpoint set -f %1 -l %2 )" " Creates the abbreviation 'bfl' (for break-file-line), with the -f and -l \ options already part of the alias. So if the user wants to set a breakpoint \ by file and line without explicitly having to use the -f and -l options, the \ user can now use 'bfl' instead. The '%1' and '%2' are positional placeholders \ for the actual arguments that will be passed when the alias command is used. \ The number in the placeholder refers to the position/order the actual value \ occupies when the alias is used. All the occurrences of '%1' in the alias \ will be replaced with the first argument, all the occurrences of '%2' in the \ alias will be replaced with the second argument, and so on. This also allows \ actual arguments to be used multiple times within an alias (see 'process \ launch' example below)." R"( )" "Note: the positional arguments must substitute as whole words in the resultant \ command, so you can't at present do something like this to append the file extension \ \".cpp\":" R"( (lldb) command alias bcppfl breakpoint set -f %1.cpp -l %2 )" "For more complex aliasing, use the \"command regex\" command instead. In the \ 'bfl' case above, the actual file value will be filled in with the first argument \ following 'bfl' and the actual line number value will be filled in with the second \ argument. The user would use this alias as follows:" R"( (lldb) command alias bfl breakpoint set -f %1 -l %2 (lldb) bfl my-file.c 137 This would be the same as if the user had entered 'breakpoint set -f my-file.c -l 137'. Another example: (lldb) command alias pltty process launch -s -o %1 -e %1 (lldb) pltty /dev/tty0 Interpreted as 'process launch -s -o /dev/tty0 -e /dev/tty0' )" "If the user always wanted to pass the same value to a particular option, the \ alias could be defined with that value directly in the alias as a constant, \ rather than using a positional placeholder:" R"( (lldb) command alias bl3 breakpoint set -f %1 -l 3 Always sets a breakpoint on line 3 of whatever file is indicated.)"); CommandArgumentEntry arg1; CommandArgumentEntry arg2; CommandArgumentEntry arg3; CommandArgumentData alias_arg; CommandArgumentData cmd_arg; CommandArgumentData options_arg; // Define the first (and only) variant of this arg. alias_arg.arg_type = eArgTypeAliasName; alias_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(alias_arg); // Define the first (and only) variant of this arg. cmd_arg.arg_type = eArgTypeCommandName; cmd_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg2.push_back(cmd_arg); // Define the first (and only) variant of this arg. options_arg.arg_type = eArgTypeAliasOptions; options_arg.arg_repetition = eArgRepeatOptional; // There is only one variant this argument could be; put it into the // argument entry. arg3.push_back(options_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); m_arguments.push_back(arg2); m_arguments.push_back(arg3); } ~CommandObjectCommandsAlias() override = default; protected: bool DoExecute(llvm::StringRef raw_command_line, CommandReturnObject &result) override { if (raw_command_line.empty()) { result.AppendError("'command alias' requires at least two arguments"); return false; } ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext(); m_option_group.NotifyOptionParsingStarting(&exe_ctx); OptionsWithRaw args_with_suffix(raw_command_line); const char *remainder = args_with_suffix.GetRawPart().c_str(); if (args_with_suffix.HasArgs()) if (!ParseOptionsAndNotify(args_with_suffix.GetArgs(), result, m_option_group, exe_ctx)) return false; llvm::StringRef raw_command_string(remainder); Args args(raw_command_string); if (args.GetArgumentCount() < 2) { result.AppendError("'command alias' requires at least two arguments"); result.SetStatus(eReturnStatusFailed); return false; } // Get the alias command. auto alias_command = args[0].ref; if (alias_command.startswith("-")) { result.AppendError("aliases starting with a dash are not supported"); if (alias_command == "--help" || alias_command == "--long-help") { result.AppendWarning("if trying to pass options to 'command alias' add " "a -- at the end of the options"); } result.SetStatus(eReturnStatusFailed); return false; } // Strip the new alias name off 'raw_command_string' (leave it on args, // which gets passed to 'Execute', which does the stripping itself. size_t pos = raw_command_string.find(alias_command); if (pos == 0) { raw_command_string = raw_command_string.substr(alias_command.size()); pos = raw_command_string.find_first_not_of(' '); if ((pos != std::string::npos) && (pos > 0)) raw_command_string = raw_command_string.substr(pos); } else { result.AppendError("Error parsing command string. No alias created."); result.SetStatus(eReturnStatusFailed); return false; } // Verify that the command is alias-able. if (m_interpreter.CommandExists(alias_command)) { result.AppendErrorWithFormat( "'%s' is a permanent debugger command and cannot be redefined.\n", args[0].c_str()); result.SetStatus(eReturnStatusFailed); return false; } // Get CommandObject that is being aliased. The command name is read from // the front of raw_command_string. raw_command_string is returned with the // name of the command object stripped off the front. llvm::StringRef original_raw_command_string = raw_command_string; CommandObject *cmd_obj = m_interpreter.GetCommandObjectForCommand(raw_command_string); if (!cmd_obj) { result.AppendErrorWithFormat("invalid command given to 'command alias'. " "'%s' does not begin with a valid command." " No alias created.", original_raw_command_string.str().c_str()); result.SetStatus(eReturnStatusFailed); return false; } else if (!cmd_obj->WantsRawCommandString()) { // Note that args was initialized with the original command, and has not // been updated to this point. Therefore can we pass it to the version of // Execute that does not need/expect raw input in the alias. return HandleAliasingNormalCommand(args, result); } else { return HandleAliasingRawCommand(alias_command, raw_command_string, *cmd_obj, result); } return result.Succeeded(); } bool HandleAliasingRawCommand(llvm::StringRef alias_command, llvm::StringRef raw_command_string, CommandObject &cmd_obj, CommandReturnObject &result) { // Verify & handle any options/arguments passed to the alias command OptionArgVectorSP option_arg_vector_sp = OptionArgVectorSP(new OptionArgVector); if (CommandObjectSP cmd_obj_sp = m_interpreter.GetCommandSPExact(cmd_obj.GetCommandName(), false)) { if (m_interpreter.AliasExists(alias_command) || m_interpreter.UserCommandExists(alias_command)) { result.AppendWarningWithFormat( "Overwriting existing definition for '%s'.\n", alias_command.str().c_str()); } if (CommandAlias *alias = m_interpreter.AddAlias( alias_command, cmd_obj_sp, raw_command_string)) { if (m_command_options.m_help.OptionWasSet()) alias->SetHelp(m_command_options.m_help.GetCurrentValue()); if (m_command_options.m_long_help.OptionWasSet()) alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue()); result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendError("Unable to create requested alias.\n"); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("Unable to create requested alias.\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } bool HandleAliasingNormalCommand(Args &args, CommandReturnObject &result) { size_t argc = args.GetArgumentCount(); if (argc < 2) { result.AppendError("'command alias' requires at least two arguments"); result.SetStatus(eReturnStatusFailed); return false; } // Save these in std::strings since we're going to shift them off. const std::string alias_command(args[0].ref); const std::string actual_command(args[1].ref); args.Shift(); // Shift the alias command word off the argument vector. args.Shift(); // Shift the old command word off the argument vector. // Verify that the command is alias'able, and get the appropriate command // object. if (m_interpreter.CommandExists(alias_command)) { result.AppendErrorWithFormat( "'%s' is a permanent debugger command and cannot be redefined.\n", alias_command.c_str()); result.SetStatus(eReturnStatusFailed); return false; } CommandObjectSP command_obj_sp( m_interpreter.GetCommandSPExact(actual_command, true)); CommandObjectSP subcommand_obj_sp; bool use_subcommand = false; if (!command_obj_sp) { result.AppendErrorWithFormat("'%s' is not an existing command.\n", actual_command.c_str()); result.SetStatus(eReturnStatusFailed); return false; } CommandObject *cmd_obj = command_obj_sp.get(); CommandObject *sub_cmd_obj = nullptr; OptionArgVectorSP option_arg_vector_sp = OptionArgVectorSP(new OptionArgVector); while (cmd_obj->IsMultiwordObject() && !args.empty()) { auto sub_command = args[0].ref; assert(!sub_command.empty()); subcommand_obj_sp = cmd_obj->GetSubcommandSP(sub_command); if (!subcommand_obj_sp) { result.AppendErrorWithFormat( "'%s' is not a valid sub-command of '%s'. " "Unable to create alias.\n", args[0].c_str(), actual_command.c_str()); result.SetStatus(eReturnStatusFailed); return false; } sub_cmd_obj = subcommand_obj_sp.get(); use_subcommand = true; args.Shift(); // Shift the sub_command word off the argument vector. cmd_obj = sub_cmd_obj; } // Verify & handle any options/arguments passed to the alias command std::string args_string; if (!args.empty()) { CommandObjectSP tmp_sp = m_interpreter.GetCommandSPExact(cmd_obj->GetCommandName(), false); if (use_subcommand) tmp_sp = m_interpreter.GetCommandSPExact(sub_cmd_obj->GetCommandName(), false); args.GetCommandString(args_string); } if (m_interpreter.AliasExists(alias_command) || m_interpreter.UserCommandExists(alias_command)) { result.AppendWarningWithFormat( "Overwriting existing definition for '%s'.\n", alias_command.c_str()); } if (CommandAlias *alias = m_interpreter.AddAlias( alias_command, use_subcommand ? subcommand_obj_sp : command_obj_sp, args_string)) { if (m_command_options.m_help.OptionWasSet()) alias->SetHelp(m_command_options.m_help.GetCurrentValue()); if (m_command_options.m_long_help.OptionWasSet()) alias->SetHelpLong(m_command_options.m_long_help.GetCurrentValue()); result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendError("Unable to create requested alias.\n"); result.SetStatus(eReturnStatusFailed); return false; } return result.Succeeded(); } }; #pragma mark CommandObjectCommandsUnalias //------------------------------------------------------------------------- // CommandObjectCommandsUnalias //------------------------------------------------------------------------- class CommandObjectCommandsUnalias : public CommandObjectParsed { public: CommandObjectCommandsUnalias(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "command unalias", "Delete one or more custom commands defined by 'command alias'.", nullptr) { CommandArgumentEntry arg; CommandArgumentData alias_arg; // Define the first (and only) variant of this arg. alias_arg.arg_type = eArgTypeAliasName; alias_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(alias_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectCommandsUnalias() override = default; protected: bool DoExecute(Args &args, CommandReturnObject &result) override { CommandObject::CommandMap::iterator pos; CommandObject *cmd_obj; if (args.empty()) { result.AppendError("must call 'unalias' with a valid alias"); result.SetStatus(eReturnStatusFailed); return false; } auto command_name = args[0].ref; cmd_obj = m_interpreter.GetCommandObject(command_name); if (!cmd_obj) { result.AppendErrorWithFormat( "'%s' is not a known command.\nTry 'help' to see a " "current list of commands.\n", args[0].c_str()); result.SetStatus(eReturnStatusFailed); return false; } if (m_interpreter.CommandExists(command_name)) { if (cmd_obj->IsRemovable()) { result.AppendErrorWithFormat( "'%s' is not an alias, it is a debugger command which can be " "removed using the 'command delete' command.\n", args[0].c_str()); } else { result.AppendErrorWithFormat( "'%s' is a permanent debugger command and cannot be removed.\n", args[0].c_str()); } result.SetStatus(eReturnStatusFailed); return false; } if (!m_interpreter.RemoveAlias(command_name)) { if (m_interpreter.AliasExists(command_name)) result.AppendErrorWithFormat( "Error occurred while attempting to unalias '%s'.\n", args[0].c_str()); else result.AppendErrorWithFormat("'%s' is not an existing alias.\n", args[0].c_str()); result.SetStatus(eReturnStatusFailed); return false; } result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } }; #pragma mark CommandObjectCommandsDelete //------------------------------------------------------------------------- // CommandObjectCommandsDelete //------------------------------------------------------------------------- class CommandObjectCommandsDelete : public CommandObjectParsed { public: CommandObjectCommandsDelete(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "command delete", "Delete one or more custom commands defined by 'command regex'.", nullptr) { CommandArgumentEntry arg; CommandArgumentData alias_arg; // Define the first (and only) variant of this arg. alias_arg.arg_type = eArgTypeCommandName; alias_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(alias_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectCommandsDelete() override = default; protected: bool DoExecute(Args &args, CommandReturnObject &result) override { CommandObject::CommandMap::iterator pos; if (args.empty()) { result.AppendErrorWithFormat("must call '%s' with one or more valid user " "defined regular expression command names", GetCommandName().str().c_str()); result.SetStatus(eReturnStatusFailed); } auto command_name = args[0].ref; if (!m_interpreter.CommandExists(command_name)) { StreamString error_msg_stream; const bool generate_apropos = true; const bool generate_type_lookup = false; CommandObjectHelp::GenerateAdditionalHelpAvenuesMessage( &error_msg_stream, command_name, llvm::StringRef(), llvm::StringRef(), generate_apropos, generate_type_lookup); result.AppendError(error_msg_stream.GetString()); result.SetStatus(eReturnStatusFailed); return false; } if (!m_interpreter.RemoveCommand(command_name)) { result.AppendErrorWithFormat( "'%s' is a permanent debugger command and cannot be removed.\n", args[0].c_str()); result.SetStatus(eReturnStatusFailed); return false; } result.SetStatus(eReturnStatusSuccessFinishNoResult); return true; } }; //------------------------------------------------------------------------- // CommandObjectCommandsAddRegex //------------------------------------------------------------------------- static OptionDefinition g_regex_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "help" , 'h', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeNone, "The help text to display for this command." }, { LLDB_OPT_SET_1, false, "syntax", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeNone, "A syntax string showing the typical usage syntax." }, // clang-format on }; #pragma mark CommandObjectCommandsAddRegex class CommandObjectCommandsAddRegex : public CommandObjectParsed, public IOHandlerDelegateMultiline { public: CommandObjectCommandsAddRegex(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "command regex", "Define a custom command in terms of " "existing commands by matching " "regular expressions.", "command regex [s/// ...]"), IOHandlerDelegateMultiline("", IOHandlerDelegate::Completion::LLDBCommand), m_options() { SetHelpLong( R"( )" "This command allows the user to create powerful regular expression commands \ with substitutions. The regular expressions and substitutions are specified \ using the regular expression substitution format of:" R"( s/// )" " is a regular expression that can use parenthesis to capture regular \ expression input and substitute the captured matches in the output using %1 \ for the first match, %2 for the second, and so on." R"( )" "The regular expressions can all be specified on the command line if more than \ one argument is provided. If just the command name is provided on the command \ line, then the regular expressions and substitutions can be entered on separate \ lines, followed by an empty line to terminate the command definition." R"( EXAMPLES )" "The following example will define a regular expression command named 'f' that \ will call 'finish' if there are no arguments, or 'frame select ' if \ a number follows 'f':" R"( (lldb) command regex f s/^$/finish/ 's/([0-9]+)/frame select %1/')"); } ~CommandObjectCommandsAddRegex() override = default; protected: void IOHandlerActivated(IOHandler &io_handler) override { StreamFileSP output_sp(io_handler.GetOutputStreamFile()); if (output_sp) { output_sp->PutCString("Enter one of more sed substitution commands in " "the form: 's///'.\nTerminate the " "substitution list with an empty line.\n"); output_sp->Flush(); } } void IOHandlerInputComplete(IOHandler &io_handler, std::string &data) override { io_handler.SetIsDone(true); if (m_regex_cmd_ap) { StringList lines; if (lines.SplitIntoLines(data)) { const size_t num_lines = lines.GetSize(); bool check_only = false; for (size_t i = 0; i < num_lines; ++i) { llvm::StringRef bytes_strref(lines[i]); Status error = AppendRegexSubstitution(bytes_strref, check_only); if (error.Fail()) { if (!m_interpreter.GetDebugger() .GetCommandInterpreter() .GetBatchCommandMode()) { StreamSP out_stream = m_interpreter.GetDebugger().GetAsyncOutputStream(); out_stream->Printf("error: %s\n", error.AsCString()); } } } } if (m_regex_cmd_ap->HasRegexEntries()) { CommandObjectSP cmd_sp(m_regex_cmd_ap.release()); m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true); } } } bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) { result.AppendError("usage: 'command regex " "[s/// s/// ...]'\n"); result.SetStatus(eReturnStatusFailed); return false; } Status error; auto name = command[0].ref; m_regex_cmd_ap = llvm::make_unique( m_interpreter, name, m_options.GetHelp(), m_options.GetSyntax(), 10, 0, true); if (argc == 1) { Debugger &debugger = m_interpreter.GetDebugger(); bool color_prompt = debugger.GetUseColor(); const bool multiple_lines = true; // Get multiple lines IOHandlerSP io_handler_sp(new IOHandlerEditline( debugger, IOHandler::Type::Other, "lldb-regex", // Name of input reader for history llvm::StringRef("> "), // Prompt llvm::StringRef(), // Continuation prompt multiple_lines, color_prompt, 0, // Don't show line numbers *this)); if (io_handler_sp) { debugger.PushIOHandler(io_handler_sp); result.SetStatus(eReturnStatusSuccessFinishNoResult); } } else { for (auto &entry : command.entries().drop_front()) { bool check_only = false; error = AppendRegexSubstitution(entry.ref, check_only); if (error.Fail()) break; } if (error.Success()) { AddRegexCommandToInterpreter(); } } if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } Status AppendRegexSubstitution(const llvm::StringRef ®ex_sed, bool check_only) { Status error; if (!m_regex_cmd_ap) { error.SetErrorStringWithFormat( "invalid regular expression command object for: '%.*s'", (int)regex_sed.size(), regex_sed.data()); return error; } size_t regex_sed_size = regex_sed.size(); if (regex_sed_size <= 1) { error.SetErrorStringWithFormat( "regular expression substitution string is too short: '%.*s'", (int)regex_sed.size(), regex_sed.data()); return error; } if (regex_sed[0] != 's') { error.SetErrorStringWithFormat("regular expression substitution string " "doesn't start with 's': '%.*s'", (int)regex_sed.size(), regex_sed.data()); return error; } const size_t first_separator_char_pos = 1; // use the char that follows 's' as the regex separator character so we can // have "s///" or "s|||" const char separator_char = regex_sed[first_separator_char_pos]; const size_t second_separator_char_pos = regex_sed.find(separator_char, first_separator_char_pos + 1); if (second_separator_char_pos == std::string::npos) { error.SetErrorStringWithFormat( "missing second '%c' separator char after '%.*s' in '%.*s'", separator_char, (int)(regex_sed.size() - first_separator_char_pos - 1), regex_sed.data() + (first_separator_char_pos + 1), (int)regex_sed.size(), regex_sed.data()); return error; } const size_t third_separator_char_pos = regex_sed.find(separator_char, second_separator_char_pos + 1); if (third_separator_char_pos == std::string::npos) { error.SetErrorStringWithFormat( "missing third '%c' separator char after '%.*s' in '%.*s'", separator_char, (int)(regex_sed.size() - second_separator_char_pos - 1), regex_sed.data() + (second_separator_char_pos + 1), (int)regex_sed.size(), regex_sed.data()); return error; } if (third_separator_char_pos != regex_sed_size - 1) { // Make sure that everything that follows the last regex separator char if (regex_sed.find_first_not_of("\t\n\v\f\r ", third_separator_char_pos + 1) != std::string::npos) { error.SetErrorStringWithFormat( "extra data found after the '%.*s' regular expression substitution " "string: '%.*s'", (int)third_separator_char_pos + 1, regex_sed.data(), (int)(regex_sed.size() - third_separator_char_pos - 1), regex_sed.data() + (third_separator_char_pos + 1)); return error; } } else if (first_separator_char_pos + 1 == second_separator_char_pos) { error.SetErrorStringWithFormat( " can't be empty in 's%c%c%c' string: '%.*s'", separator_char, separator_char, separator_char, (int)regex_sed.size(), regex_sed.data()); return error; } else if (second_separator_char_pos + 1 == third_separator_char_pos) { error.SetErrorStringWithFormat( " can't be empty in 's%c%c%c' string: '%.*s'", separator_char, separator_char, separator_char, (int)regex_sed.size(), regex_sed.data()); return error; } if (!check_only) { std::string regex(regex_sed.substr(first_separator_char_pos + 1, second_separator_char_pos - first_separator_char_pos - 1)); std::string subst(regex_sed.substr(second_separator_char_pos + 1, third_separator_char_pos - second_separator_char_pos - 1)); m_regex_cmd_ap->AddRegexCommand(regex.c_str(), subst.c_str()); } return error; } void AddRegexCommandToInterpreter() { if (m_regex_cmd_ap) { if (m_regex_cmd_ap->HasRegexEntries()) { CommandObjectSP cmd_sp(m_regex_cmd_ap.release()); m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true); } } } private: std::unique_ptr m_regex_cmd_ap; class CommandOptions : public Options { public: CommandOptions() : Options() {} ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'h': m_help.assign(option_arg); break; case 's': m_syntax.assign(option_arg); break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_help.clear(); m_syntax.clear(); } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_regex_options); } // TODO: Convert these functions to return StringRefs. const char *GetHelp() { return (m_help.empty() ? nullptr : m_help.c_str()); } const char *GetSyntax() { return (m_syntax.empty() ? nullptr : m_syntax.c_str()); } protected: // Instance variables to hold the values for command options. std::string m_help; std::string m_syntax; }; Options *GetOptions() override { return &m_options; } CommandOptions m_options; }; class CommandObjectPythonFunction : public CommandObjectRaw { public: CommandObjectPythonFunction(CommandInterpreter &interpreter, std::string name, std::string funct, std::string help, ScriptedCommandSynchronicity synch) : CommandObjectRaw(interpreter, name), m_function_name(funct), m_synchro(synch), m_fetched_help_long(false) { if (!help.empty()) SetHelp(help); else { StreamString stream; stream.Printf("For more information run 'help %s'", name.c_str()); SetHelp(stream.GetString()); } } ~CommandObjectPythonFunction() override = default; bool IsRemovable() const override { return true; } const std::string &GetFunctionName() { return m_function_name; } ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; } llvm::StringRef GetHelpLong() override { if (m_fetched_help_long) return CommandObjectRaw::GetHelpLong(); ScriptInterpreter *scripter = m_interpreter.GetScriptInterpreter(); if (!scripter) return CommandObjectRaw::GetHelpLong(); std::string docstring; m_fetched_help_long = scripter->GetDocumentationForItem(m_function_name.c_str(), docstring); if (!docstring.empty()) SetHelpLong(docstring); return CommandObjectRaw::GetHelpLong(); } protected: bool DoExecute(llvm::StringRef raw_command_line, CommandReturnObject &result) override { ScriptInterpreter *scripter = m_interpreter.GetScriptInterpreter(); Status error; result.SetStatus(eReturnStatusInvalid); if (!scripter || !scripter->RunScriptBasedCommand(m_function_name.c_str(), raw_command_line, m_synchro, result, error, m_exe_ctx)) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } else { // Don't change the status if the command already set it... if (result.GetStatus() == eReturnStatusInvalid) { if (result.GetOutputData().empty()) result.SetStatus(eReturnStatusSuccessFinishNoResult); else result.SetStatus(eReturnStatusSuccessFinishResult); } } return result.Succeeded(); } private: std::string m_function_name; ScriptedCommandSynchronicity m_synchro; bool m_fetched_help_long; }; class CommandObjectScriptingObject : public CommandObjectRaw { public: CommandObjectScriptingObject(CommandInterpreter &interpreter, std::string name, StructuredData::GenericSP cmd_obj_sp, ScriptedCommandSynchronicity synch) : CommandObjectRaw(interpreter, name), m_cmd_obj_sp(cmd_obj_sp), m_synchro(synch), m_fetched_help_short(false), m_fetched_help_long(false) { StreamString stream; stream.Printf("For more information run 'help %s'", name.c_str()); SetHelp(stream.GetString()); if (ScriptInterpreter *scripter = m_interpreter.GetScriptInterpreter()) GetFlags().Set(scripter->GetFlagsForCommandObject(cmd_obj_sp)); } ~CommandObjectScriptingObject() override = default; bool IsRemovable() const override { return true; } StructuredData::GenericSP GetImplementingObject() { return m_cmd_obj_sp; } ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; } llvm::StringRef GetHelp() override { if (m_fetched_help_short) return CommandObjectRaw::GetHelp(); ScriptInterpreter *scripter = m_interpreter.GetScriptInterpreter(); if (!scripter) return CommandObjectRaw::GetHelp(); std::string docstring; m_fetched_help_short = scripter->GetShortHelpForCommandObject(m_cmd_obj_sp, docstring); if (!docstring.empty()) SetHelp(docstring); return CommandObjectRaw::GetHelp(); } llvm::StringRef GetHelpLong() override { if (m_fetched_help_long) return CommandObjectRaw::GetHelpLong(); ScriptInterpreter *scripter = m_interpreter.GetScriptInterpreter(); if (!scripter) return CommandObjectRaw::GetHelpLong(); std::string docstring; m_fetched_help_long = scripter->GetLongHelpForCommandObject(m_cmd_obj_sp, docstring); if (!docstring.empty()) SetHelpLong(docstring); return CommandObjectRaw::GetHelpLong(); } protected: bool DoExecute(llvm::StringRef raw_command_line, CommandReturnObject &result) override { ScriptInterpreter *scripter = m_interpreter.GetScriptInterpreter(); Status error; result.SetStatus(eReturnStatusInvalid); if (!scripter || !scripter->RunScriptBasedCommand(m_cmd_obj_sp, raw_command_line, m_synchro, result, error, m_exe_ctx)) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } else { // Don't change the status if the command already set it... if (result.GetStatus() == eReturnStatusInvalid) { if (result.GetOutputData().empty()) result.SetStatus(eReturnStatusSuccessFinishNoResult); else result.SetStatus(eReturnStatusSuccessFinishResult); } } return result.Succeeded(); } private: StructuredData::GenericSP m_cmd_obj_sp; ScriptedCommandSynchronicity m_synchro; bool m_fetched_help_short : 1; bool m_fetched_help_long : 1; }; //------------------------------------------------------------------------- // CommandObjectCommandsScriptImport //------------------------------------------------------------------------- OptionDefinition g_script_import_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "allow-reload", 'r', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Allow the script to be loaded even if it was already loaded before. This argument exists for backwards compatibility, but reloading is always allowed, whether you specify it or not." }, // clang-format on }; class CommandObjectCommandsScriptImport : public CommandObjectParsed { public: CommandObjectCommandsScriptImport(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "command script import", "Import a scripting module in LLDB.", nullptr), m_options() { CommandArgumentEntry arg1; CommandArgumentData cmd_arg; // Define the first (and only) variant of this arg. cmd_arg.arg_type = eArgTypeFilename; cmd_arg.arg_repetition = eArgRepeatPlus; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(cmd_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); } ~CommandObjectCommandsScriptImport() override = default; int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } Options *GetOptions() override { return &m_options; } protected: class CommandOptions : public Options { public: CommandOptions() : Options() {} ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'r': m_allow_reload = true; break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_allow_reload = true; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_script_import_options); } // Instance variables to hold the values for command options. bool m_allow_reload; }; bool DoExecute(Args &command, CommandReturnObject &result) override { if (m_interpreter.GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) { result.AppendError("only scripting language supported for module " "importing is currently Python"); result.SetStatus(eReturnStatusFailed); return false; } if (command.empty()) { result.AppendError("command script import needs one or more arguments"); result.SetStatus(eReturnStatusFailed); return false; } for (auto &entry : command.entries()) { Status error; const bool init_session = true; // FIXME: this is necessary because CommandObject::CheckRequirements() // assumes that commands won't ever be recursively invoked, but it's // actually possible to craft a Python script that does other "command // script imports" in __lldb_init_module the real fix is to have // recursive commands possible with a CommandInvocation object separate // from the CommandObject itself, so that recursive command invocations // won't stomp on each other (wrt to execution contents, options, and // more) m_exe_ctx.Clear(); if (m_interpreter.GetScriptInterpreter()->LoadScriptingModule( entry.c_str(), m_options.m_allow_reload, init_session, error)) { result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendErrorWithFormat("module importing failed: %s", error.AsCString()); result.SetStatus(eReturnStatusFailed); } } return result.Succeeded(); } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectCommandsScriptAdd //------------------------------------------------------------------------- static OptionEnumValueElement g_script_synchro_type[] = { {eScriptedCommandSynchronicitySynchronous, "synchronous", "Run synchronous"}, {eScriptedCommandSynchronicityAsynchronous, "asynchronous", "Run asynchronous"}, {eScriptedCommandSynchronicityCurrentValue, "current", "Do not alter current setting"}, {0, nullptr, nullptr}}; static OptionDefinition g_script_add_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "function", 'f', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePythonFunction, "Name of the Python function to bind to this command name." }, { LLDB_OPT_SET_2, false, "class", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePythonClass, "Name of the Python class to bind to this command name." }, { LLDB_OPT_SET_1, false, "help" , 'h', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeHelpText, "The help text to display for this command." }, { LLDB_OPT_SET_ALL, false, "synchronicity", 's', OptionParser::eRequiredArgument, nullptr, g_script_synchro_type, 0, eArgTypeScriptedCommandSynchronicity, "Set the synchronicity of this command's executions with regard to LLDB event system." }, // clang-format on }; class CommandObjectCommandsScriptAdd : public CommandObjectParsed, public IOHandlerDelegateMultiline { public: CommandObjectCommandsScriptAdd(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "command script add", "Add a scripted function as an LLDB command.", nullptr), IOHandlerDelegateMultiline("DONE"), m_options() { CommandArgumentEntry arg1; CommandArgumentData cmd_arg; // Define the first (and only) variant of this arg. cmd_arg.arg_type = eArgTypeCommandName; cmd_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(cmd_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); } ~CommandObjectCommandsScriptAdd() override = default; Options *GetOptions() override { return &m_options; } protected: class CommandOptions : public Options { public: CommandOptions() : Options(), m_class_name(), m_funct_name(), m_short_help(), m_synchronicity(eScriptedCommandSynchronicitySynchronous) {} ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'f': if (!option_arg.empty()) m_funct_name = option_arg; break; case 'c': if (!option_arg.empty()) m_class_name = option_arg; break; case 'h': if (!option_arg.empty()) m_short_help = option_arg; break; case 's': m_synchronicity = (ScriptedCommandSynchronicity)OptionArgParser::ToOptionEnum( option_arg, GetDefinitions()[option_idx].enum_values, 0, error); if (!error.Success()) error.SetErrorStringWithFormat( "unrecognized value for synchronicity '%s'", option_arg.str().c_str()); break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_class_name.clear(); m_funct_name.clear(); m_short_help.clear(); m_synchronicity = eScriptedCommandSynchronicitySynchronous; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_script_add_options); } // Instance variables to hold the values for command options. std::string m_class_name; std::string m_funct_name; std::string m_short_help; ScriptedCommandSynchronicity m_synchronicity; }; void IOHandlerActivated(IOHandler &io_handler) override { StreamFileSP output_sp(io_handler.GetOutputStreamFile()); if (output_sp) { output_sp->PutCString(g_python_command_instructions); output_sp->Flush(); } } void IOHandlerInputComplete(IOHandler &io_handler, std::string &data) override { StreamFileSP error_sp = io_handler.GetErrorStreamFile(); ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); if (interpreter) { StringList lines; lines.SplitIntoLines(data); if (lines.GetSize() > 0) { std::string funct_name_str; if (interpreter->GenerateScriptAliasFunction(lines, funct_name_str)) { if (funct_name_str.empty()) { error_sp->Printf("error: unable to obtain a function name, didn't " "add python command.\n"); error_sp->Flush(); } else { // everything should be fine now, let's add this alias CommandObjectSP command_obj_sp(new CommandObjectPythonFunction( m_interpreter, m_cmd_name, funct_name_str, m_short_help, m_synchronicity)); if (!m_interpreter.AddUserCommand(m_cmd_name, command_obj_sp, true)) { error_sp->Printf("error: unable to add selected command, didn't " "add python command.\n"); error_sp->Flush(); } } } else { error_sp->Printf( "error: unable to create function, didn't add python command.\n"); error_sp->Flush(); } } else { error_sp->Printf("error: empty function, didn't add python command.\n"); error_sp->Flush(); } } else { error_sp->Printf( "error: script interpreter missing, didn't add python command.\n"); error_sp->Flush(); } io_handler.SetIsDone(true); } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { if (m_interpreter.GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) { result.AppendError("only scripting language supported for scripted " "commands is currently Python"); result.SetStatus(eReturnStatusFailed); return false; } if (command.GetArgumentCount() != 1) { result.AppendError("'command script add' requires one argument"); result.SetStatus(eReturnStatusFailed); return false; } // Store the options in case we get multi-line input m_cmd_name = command[0].ref; m_short_help.assign(m_options.m_short_help); m_synchronicity = m_options.m_synchronicity; if (m_options.m_class_name.empty()) { if (m_options.m_funct_name.empty()) { m_interpreter.GetPythonCommandsFromIOHandler( " ", // Prompt *this, // IOHandlerDelegate true, // Run IOHandler in async mode nullptr); // Baton for the "io_handler" that will be passed back // into our IOHandlerDelegate functions } else { CommandObjectSP new_cmd(new CommandObjectPythonFunction( m_interpreter, m_cmd_name, m_options.m_funct_name, m_options.m_short_help, m_synchronicity)); if (m_interpreter.AddUserCommand(m_cmd_name, new_cmd, true)) { result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendError("cannot add command"); result.SetStatus(eReturnStatusFailed); } } } else { ScriptInterpreter *interpreter = GetCommandInterpreter().GetScriptInterpreter(); if (!interpreter) { result.AppendError("cannot find ScriptInterpreter"); result.SetStatus(eReturnStatusFailed); return false; } auto cmd_obj_sp = interpreter->CreateScriptCommandObject( m_options.m_class_name.c_str()); if (!cmd_obj_sp) { result.AppendError("cannot create helper object"); result.SetStatus(eReturnStatusFailed); return false; } CommandObjectSP new_cmd(new CommandObjectScriptingObject( m_interpreter, m_cmd_name, cmd_obj_sp, m_synchronicity)); if (m_interpreter.AddUserCommand(m_cmd_name, new_cmd, true)) { result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendError("cannot add command"); result.SetStatus(eReturnStatusFailed); } } return result.Succeeded(); } CommandOptions m_options; std::string m_cmd_name; std::string m_short_help; ScriptedCommandSynchronicity m_synchronicity; }; //------------------------------------------------------------------------- // CommandObjectCommandsScriptList //------------------------------------------------------------------------- class CommandObjectCommandsScriptList : public CommandObjectParsed { public: CommandObjectCommandsScriptList(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "command script list", "List defined scripted commands.", nullptr) {} ~CommandObjectCommandsScriptList() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { m_interpreter.GetHelp(result, CommandInterpreter::eCommandTypesUserDef); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } }; //------------------------------------------------------------------------- // CommandObjectCommandsScriptClear //------------------------------------------------------------------------- class CommandObjectCommandsScriptClear : public CommandObjectParsed { public: CommandObjectCommandsScriptClear(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "command script clear", "Delete all scripted commands.", nullptr) {} ~CommandObjectCommandsScriptClear() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { m_interpreter.RemoveAllUser(); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } }; //------------------------------------------------------------------------- // CommandObjectCommandsScriptDelete //------------------------------------------------------------------------- class CommandObjectCommandsScriptDelete : public CommandObjectParsed { public: CommandObjectCommandsScriptDelete(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "command script delete", "Delete a scripted command.", nullptr) { CommandArgumentEntry arg1; CommandArgumentData cmd_arg; // Define the first (and only) variant of this arg. cmd_arg.arg_type = eArgTypeCommandName; cmd_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(cmd_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); } ~CommandObjectCommandsScriptDelete() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { if (command.GetArgumentCount() != 1) { result.AppendError("'command script delete' requires one argument"); result.SetStatus(eReturnStatusFailed); return false; } auto cmd_name = command[0].ref; if (cmd_name.empty() || !m_interpreter.HasUserCommands() || !m_interpreter.UserCommandExists(cmd_name)) { result.AppendErrorWithFormat("command %s not found", command[0].c_str()); result.SetStatus(eReturnStatusFailed); return false; } m_interpreter.RemoveUser(cmd_name); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } }; #pragma mark CommandObjectMultiwordCommandsScript //------------------------------------------------------------------------- // CommandObjectMultiwordCommandsScript //------------------------------------------------------------------------- class CommandObjectMultiwordCommandsScript : public CommandObjectMultiword { public: CommandObjectMultiwordCommandsScript(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "command script", "Commands for managing custom " "commands implemented by " "interpreter scripts.", "command script []") { LoadSubCommand("add", CommandObjectSP( new CommandObjectCommandsScriptAdd(interpreter))); LoadSubCommand( "delete", CommandObjectSP(new CommandObjectCommandsScriptDelete(interpreter))); LoadSubCommand( "clear", CommandObjectSP(new CommandObjectCommandsScriptClear(interpreter))); LoadSubCommand("list", CommandObjectSP(new CommandObjectCommandsScriptList( interpreter))); LoadSubCommand( "import", CommandObjectSP(new CommandObjectCommandsScriptImport(interpreter))); } ~CommandObjectMultiwordCommandsScript() override = default; }; #pragma mark CommandObjectMultiwordCommands //------------------------------------------------------------------------- // CommandObjectMultiwordCommands //------------------------------------------------------------------------- CommandObjectMultiwordCommands::CommandObjectMultiwordCommands( CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "command", "Commands for managing custom LLDB commands.", "command []") { LoadSubCommand("source", CommandObjectSP(new CommandObjectCommandsSource(interpreter))); LoadSubCommand("alias", CommandObjectSP(new CommandObjectCommandsAlias(interpreter))); LoadSubCommand("unalias", CommandObjectSP( new CommandObjectCommandsUnalias(interpreter))); LoadSubCommand("delete", CommandObjectSP(new CommandObjectCommandsDelete(interpreter))); LoadSubCommand( "regex", CommandObjectSP(new CommandObjectCommandsAddRegex(interpreter))); LoadSubCommand("history", CommandObjectSP( new CommandObjectCommandsHistory(interpreter))); LoadSubCommand( "script", CommandObjectSP(new CommandObjectMultiwordCommandsScript(interpreter))); } CommandObjectMultiwordCommands::~CommandObjectMultiwordCommands() = default; Index: vendor/lldb/dist/source/Commands/CommandObjectFrame.cpp =================================================================== --- vendor/lldb/dist/source/Commands/CommandObjectFrame.cpp (revision 337146) +++ vendor/lldb/dist/source/Commands/CommandObjectFrame.cpp (revision 337147) @@ -1,753 +1,753 @@ //===-- CommandObjectFrame.cpp ----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes #include // Other libraries and framework includes // Project includes #include "CommandObjectFrame.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/Value.h" #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/DataFormatters/ValueObjectPrinter.h" #include "lldb/Host/Host.h" #include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionGroupFormat.h" #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" #include "lldb/Interpreter/OptionGroupVariable.h" #include "lldb/Interpreter/Options.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/Variable.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" using namespace lldb; using namespace lldb_private; #pragma mark CommandObjectFrameDiagnose //------------------------------------------------------------------------- // CommandObjectFrameInfo //------------------------------------------------------------------------- //------------------------------------------------------------------------- // CommandObjectFrameDiagnose //------------------------------------------------------------------------- static OptionDefinition g_frame_diag_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "register", 'r', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeRegisterName, "A register to diagnose." }, { LLDB_OPT_SET_1, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddress, "An address to diagnose." }, { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeOffset, "An optional offset. Requires --register." } // clang-format on }; class CommandObjectFrameDiagnose : public CommandObjectParsed { public: class CommandOptions : public Options { public: CommandOptions() : Options() { OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'r': reg = ConstString(option_arg); break; case 'a': { address.emplace(); if (option_arg.getAsInteger(0, *address)) { address.reset(); error.SetErrorStringWithFormat("invalid address argument '%s'", option_arg.str().c_str()); } } break; case 'o': { offset.emplace(); if (option_arg.getAsInteger(0, *offset)) { offset.reset(); error.SetErrorStringWithFormat("invalid offset argument '%s'", option_arg.str().c_str()); } } break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { address.reset(); reg.reset(); offset.reset(); } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_frame_diag_options); } // Options. llvm::Optional address; llvm::Optional reg; llvm::Optional offset; }; CommandObjectFrameDiagnose(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "frame diagnose", "Try to determine what path path the current stop " "location used to get to a register or address", nullptr, eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() { CommandArgumentEntry arg; CommandArgumentData index_arg; // Define the first (and only) variant of this arg. index_arg.arg_type = eArgTypeFrameIndex; index_arg.arg_repetition = eArgRepeatOptional; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(index_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectFrameDiagnose() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Thread *thread = m_exe_ctx.GetThreadPtr(); StackFrameSP frame_sp = thread->GetSelectedFrame(); ValueObjectSP valobj_sp; if (m_options.address.hasValue()) { if (m_options.reg.hasValue() || m_options.offset.hasValue()) { result.AppendError( "`frame diagnose --address` is incompatible with other arguments."); result.SetStatus(eReturnStatusFailed); return false; } valobj_sp = frame_sp->GuessValueForAddress(m_options.address.getValue()); } else if (m_options.reg.hasValue()) { valobj_sp = frame_sp->GuessValueForRegisterAndOffset( m_options.reg.getValue(), m_options.offset.getValueOr(0)); } else { StopInfoSP stop_info_sp = thread->GetStopInfo(); if (!stop_info_sp) { result.AppendError("No arguments provided, and no stop info."); result.SetStatus(eReturnStatusFailed); return false; } valobj_sp = StopInfo::GetCrashingDereference(stop_info_sp); } if (!valobj_sp) { result.AppendError("No diagnosis available."); result.SetStatus(eReturnStatusFailed); return false; } DumpValueObjectOptions::DeclPrintingHelper helper = [&valobj_sp]( ConstString type, ConstString var, const DumpValueObjectOptions &opts, Stream &stream) -> bool { const ValueObject::GetExpressionPathFormat format = ValueObject:: GetExpressionPathFormat::eGetExpressionPathFormatHonorPointers; const bool qualify_cxx_base_classes = false; valobj_sp->GetExpressionPath(stream, qualify_cxx_base_classes, format); stream.PutCString(" ="); return true; }; DumpValueObjectOptions options; options.SetDeclPrintingHelper(helper); ValueObjectPrinter printer(valobj_sp.get(), &result.GetOutputStream(), options); printer.PrintValueObject(); return true; } protected: CommandOptions m_options; }; #pragma mark CommandObjectFrameInfo //------------------------------------------------------------------------- // CommandObjectFrameInfo //------------------------------------------------------------------------- class CommandObjectFrameInfo : public CommandObjectParsed { public: CommandObjectFrameInfo(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "frame info", "List information about the current " "stack frame in the current thread.", "frame info", eCommandRequiresFrame | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {} ~CommandObjectFrameInfo() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { m_exe_ctx.GetFrameRef().DumpUsingSettingsFormat(&result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } }; #pragma mark CommandObjectFrameSelect //------------------------------------------------------------------------- // CommandObjectFrameSelect //------------------------------------------------------------------------- static OptionDefinition g_frame_select_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "relative", 'r', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeOffset, "A relative frame index offset from the current frame index." }, // clang-format on }; class CommandObjectFrameSelect : public CommandObjectParsed { public: class CommandOptions : public Options { public: CommandOptions() : Options() { OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'r': if (option_arg.getAsInteger(0, relative_frame_offset)) { relative_frame_offset = INT32_MIN; error.SetErrorStringWithFormat("invalid frame offset argument '%s'", option_arg.str().c_str()); } break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { relative_frame_offset = INT32_MIN; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_frame_select_options); } int32_t relative_frame_offset; }; CommandObjectFrameSelect(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "frame select", "Select the current stack frame by " "index from within the current thread " "(see 'thread backtrace'.)", nullptr, eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() { CommandArgumentEntry arg; CommandArgumentData index_arg; // Define the first (and only) variant of this arg. index_arg.arg_type = eArgTypeFrameIndex; index_arg.arg_repetition = eArgRepeatOptional; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(index_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectFrameSelect() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { // No need to check "thread" for validity as eCommandRequiresThread ensures // it is valid Thread *thread = m_exe_ctx.GetThreadPtr(); uint32_t frame_idx = UINT32_MAX; if (m_options.relative_frame_offset != INT32_MIN) { // The one and only argument is a signed relative frame index frame_idx = thread->GetSelectedFrameIndex(); if (frame_idx == UINT32_MAX) frame_idx = 0; if (m_options.relative_frame_offset < 0) { if (static_cast(frame_idx) >= -m_options.relative_frame_offset) frame_idx += m_options.relative_frame_offset; else { if (frame_idx == 0) { // If you are already at the bottom of the stack, then just warn // and don't reset the frame. result.AppendError("Already at the bottom of the stack."); result.SetStatus(eReturnStatusFailed); return false; } else frame_idx = 0; } } else if (m_options.relative_frame_offset > 0) { // I don't want "up 20" where "20" takes you past the top of the stack // to produce // an error, but rather to just go to the top. So I have to count the // stack here... const uint32_t num_frames = thread->GetStackFrameCount(); if (static_cast(num_frames - frame_idx) > m_options.relative_frame_offset) frame_idx += m_options.relative_frame_offset; else { if (frame_idx == num_frames - 1) { // If we are already at the top of the stack, just warn and don't // reset the frame. result.AppendError("Already at the top of the stack."); result.SetStatus(eReturnStatusFailed); return false; } else frame_idx = num_frames - 1; } } } else { if (command.GetArgumentCount() > 1) { result.AppendErrorWithFormat( "too many arguments; expected frame-index, saw '%s'.\n", command[0].c_str()); m_options.GenerateOptionUsage( result.GetErrorStream(), this, GetCommandInterpreter().GetDebugger().GetTerminalWidth()); return false; } if (command.GetArgumentCount() == 1) { if (command[0].ref.getAsInteger(0, frame_idx)) { result.AppendErrorWithFormat("invalid frame index argument '%s'.", command[0].c_str()); result.SetStatus(eReturnStatusFailed); return false; } } else if (command.GetArgumentCount() == 0) { frame_idx = thread->GetSelectedFrameIndex(); if (frame_idx == UINT32_MAX) { frame_idx = 0; } } } bool success = thread->SetSelectedFrameByIndexNoisily( frame_idx, result.GetOutputStream()); if (success) { m_exe_ctx.SetFrameSP(thread->GetSelectedFrame()); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat("Frame index (%u) out of range.\n", frame_idx); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } protected: CommandOptions m_options; }; #pragma mark CommandObjectFrameVariable //---------------------------------------------------------------------- // List images with associated information //---------------------------------------------------------------------- class CommandObjectFrameVariable : public CommandObjectParsed { public: CommandObjectFrameVariable(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "frame variable", "Show variables for the current stack frame. Defaults to all " "arguments and local variables in scope. Names of argument, " "local, file static and file global variables can be specified. " "Children of aggregate variables can be specified such as " "'var->child.x'.", nullptr, eCommandRequiresFrame | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused | eCommandRequiresProcess), m_option_group(), m_option_variable( true), // Include the frame specific options by passing "true" m_option_format(eFormatDefault), m_varobj_options() { CommandArgumentEntry arg; CommandArgumentData var_name_arg; // Define the first (and only) variant of this arg. var_name_arg.arg_type = eArgTypeVarName; var_name_arg.arg_repetition = eArgRepeatStar; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(var_name_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); m_option_group.Append(&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_option_format, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_1); m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectFrameVariable() override = default; Options *GetOptions() override { return &m_option_group; } int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { // Arguments are the standard source file completer. CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eVariablePathCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } protected: llvm::StringRef GetScopeString(VariableSP var_sp) { if (!var_sp) return llvm::StringRef::withNullAsEmpty(nullptr); switch (var_sp->GetScope()) { case eValueTypeVariableGlobal: return "GLOBAL: "; case eValueTypeVariableStatic: return "STATIC: "; case eValueTypeVariableArgument: return "ARG: "; case eValueTypeVariableLocal: return "LOCAL: "; case eValueTypeVariableThreadLocal: return "THREAD: "; default: break; } return llvm::StringRef::withNullAsEmpty(nullptr); } bool DoExecute(Args &command, CommandReturnObject &result) override { // No need to check "frame" for validity as eCommandRequiresFrame ensures // it is valid StackFrame *frame = m_exe_ctx.GetFramePtr(); Stream &s = result.GetOutputStream(); // Be careful about the stack frame, if any summary formatter runs code, it // might clear the StackFrameList for the thread. So hold onto a shared // pointer to the frame so it stays alive. VariableList *variable_list = frame->GetVariableList(m_option_variable.show_globals); VariableSP var_sp; ValueObjectSP valobj_sp; TypeSummaryImplSP summary_format_sp; if (!m_option_variable.summary.IsCurrentValueEmpty()) DataVisualization::NamedSummaryFormats::GetSummaryFormat( ConstString(m_option_variable.summary.GetCurrentValue()), summary_format_sp); else if (!m_option_variable.summary_string.IsCurrentValueEmpty()) summary_format_sp.reset(new StringSummaryFormat( TypeSummaryImpl::Flags(), m_option_variable.summary_string.GetCurrentValue())); DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions( eLanguageRuntimeDescriptionDisplayVerbosityFull, eFormatDefault, summary_format_sp)); const SymbolContext &sym_ctx = frame->GetSymbolContext(eSymbolContextFunction); if (sym_ctx.function && sym_ctx.function->IsTopLevelFunction()) m_option_variable.show_globals = true; if (variable_list) { const Format format = m_option_format.GetFormat(); options.SetFormat(format); if (!command.empty()) { VariableList regex_var_list; // If we have any args to the variable command, we will make variable // objects from them... for (auto &entry : command) { if (m_option_variable.use_regex) { const size_t regex_start_index = regex_var_list.GetSize(); llvm::StringRef name_str = entry.ref; RegularExpression regex(name_str); if (regex.Compile(name_str)) { size_t num_matches = 0; const size_t num_new_regex_vars = variable_list->AppendVariablesIfUnique(regex, regex_var_list, num_matches); if (num_new_regex_vars > 0) { for (size_t regex_idx = regex_start_index, end_index = regex_var_list.GetSize(); regex_idx < end_index; ++regex_idx) { var_sp = regex_var_list.GetVariableAtIndex(regex_idx); if (var_sp) { valobj_sp = frame->GetValueObjectForFrameVariable( var_sp, m_varobj_options.use_dynamic); if (valobj_sp) { std::string scope_string; if (m_option_variable.show_scope) scope_string = GetScopeString(var_sp).str(); if (!scope_string.empty()) s.PutCString(scope_string); if (m_option_variable.show_decl && var_sp->GetDeclaration().GetFile()) { bool show_fullpaths = false; bool show_module = true; if (var_sp->DumpDeclaration(&s, show_fullpaths, show_module)) s.PutCString(": "); } valobj_sp->Dump(result.GetOutputStream(), options); } } } } else if (num_matches == 0) { result.GetErrorStream().Printf("error: no variables matched " "the regular expression '%s'.\n", entry.c_str()); } } else { char regex_error[1024]; if (regex.GetErrorAsCString(regex_error, sizeof(regex_error))) result.GetErrorStream().Printf("error: %s\n", regex_error); else result.GetErrorStream().Printf( "error: unknown regex error when compiling '%s'\n", entry.c_str()); } } else // No regex, either exact variable names or variable // expressions. { Status error; uint32_t expr_path_options = StackFrame::eExpressionPathOptionCheckPtrVsMember | StackFrame::eExpressionPathOptionsAllowDirectIVarAccess | StackFrame::eExpressionPathOptionsInspectAnonymousUnions; lldb::VariableSP var_sp; valobj_sp = frame->GetValueForVariableExpressionPath( entry.ref, m_varobj_options.use_dynamic, expr_path_options, var_sp, error); if (valobj_sp) { std::string scope_string; if (m_option_variable.show_scope) scope_string = GetScopeString(var_sp).str(); if (!scope_string.empty()) s.PutCString(scope_string); if (m_option_variable.show_decl && var_sp && var_sp->GetDeclaration().GetFile()) { var_sp->GetDeclaration().DumpStopContext(&s, false); s.PutCString(": "); } options.SetFormat(format); options.SetVariableFormatDisplayLanguage( valobj_sp->GetPreferredDisplayLanguage()); Stream &output_stream = result.GetOutputStream(); options.SetRootValueObjectName( valobj_sp->GetParent() ? entry.c_str() : nullptr); valobj_sp->Dump(output_stream, options); } else { const char *error_cstr = error.AsCString(nullptr); if (error_cstr) result.GetErrorStream().Printf("error: %s\n", error_cstr); else result.GetErrorStream().Printf("error: unable to find any " "variable expression path that " "matches '%s'.\n", entry.c_str()); } } } } else // No command arg specified. Use variable_list, instead. { const size_t num_variables = variable_list->GetSize(); if (num_variables > 0) { for (size_t i = 0; i < num_variables; i++) { var_sp = variable_list->GetVariableAtIndex(i); switch (var_sp->GetScope()) { case eValueTypeVariableGlobal: if (!m_option_variable.show_globals) continue; break; case eValueTypeVariableStatic: if (!m_option_variable.show_globals) continue; break; case eValueTypeVariableArgument: if (!m_option_variable.show_args) continue; break; case eValueTypeVariableLocal: if (!m_option_variable.show_locals) continue; break; default: continue; break; } std::string scope_string; if (m_option_variable.show_scope) scope_string = GetScopeString(var_sp).str(); // Use the variable object code to make sure we are using the same // APIs as the public API will be using... valobj_sp = frame->GetValueObjectForFrameVariable( var_sp, m_varobj_options.use_dynamic); if (valobj_sp) { // When dumping all variables, don't print any variables that are // not in scope to avoid extra unneeded output if (valobj_sp->IsInScope()) { if (!valobj_sp->GetTargetSP() ->GetDisplayRuntimeSupportValues() && valobj_sp->IsRuntimeSupportValue()) continue; if (!scope_string.empty()) s.PutCString(scope_string); if (m_option_variable.show_decl && var_sp->GetDeclaration().GetFile()) { var_sp->GetDeclaration().DumpStopContext(&s, false); s.PutCString(": "); } options.SetFormat(format); options.SetVariableFormatDisplayLanguage( valobj_sp->GetPreferredDisplayLanguage()); options.SetRootValueObjectName( var_sp ? var_sp->GetName().AsCString() : nullptr); valobj_sp->Dump(result.GetOutputStream(), options); } } } } } result.SetStatus(eReturnStatusSuccessFinishResult); } if (m_interpreter.TruncationWarningNecessary()) { result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(), m_cmd_name.c_str()); m_interpreter.TruncationWarningGiven(); } // Increment statistics. bool res = result.Succeeded(); Target *target = GetSelectedOrDummyTarget(); if (res) target->IncrementStats(StatisticKind::FrameVarSuccess); else target->IncrementStats(StatisticKind::FrameVarFailure); return res; } protected: OptionGroupOptions m_option_group; OptionGroupVariable m_option_variable; OptionGroupFormat m_option_format; OptionGroupValueObjectDisplay m_varobj_options; }; #pragma mark CommandObjectMultiwordFrame //------------------------------------------------------------------------- // CommandObjectMultiwordFrame //------------------------------------------------------------------------- CommandObjectMultiwordFrame::CommandObjectMultiwordFrame( CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "frame", "Commands for selecting and " "examing the current " "thread's stack frames.", "frame []") { LoadSubCommand("diagnose", CommandObjectSP(new CommandObjectFrameDiagnose(interpreter))); LoadSubCommand("info", CommandObjectSP(new CommandObjectFrameInfo(interpreter))); LoadSubCommand("select", CommandObjectSP(new CommandObjectFrameSelect(interpreter))); LoadSubCommand("variable", CommandObjectSP(new CommandObjectFrameVariable(interpreter))); } CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame() = default; Index: vendor/lldb/dist/source/Commands/CommandObjectMultiword.cpp =================================================================== --- vendor/lldb/dist/source/Commands/CommandObjectMultiword.cpp (revision 337146) +++ vendor/lldb/dist/source/Commands/CommandObjectMultiword.cpp (revision 337147) @@ -1,398 +1,400 @@ //===-- CommandObjectMultiword.cpp ------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Core/Debugger.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/Options.h" using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------------- // CommandObjectMultiword //------------------------------------------------------------------------- CommandObjectMultiword::CommandObjectMultiword(CommandInterpreter &interpreter, const char *name, const char *help, const char *syntax, uint32_t flags) : CommandObject(interpreter, name, help, syntax, flags), m_can_be_removed(false) {} CommandObjectMultiword::~CommandObjectMultiword() = default; CommandObjectSP CommandObjectMultiword::GetSubcommandSP(llvm::StringRef sub_cmd, StringList *matches) { CommandObjectSP return_cmd_sp; CommandObject::CommandMap::iterator pos; if (!m_subcommand_dict.empty()) { pos = m_subcommand_dict.find(sub_cmd); if (pos != m_subcommand_dict.end()) { // An exact match; append the sub_cmd to the 'matches' string list. if (matches) matches->AppendString(sub_cmd); return_cmd_sp = pos->second; } else { StringList local_matches; if (matches == nullptr) matches = &local_matches; int num_matches = AddNamesMatchingPartialString(m_subcommand_dict, sub_cmd, *matches); if (num_matches == 1) { // Cleaner, but slightly less efficient would be to call back into this // function, since I now know I have an exact match... sub_cmd = matches->GetStringAtIndex(0); pos = m_subcommand_dict.find(sub_cmd); if (pos != m_subcommand_dict.end()) return_cmd_sp = pos->second; } } } return return_cmd_sp; } CommandObject * CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd, StringList *matches) { return GetSubcommandSP(sub_cmd, matches).get(); } bool CommandObjectMultiword::LoadSubCommand(llvm::StringRef name, const CommandObjectSP &cmd_obj) { if (cmd_obj) assert((&GetCommandInterpreter() == &cmd_obj->GetCommandInterpreter()) && "tried to add a CommandObject from a different interpreter"); CommandMap::iterator pos; bool success = true; pos = m_subcommand_dict.find(name); if (pos == m_subcommand_dict.end()) { m_subcommand_dict[name] = cmd_obj; } else success = false; return success; } bool CommandObjectMultiword::Execute(const char *args_string, CommandReturnObject &result) { Args args(args_string); const size_t argc = args.GetArgumentCount(); if (argc == 0) { this->CommandObject::GenerateHelpText(result); return result.Succeeded(); } auto sub_command = args[0].ref; if (sub_command.empty()) return result.Succeeded(); if (sub_command.equals_lower("help")) { this->CommandObject::GenerateHelpText(result); return result.Succeeded(); } if (m_subcommand_dict.empty()) { result.AppendErrorWithFormat("'%s' does not have any subcommands.\n", GetCommandName().str().c_str()); result.SetStatus(eReturnStatusFailed); return false; } StringList matches; CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches); if (sub_cmd_obj != nullptr) { // Now call CommandObject::Execute to process options in `rest_of_line`. // From there the command-specific version of Execute will be called, with // the processed arguments. args.Shift(); sub_cmd_obj->Execute(args_string, result); return result.Succeeded(); } std::string error_msg; const size_t num_subcmd_matches = matches.GetSize(); if (num_subcmd_matches > 0) error_msg.assign("ambiguous command "); else error_msg.assign("invalid command "); error_msg.append("'"); error_msg.append(GetCommandName()); error_msg.append(" "); error_msg.append(sub_command); error_msg.append("'."); if (num_subcmd_matches > 0) { error_msg.append(" Possible completions:"); - for (size_t i = 0; i < num_subcmd_matches; i++) { + for (size_t i = 0; i < matches.GetSize(); i++) { error_msg.append("\n\t"); error_msg.append(matches.GetStringAtIndex(i)); } } error_msg.append("\n"); result.AppendRawError(error_msg.c_str()); result.SetStatus(eReturnStatusFailed); return false; } void CommandObjectMultiword::GenerateHelpText(Stream &output_stream) { // First time through here, generate the help text for the object and push it // to the return result object as well CommandObject::GenerateHelpText(output_stream); output_stream.PutCString("\nThe following subcommands are supported:\n\n"); CommandMap::iterator pos; uint32_t max_len = FindLongestCommandWord(m_subcommand_dict); if (max_len) max_len += 4; // Indent the output by 4 spaces. for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) { std::string indented_command(" "); indented_command.append(pos->first); if (pos->second->WantsRawCommandString()) { std::string help_text(pos->second->GetHelp()); help_text.append(" Expects 'raw' input (see 'help raw-input'.)"); m_interpreter.OutputFormattedHelpText(output_stream, indented_command.c_str(), "--", help_text.c_str(), max_len); } else m_interpreter.OutputFormattedHelpText(output_stream, indented_command.c_str(), "--", pos->second->GetHelp(), max_len); } output_stream.PutCString("\nFor more help on any particular subcommand, type " "'help '.\n"); } int CommandObjectMultiword::HandleCompletion(CompletionRequest &request) { // Any of the command matches will provide a complete word, otherwise the // individual completers will override this. request.SetWordComplete(true); - auto &matches = request.GetMatches(); auto arg0 = request.GetParsedLine()[0].ref; if (request.GetCursorIndex() == 0) { - AddNamesMatchingPartialString(m_subcommand_dict, arg0, matches); + StringList new_matches; + AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches); + request.AddCompletions(new_matches); - if (matches.GetSize() == 1 && matches.GetStringAtIndex(0) != nullptr && - (arg0 == matches.GetStringAtIndex(0))) { + if (new_matches.GetSize() == 1 && + new_matches.GetStringAtIndex(0) != nullptr && + (arg0 == new_matches.GetStringAtIndex(0))) { StringList temp_matches; CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches); if (cmd_obj != nullptr) { if (request.GetParsedLine().GetArgumentCount() == 1) { request.SetWordComplete(true); } else { - matches.DeleteStringAtIndex(0); request.GetParsedLine().Shift(); request.SetCursorCharPosition(0); request.GetParsedLine().AppendArgument(llvm::StringRef()); return cmd_obj->HandleCompletion(request); } } } - return matches.GetSize(); + return new_matches.GetSize(); } else { - CommandObject *sub_command_object = GetSubcommandObject(arg0, &matches); + StringList new_matches; + CommandObject *sub_command_object = GetSubcommandObject(arg0, &new_matches); if (sub_command_object == nullptr) { - return matches.GetSize(); + request.AddCompletions(new_matches); + return request.GetNumberOfMatches(); } else { // Remove the one match that we got from calling GetSubcommandObject. - matches.DeleteStringAtIndex(0); + new_matches.DeleteStringAtIndex(0); + request.AddCompletions(new_matches); request.GetParsedLine().Shift(); request.SetCursorIndex(request.GetCursorIndex() - 1); return sub_command_object->HandleCompletion(request); } } } const char *CommandObjectMultiword::GetRepeatCommand(Args ¤t_command_args, uint32_t index) { index++; if (current_command_args.GetArgumentCount() <= index) return nullptr; CommandObject *sub_command_object = GetSubcommandObject(current_command_args[index].ref); if (sub_command_object == nullptr) return nullptr; return sub_command_object->GetRepeatCommand(current_command_args, index); } void CommandObjectMultiword::AproposAllSubCommands(llvm::StringRef prefix, llvm::StringRef search_word, StringList &commands_found, StringList &commands_help) { CommandObject::CommandMap::const_iterator pos; for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) { const char *command_name = pos->first.c_str(); CommandObject *sub_cmd_obj = pos->second.get(); StreamString complete_command_name; complete_command_name << prefix << " " << command_name; if (sub_cmd_obj->HelpTextContainsWord(search_word)) { commands_found.AppendString(complete_command_name.GetString()); commands_help.AppendString(sub_cmd_obj->GetHelp()); } if (sub_cmd_obj->IsMultiwordObject()) sub_cmd_obj->AproposAllSubCommands(complete_command_name.GetString(), search_word, commands_found, commands_help); } } CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter, const char *name, const char *help, const char *syntax, uint32_t flags) : CommandObject(interpreter, name, help, syntax, flags) {} CommandObjectProxy::~CommandObjectProxy() = default; llvm::StringRef CommandObjectProxy::GetHelpLong() { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->GetHelpLong(); return llvm::StringRef(); } bool CommandObjectProxy::IsRemovable() const { const CommandObject *proxy_command = const_cast(this)->GetProxyCommandObject(); if (proxy_command) return proxy_command->IsRemovable(); return false; } bool CommandObjectProxy::IsMultiwordObject() { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->IsMultiwordObject(); return false; } CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->GetAsMultiwordCommand(); return nullptr; } void CommandObjectProxy::GenerateHelpText(Stream &result) { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->GenerateHelpText(result); } lldb::CommandObjectSP CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd, StringList *matches) { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->GetSubcommandSP(sub_cmd, matches); return lldb::CommandObjectSP(); } CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd, StringList *matches) { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->GetSubcommandObject(sub_cmd, matches); return nullptr; } void CommandObjectProxy::AproposAllSubCommands(llvm::StringRef prefix, llvm::StringRef search_word, StringList &commands_found, StringList &commands_help) { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->AproposAllSubCommands(prefix, search_word, commands_found, commands_help); } bool CommandObjectProxy::LoadSubCommand( llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->LoadSubCommand(cmd_name, command_sp); return false; } bool CommandObjectProxy::WantsRawCommandString() { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->WantsRawCommandString(); return false; } bool CommandObjectProxy::WantsCompletion() { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->WantsCompletion(); return false; } Options *CommandObjectProxy::GetOptions() { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->GetOptions(); return nullptr; } int CommandObjectProxy::HandleCompletion(CompletionRequest &request) { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->HandleCompletion(request); - request.GetMatches().Clear(); return 0; } int CommandObjectProxy::HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->HandleArgumentCompletion(request, opt_element_vector); - request.GetMatches().Clear(); return 0; } const char *CommandObjectProxy::GetRepeatCommand(Args ¤t_command_args, uint32_t index) { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->GetRepeatCommand(current_command_args, index); return nullptr; } bool CommandObjectProxy::Execute(const char *args_string, CommandReturnObject &result) { CommandObject *proxy_command = GetProxyCommandObject(); if (proxy_command) return proxy_command->Execute(args_string, result); result.AppendError("command is not implemented"); result.SetStatus(eReturnStatusFailed); return false; } Index: vendor/lldb/dist/source/Commands/CommandObjectPlatform.cpp =================================================================== --- vendor/lldb/dist/source/Commands/CommandObjectPlatform.cpp (revision 337146) +++ vendor/lldb/dist/source/Commands/CommandObjectPlatform.cpp (revision 337147) @@ -1,1881 +1,1881 @@ //===-- CommandObjectPlatform.cpp -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes #include // Other libraries and framework includes // Project includes #include "CommandObjectPlatform.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/StringConvert.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandOptionValidators.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionGroupFile.h" #include "lldb/Interpreter/OptionGroupPlatform.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/DataExtractor.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Threading.h" using namespace lldb; using namespace lldb_private; static mode_t ParsePermissionString(const char *) = delete; static mode_t ParsePermissionString(llvm::StringRef permissions) { if (permissions.size() != 9) return (mode_t)(-1); bool user_r, user_w, user_x, group_r, group_w, group_x, world_r, world_w, world_x; user_r = (permissions[0] == 'r'); user_w = (permissions[1] == 'w'); user_x = (permissions[2] == 'x'); group_r = (permissions[3] == 'r'); group_w = (permissions[4] == 'w'); group_x = (permissions[5] == 'x'); world_r = (permissions[6] == 'r'); world_w = (permissions[7] == 'w'); world_x = (permissions[8] == 'x'); mode_t user, group, world; user = (user_r ? 4 : 0) | (user_w ? 2 : 0) | (user_x ? 1 : 0); group = (group_r ? 4 : 0) | (group_w ? 2 : 0) | (group_x ? 1 : 0); world = (world_r ? 4 : 0) | (world_w ? 2 : 0) | (world_x ? 1 : 0); return user | group | world; } static OptionDefinition g_permissions_options[] = { // clang-format off {LLDB_OPT_SET_ALL, false, "permissions-value", 'v', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePermissionsNumber, "Give out the numeric value for permissions (e.g. 757)"}, {LLDB_OPT_SET_ALL, false, "permissions-string", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePermissionsString, "Give out the string value for permissions (e.g. rwxr-xr--)."}, {LLDB_OPT_SET_ALL, false, "user-read", 'r', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Allow user to read."}, {LLDB_OPT_SET_ALL, false, "user-write", 'w', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Allow user to write."}, {LLDB_OPT_SET_ALL, false, "user-exec", 'x', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Allow user to execute."}, {LLDB_OPT_SET_ALL, false, "group-read", 'R', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Allow group to read."}, {LLDB_OPT_SET_ALL, false, "group-write", 'W', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Allow group to write."}, {LLDB_OPT_SET_ALL, false, "group-exec", 'X', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Allow group to execute."}, {LLDB_OPT_SET_ALL, false, "world-read", 'd', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Allow world to read."}, {LLDB_OPT_SET_ALL, false, "world-write", 't', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Allow world to write."}, {LLDB_OPT_SET_ALL, false, "world-exec", 'e', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Allow world to execute."}, // clang-format on }; class OptionPermissions : public OptionGroup { public: OptionPermissions() {} ~OptionPermissions() override = default; lldb_private::Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; char short_option = (char)GetDefinitions()[option_idx].short_option; switch (short_option) { case 'v': { if (option_arg.getAsInteger(8, m_permissions)) { m_permissions = 0777; error.SetErrorStringWithFormat("invalid value for permissions: %s", option_arg.str().c_str()); } } break; case 's': { mode_t perms = ParsePermissionString(option_arg); if (perms == (mode_t)-1) error.SetErrorStringWithFormat("invalid value for permissions: %s", option_arg.str().c_str()); else m_permissions = perms; } break; case 'r': m_permissions |= lldb::eFilePermissionsUserRead; break; case 'w': m_permissions |= lldb::eFilePermissionsUserWrite; break; case 'x': m_permissions |= lldb::eFilePermissionsUserExecute; break; case 'R': m_permissions |= lldb::eFilePermissionsGroupRead; break; case 'W': m_permissions |= lldb::eFilePermissionsGroupWrite; break; case 'X': m_permissions |= lldb::eFilePermissionsGroupExecute; break; case 'd': m_permissions |= lldb::eFilePermissionsWorldRead; break; case 't': m_permissions |= lldb::eFilePermissionsWorldWrite; break; case 'e': m_permissions |= lldb::eFilePermissionsWorldExecute; break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_permissions = 0; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_permissions_options); } // Instance variables to hold the values for command options. uint32_t m_permissions; private: DISALLOW_COPY_AND_ASSIGN(OptionPermissions); }; //---------------------------------------------------------------------- // "platform select " //---------------------------------------------------------------------- class CommandObjectPlatformSelect : public CommandObjectParsed { public: CommandObjectPlatformSelect(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform select", "Create a platform if needed and select it as the " "current platform.", "platform select ", 0), m_option_group(), m_platform_options( false) // Don't include the "--platform" option by passing false { m_option_group.Append(&m_platform_options, LLDB_OPT_SET_ALL, 1); m_option_group.Finalize(); } ~CommandObjectPlatformSelect() override = default; int HandleCompletion(CompletionRequest &request) override { CommandCompletions::PlatformPluginNames(GetCommandInterpreter(), request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } Options *GetOptions() override { return &m_option_group; } protected: bool DoExecute(Args &args, CommandReturnObject &result) override { if (args.GetArgumentCount() == 1) { const char *platform_name = args.GetArgumentAtIndex(0); if (platform_name && platform_name[0]) { const bool select = true; m_platform_options.SetPlatformName(platform_name); Status error; ArchSpec platform_arch; PlatformSP platform_sp(m_platform_options.CreatePlatformWithOptions( m_interpreter, ArchSpec(), select, error, platform_arch)); if (platform_sp) { m_interpreter.GetDebugger().GetPlatformList().SetSelectedPlatform( platform_sp); platform_sp->GetStatus(result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("invalid platform name"); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError( "platform create takes a platform name as an argument\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } OptionGroupOptions m_option_group; OptionGroupPlatform m_platform_options; }; //---------------------------------------------------------------------- // "platform list" //---------------------------------------------------------------------- class CommandObjectPlatformList : public CommandObjectParsed { public: CommandObjectPlatformList(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform list", "List all platforms that are available.", nullptr, 0) {} ~CommandObjectPlatformList() override = default; protected: bool DoExecute(Args &args, CommandReturnObject &result) override { Stream &ostrm = result.GetOutputStream(); ostrm.Printf("Available platforms:\n"); PlatformSP host_platform_sp(Platform::GetHostPlatform()); ostrm.Printf("%s: %s\n", host_platform_sp->GetPluginName().GetCString(), host_platform_sp->GetDescription()); uint32_t idx; for (idx = 0; 1; ++idx) { const char *plugin_name = PluginManager::GetPlatformPluginNameAtIndex(idx); if (plugin_name == nullptr) break; const char *plugin_desc = PluginManager::GetPlatformPluginDescriptionAtIndex(idx); if (plugin_desc == nullptr) break; ostrm.Printf("%s: %s\n", plugin_name, plugin_desc); } if (idx == 0) { result.AppendError("no platforms are available\n"); result.SetStatus(eReturnStatusFailed); } else result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } }; //---------------------------------------------------------------------- // "platform status" //---------------------------------------------------------------------- class CommandObjectPlatformStatus : public CommandObjectParsed { public: CommandObjectPlatformStatus(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform status", "Display status for the current platform.", nullptr, 0) {} ~CommandObjectPlatformStatus() override = default; protected: bool DoExecute(Args &args, CommandReturnObject &result) override { Stream &ostrm = result.GetOutputStream(); Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); PlatformSP platform_sp; if (target) { platform_sp = target->GetPlatform(); } if (!platform_sp) { platform_sp = m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); } if (platform_sp) { platform_sp->GetStatus(ostrm); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendError("no platform is currently selected\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; //---------------------------------------------------------------------- // "platform connect " //---------------------------------------------------------------------- class CommandObjectPlatformConnect : public CommandObjectParsed { public: CommandObjectPlatformConnect(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "platform connect", "Select the current platform by providing a connection URL.", "platform connect ", 0) {} ~CommandObjectPlatformConnect() override = default; protected: bool DoExecute(Args &args, CommandReturnObject &result) override { Stream &ostrm = result.GetOutputStream(); PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); if (platform_sp) { Status error(platform_sp->ConnectRemote(args)); if (error.Success()) { platform_sp->GetStatus(ostrm); result.SetStatus(eReturnStatusSuccessFinishResult); platform_sp->ConnectToWaitingProcesses(m_interpreter.GetDebugger(), error); if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat("%s\n", error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("no platform is currently selected\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } Options *GetOptions() override { PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); OptionGroupOptions *m_platform_options = nullptr; if (platform_sp) { m_platform_options = platform_sp->GetConnectionOptions(m_interpreter); if (m_platform_options != nullptr && !m_platform_options->m_did_finalize) m_platform_options->Finalize(); } return m_platform_options; } }; //---------------------------------------------------------------------- // "platform disconnect" //---------------------------------------------------------------------- class CommandObjectPlatformDisconnect : public CommandObjectParsed { public: CommandObjectPlatformDisconnect(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform disconnect", "Disconnect from the current platform.", "platform disconnect", 0) {} ~CommandObjectPlatformDisconnect() override = default; protected: bool DoExecute(Args &args, CommandReturnObject &result) override { PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); if (platform_sp) { if (args.GetArgumentCount() == 0) { Status error; if (platform_sp->IsConnected()) { // Cache the instance name if there is one since we are about to // disconnect and the name might go with it. const char *hostname_cstr = platform_sp->GetHostname(); std::string hostname; if (hostname_cstr) hostname.assign(hostname_cstr); error = platform_sp->DisconnectRemote(); if (error.Success()) { Stream &ostrm = result.GetOutputStream(); if (hostname.empty()) ostrm.Printf("Disconnected from \"%s\"\n", platform_sp->GetPluginName().GetCString()); else ostrm.Printf("Disconnected from \"%s\"\n", hostname.c_str()); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat("%s", error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { // Not connected... result.AppendErrorWithFormat( "not connected to '%s'", platform_sp->GetPluginName().GetCString()); result.SetStatus(eReturnStatusFailed); } } else { // Bad args result.AppendError( "\"platform disconnect\" doesn't take any arguments"); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("no platform is currently selected"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; //---------------------------------------------------------------------- // "platform settings" //---------------------------------------------------------------------- class CommandObjectPlatformSettings : public CommandObjectParsed { public: CommandObjectPlatformSettings(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform settings", "Set settings for the current target's platform, " "or for a platform by name.", "platform settings", 0), m_options(), m_option_working_dir(LLDB_OPT_SET_1, false, "working-dir", 'w', 0, eArgTypePath, "The working directory for the platform.") { m_options.Append(&m_option_working_dir, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); } ~CommandObjectPlatformSettings() override = default; protected: bool DoExecute(Args &args, CommandReturnObject &result) override { PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); if (platform_sp) { if (m_option_working_dir.GetOptionValue().OptionWasSet()) platform_sp->SetWorkingDirectory( m_option_working_dir.GetOptionValue().GetCurrentValue()); } else { result.AppendError("no platform is currently selected"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } Options *GetOptions() override { if (!m_options.DidFinalize()) m_options.Finalize(); return &m_options; } protected: OptionGroupOptions m_options; OptionGroupFile m_option_working_dir; }; //---------------------------------------------------------------------- // "platform mkdir" //---------------------------------------------------------------------- class CommandObjectPlatformMkDir : public CommandObjectParsed { public: CommandObjectPlatformMkDir(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform mkdir", "Make a new directory on the remote end.", nullptr, 0), m_options() {} ~CommandObjectPlatformMkDir() override = default; bool DoExecute(Args &args, CommandReturnObject &result) override { PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); if (platform_sp) { std::string cmd_line; args.GetCommandString(cmd_line); uint32_t mode; const OptionPermissions *options_permissions = (const OptionPermissions *)m_options.GetGroupWithOption('r'); if (options_permissions) mode = options_permissions->m_permissions; else mode = lldb::eFilePermissionsUserRWX | lldb::eFilePermissionsGroupRWX | lldb::eFilePermissionsWorldRX; Status error = platform_sp->MakeDirectory(FileSpec{cmd_line, false}, mode); if (error.Success()) { result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("no platform currently selected\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } Options *GetOptions() override { if (!m_options.DidFinalize()) { m_options.Append(new OptionPermissions()); m_options.Finalize(); } return &m_options; } OptionGroupOptions m_options; }; //---------------------------------------------------------------------- // "platform fopen" //---------------------------------------------------------------------- class CommandObjectPlatformFOpen : public CommandObjectParsed { public: CommandObjectPlatformFOpen(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform file open", "Open a file on the remote end.", nullptr, 0), m_options() {} ~CommandObjectPlatformFOpen() override = default; bool DoExecute(Args &args, CommandReturnObject &result) override { PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); if (platform_sp) { Status error; std::string cmd_line; args.GetCommandString(cmd_line); mode_t perms; const OptionPermissions *options_permissions = (const OptionPermissions *)m_options.GetGroupWithOption('r'); if (options_permissions) perms = options_permissions->m_permissions; else perms = lldb::eFilePermissionsUserRW | lldb::eFilePermissionsGroupRW | lldb::eFilePermissionsWorldRead; lldb::user_id_t fd = platform_sp->OpenFile( FileSpec(cmd_line, false), File::eOpenOptionRead | File::eOpenOptionWrite | File::eOpenOptionAppend | File::eOpenOptionCanCreate, perms, error); if (error.Success()) { result.AppendMessageWithFormat("File Descriptor = %" PRIu64 "\n", fd); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("no platform currently selected\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } Options *GetOptions() override { if (!m_options.DidFinalize()) { m_options.Append(new OptionPermissions()); m_options.Finalize(); } return &m_options; } OptionGroupOptions m_options; }; //---------------------------------------------------------------------- // "platform fclose" //---------------------------------------------------------------------- class CommandObjectPlatformFClose : public CommandObjectParsed { public: CommandObjectPlatformFClose(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform file close", "Close a file on the remote end.", nullptr, 0) {} ~CommandObjectPlatformFClose() override = default; bool DoExecute(Args &args, CommandReturnObject &result) override { PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); if (platform_sp) { std::string cmd_line; args.GetCommandString(cmd_line); const lldb::user_id_t fd = StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX); Status error; bool success = platform_sp->CloseFile(fd, error); if (success) { result.AppendMessageWithFormat("file %" PRIu64 " closed.\n", fd); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("no platform currently selected\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; //---------------------------------------------------------------------- // "platform fread" //---------------------------------------------------------------------- static OptionDefinition g_platform_fread_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeIndex, "Offset into the file at which to start reading." }, { LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCount, "Number of bytes to read from the file." }, // clang-format on }; class CommandObjectPlatformFRead : public CommandObjectParsed { public: CommandObjectPlatformFRead(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform file read", "Read data from a file on the remote end.", nullptr, 0), m_options() {} ~CommandObjectPlatformFRead() override = default; bool DoExecute(Args &args, CommandReturnObject &result) override { PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); if (platform_sp) { std::string cmd_line; args.GetCommandString(cmd_line); const lldb::user_id_t fd = StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX); std::string buffer(m_options.m_count, 0); Status error; uint32_t retcode = platform_sp->ReadFile( fd, m_options.m_offset, &buffer[0], m_options.m_count, error); result.AppendMessageWithFormat("Return = %d\n", retcode); result.AppendMessageWithFormat("Data = \"%s\"\n", buffer.c_str()); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendError("no platform currently selected\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } Options *GetOptions() override { return &m_options; } protected: class CommandOptions : public Options { public: CommandOptions() : Options() {} ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; char short_option = (char)m_getopt_table[option_idx].val; switch (short_option) { case 'o': if (option_arg.getAsInteger(0, m_offset)) error.SetErrorStringWithFormat("invalid offset: '%s'", option_arg.str().c_str()); break; case 'c': if (option_arg.getAsInteger(0, m_count)) error.SetErrorStringWithFormat("invalid offset: '%s'", option_arg.str().c_str()); break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_offset = 0; m_count = 1; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_platform_fread_options); } // Instance variables to hold the values for command options. uint32_t m_offset; uint32_t m_count; }; CommandOptions m_options; }; //---------------------------------------------------------------------- // "platform fwrite" //---------------------------------------------------------------------- static OptionDefinition g_platform_fwrite_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeIndex, "Offset into the file at which to start reading." }, { LLDB_OPT_SET_1, false, "data", 'd', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeValue, "Text to write to the file." }, // clang-format on }; class CommandObjectPlatformFWrite : public CommandObjectParsed { public: CommandObjectPlatformFWrite(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform file write", "Write data to a file on the remote end.", nullptr, 0), m_options() {} ~CommandObjectPlatformFWrite() override = default; bool DoExecute(Args &args, CommandReturnObject &result) override { PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); if (platform_sp) { std::string cmd_line; args.GetCommandString(cmd_line); Status error; const lldb::user_id_t fd = StringConvert::ToUInt64(cmd_line.c_str(), UINT64_MAX); uint32_t retcode = platform_sp->WriteFile(fd, m_options.m_offset, &m_options.m_data[0], m_options.m_data.size(), error); result.AppendMessageWithFormat("Return = %d\n", retcode); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendError("no platform currently selected\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } Options *GetOptions() override { return &m_options; } protected: class CommandOptions : public Options { public: CommandOptions() : Options() {} ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; char short_option = (char)m_getopt_table[option_idx].val; switch (short_option) { case 'o': if (option_arg.getAsInteger(0, m_offset)) error.SetErrorStringWithFormat("invalid offset: '%s'", option_arg.str().c_str()); break; case 'd': m_data.assign(option_arg); break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_offset = 0; m_data.clear(); } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_platform_fwrite_options); } // Instance variables to hold the values for command options. uint32_t m_offset; std::string m_data; }; CommandOptions m_options; }; class CommandObjectPlatformFile : public CommandObjectMultiword { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ CommandObjectPlatformFile(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "platform file", "Commands to access files on the current platform.", "platform file [open|close|read|write] ...") { LoadSubCommand( "open", CommandObjectSP(new CommandObjectPlatformFOpen(interpreter))); LoadSubCommand( "close", CommandObjectSP(new CommandObjectPlatformFClose(interpreter))); LoadSubCommand( "read", CommandObjectSP(new CommandObjectPlatformFRead(interpreter))); LoadSubCommand( "write", CommandObjectSP(new CommandObjectPlatformFWrite(interpreter))); } ~CommandObjectPlatformFile() override = default; private: //------------------------------------------------------------------ // For CommandObjectPlatform only //------------------------------------------------------------------ DISALLOW_COPY_AND_ASSIGN(CommandObjectPlatformFile); }; //---------------------------------------------------------------------- // "platform get-file remote-file-path host-file-path" //---------------------------------------------------------------------- class CommandObjectPlatformGetFile : public CommandObjectParsed { public: CommandObjectPlatformGetFile(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "platform get-file", "Transfer a file from the remote end to the local host.", "platform get-file ", 0) { SetHelpLong( R"(Examples: (lldb) platform get-file /the/remote/file/path /the/local/file/path Transfer a file from the remote end with file path /the/remote/file/path to the local host.)"); CommandArgumentEntry arg1, arg2; CommandArgumentData file_arg_remote, file_arg_host; // Define the first (and only) variant of this arg. file_arg_remote.arg_type = eArgTypeFilename; file_arg_remote.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(file_arg_remote); // Define the second (and only) variant of this arg. file_arg_host.arg_type = eArgTypeFilename; file_arg_host.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg2.push_back(file_arg_host); // Push the data for the first and the second arguments into the // m_arguments vector. m_arguments.push_back(arg1); m_arguments.push_back(arg2); } ~CommandObjectPlatformGetFile() override = default; bool DoExecute(Args &args, CommandReturnObject &result) override { // If the number of arguments is incorrect, issue an error message. if (args.GetArgumentCount() != 2) { result.GetErrorStream().Printf("error: required arguments missing; " "specify both the source and destination " "file paths\n"); result.SetStatus(eReturnStatusFailed); return false; } PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); if (platform_sp) { const char *remote_file_path = args.GetArgumentAtIndex(0); const char *local_file_path = args.GetArgumentAtIndex(1); Status error = platform_sp->GetFile(FileSpec(remote_file_path, false), FileSpec(local_file_path, false)); if (error.Success()) { result.AppendMessageWithFormat( "successfully get-file from %s (remote) to %s (host)\n", remote_file_path, local_file_path); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendMessageWithFormat("get-file failed: %s\n", error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("no platform currently selected\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; //---------------------------------------------------------------------- // "platform get-size remote-file-path" //---------------------------------------------------------------------- class CommandObjectPlatformGetSize : public CommandObjectParsed { public: CommandObjectPlatformGetSize(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform get-size", "Get the file size from the remote end.", "platform get-size ", 0) { SetHelpLong( R"(Examples: (lldb) platform get-size /the/remote/file/path Get the file size from the remote end with path /the/remote/file/path.)"); CommandArgumentEntry arg1; CommandArgumentData file_arg_remote; // Define the first (and only) variant of this arg. file_arg_remote.arg_type = eArgTypeFilename; file_arg_remote.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(file_arg_remote); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); } ~CommandObjectPlatformGetSize() override = default; bool DoExecute(Args &args, CommandReturnObject &result) override { // If the number of arguments is incorrect, issue an error message. if (args.GetArgumentCount() != 1) { result.GetErrorStream().Printf("error: required argument missing; " "specify the source file path as the only " "argument\n"); result.SetStatus(eReturnStatusFailed); return false; } PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); if (platform_sp) { std::string remote_file_path(args.GetArgumentAtIndex(0)); user_id_t size = platform_sp->GetFileSize(FileSpec(remote_file_path, false)); if (size != UINT64_MAX) { result.AppendMessageWithFormat("File size of %s (remote): %" PRIu64 "\n", remote_file_path.c_str(), size); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendMessageWithFormat( "Error getting file size of %s (remote)\n", remote_file_path.c_str()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("no platform currently selected\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; //---------------------------------------------------------------------- // "platform put-file" //---------------------------------------------------------------------- class CommandObjectPlatformPutFile : public CommandObjectParsed { public: CommandObjectPlatformPutFile(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "platform put-file", "Transfer a file from this system to the remote end.", nullptr, 0) { } ~CommandObjectPlatformPutFile() override = default; bool DoExecute(Args &args, CommandReturnObject &result) override { const char *src = args.GetArgumentAtIndex(0); const char *dst = args.GetArgumentAtIndex(1); FileSpec src_fs(src, true); FileSpec dst_fs(dst ? dst : src_fs.GetFilename().GetCString(), false); PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); if (platform_sp) { Status error(platform_sp->PutFile(src_fs, dst_fs)); if (error.Success()) { result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("no platform currently selected\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; //---------------------------------------------------------------------- // "platform process launch" //---------------------------------------------------------------------- class CommandObjectPlatformProcessLaunch : public CommandObjectParsed { public: CommandObjectPlatformProcessLaunch(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform process launch", "Launch a new process on a remote platform.", "platform process launch program", eCommandRequiresTarget | eCommandTryTargetAPILock), m_options() {} ~CommandObjectPlatformProcessLaunch() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &args, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); PlatformSP platform_sp; if (target) { platform_sp = target->GetPlatform(); } if (!platform_sp) { platform_sp = m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); } if (platform_sp) { Status error; const size_t argc = args.GetArgumentCount(); Target *target = m_exe_ctx.GetTargetPtr(); Module *exe_module = target->GetExecutableModulePointer(); if (exe_module) { m_options.launch_info.GetExecutableFile() = exe_module->GetFileSpec(); llvm::SmallString exe_path; m_options.launch_info.GetExecutableFile().GetPath(exe_path); if (!exe_path.empty()) m_options.launch_info.GetArguments().AppendArgument(exe_path); m_options.launch_info.GetArchitecture() = exe_module->GetArchitecture(); } if (argc > 0) { if (m_options.launch_info.GetExecutableFile()) { // We already have an executable file, so we will use this and all // arguments to this function are extra arguments m_options.launch_info.GetArguments().AppendArguments(args); } else { // We don't have any file yet, so the first argument is our // executable, and the rest are program arguments const bool first_arg_is_executable = true; m_options.launch_info.SetArguments(args, first_arg_is_executable); } } if (m_options.launch_info.GetExecutableFile()) { Debugger &debugger = m_interpreter.GetDebugger(); if (argc == 0) target->GetRunArguments(m_options.launch_info.GetArguments()); ProcessSP process_sp(platform_sp->DebugProcess( m_options.launch_info, debugger, target, error)); if (process_sp && process_sp->IsAlive()) { result.SetStatus(eReturnStatusSuccessFinishNoResult); return true; } if (error.Success()) result.AppendError("process launch failed"); else result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } else { result.AppendError("'platform process launch' uses the current target " "file and arguments, or the executable and its " "arguments can be specified in this command"); result.SetStatus(eReturnStatusFailed); return false; } } else { result.AppendError("no platform is selected\n"); } return result.Succeeded(); } protected: ProcessLaunchCommandOptions m_options; }; //---------------------------------------------------------------------- // "platform process list" //---------------------------------------------------------------------- OptionDefinition g_platform_process_list_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "pid", 'p', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePid, "List the process info for a specific process ID." }, { LLDB_OPT_SET_2, true, "name", 'n', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeProcessName, "Find processes with executable basenames that match a string." }, { LLDB_OPT_SET_3, true, "ends-with", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeProcessName, "Find processes with executable basenames that end with a string." }, { LLDB_OPT_SET_4, true, "starts-with", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeProcessName, "Find processes with executable basenames that start with a string." }, { LLDB_OPT_SET_5, true, "contains", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeProcessName, "Find processes with executable basenames that contain a string." }, { LLDB_OPT_SET_6, true, "regex", 'r', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeRegularExpression, "Find processes with executable basenames that match a regular expression." }, { LLDB_OPT_SET_FROM_TO(2, 6), false, "parent", 'P', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePid, "Find processes that have a matching parent process ID." }, { LLDB_OPT_SET_FROM_TO(2, 6), false, "uid", 'u', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger, "Find processes that have a matching user ID." }, { LLDB_OPT_SET_FROM_TO(2, 6), false, "euid", 'U', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger, "Find processes that have a matching effective user ID." }, { LLDB_OPT_SET_FROM_TO(2, 6), false, "gid", 'g', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger, "Find processes that have a matching group ID." }, { LLDB_OPT_SET_FROM_TO(2, 6), false, "egid", 'G', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger, "Find processes that have a matching effective group ID." }, { LLDB_OPT_SET_FROM_TO(2, 6), false, "arch", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeArchitecture, "Find processes that have a matching architecture." }, { LLDB_OPT_SET_FROM_TO(1, 6), false, "show-args", 'A', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Show process arguments instead of the process executable basename." }, { LLDB_OPT_SET_FROM_TO(1, 6), false, "verbose", 'v', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Enable verbose output." }, // clang-format on }; class CommandObjectPlatformProcessList : public CommandObjectParsed { public: CommandObjectPlatformProcessList(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform process list", "List processes on a remote platform by name, pid, " "or many other matching attributes.", "platform process list", 0), m_options() {} ~CommandObjectPlatformProcessList() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &args, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); PlatformSP platform_sp; if (target) { platform_sp = target->GetPlatform(); } if (!platform_sp) { platform_sp = m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); } if (platform_sp) { Status error; if (args.GetArgumentCount() == 0) { if (platform_sp) { Stream &ostrm = result.GetOutputStream(); lldb::pid_t pid = m_options.match_info.GetProcessInfo().GetProcessID(); if (pid != LLDB_INVALID_PROCESS_ID) { ProcessInstanceInfo proc_info; if (platform_sp->GetProcessInfo(pid, proc_info)) { ProcessInstanceInfo::DumpTableHeader(ostrm, platform_sp.get(), m_options.show_args, m_options.verbose); proc_info.DumpAsTableRow(ostrm, platform_sp.get(), m_options.show_args, m_options.verbose); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat( "no process found with pid = %" PRIu64 "\n", pid); result.SetStatus(eReturnStatusFailed); } } else { ProcessInstanceInfoList proc_infos; const uint32_t matches = platform_sp->FindProcesses(m_options.match_info, proc_infos); const char *match_desc = nullptr; const char *match_name = m_options.match_info.GetProcessInfo().GetName(); if (match_name && match_name[0]) { switch (m_options.match_info.GetNameMatchType()) { case NameMatch::Ignore: break; case NameMatch::Equals: match_desc = "matched"; break; case NameMatch::Contains: match_desc = "contained"; break; case NameMatch::StartsWith: match_desc = "started with"; break; case NameMatch::EndsWith: match_desc = "ended with"; break; case NameMatch::RegularExpression: match_desc = "matched the regular expression"; break; } } if (matches == 0) { if (match_desc) result.AppendErrorWithFormat( "no processes were found that %s \"%s\" on the \"%s\" " "platform\n", match_desc, match_name, platform_sp->GetPluginName().GetCString()); else result.AppendErrorWithFormat( "no processes were found on the \"%s\" platform\n", platform_sp->GetPluginName().GetCString()); result.SetStatus(eReturnStatusFailed); } else { result.AppendMessageWithFormat( "%u matching process%s found on \"%s\"", matches, matches > 1 ? "es were" : " was", platform_sp->GetName().GetCString()); if (match_desc) result.AppendMessageWithFormat(" whose name %s \"%s\"", match_desc, match_name); result.AppendMessageWithFormat("\n"); ProcessInstanceInfo::DumpTableHeader(ostrm, platform_sp.get(), m_options.show_args, m_options.verbose); for (uint32_t i = 0; i < matches; ++i) { proc_infos.GetProcessInfoAtIndex(i).DumpAsTableRow( ostrm, platform_sp.get(), m_options.show_args, m_options.verbose); } } } } } else { result.AppendError("invalid args: process list takes only options\n"); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("no platform is selected\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } class CommandOptions : public Options { public: CommandOptions() : Options(), match_info(), show_args(false), verbose(false) { static llvm::once_flag g_once_flag; llvm::call_once(g_once_flag, []() { PosixPlatformCommandOptionValidator *posix_validator = new PosixPlatformCommandOptionValidator(); for (auto &Option : g_platform_process_list_options) { switch (Option.short_option) { case 'u': case 'U': case 'g': case 'G': Option.validator = posix_validator; break; default: break; } } }); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; bool success = false; uint32_t id = LLDB_INVALID_PROCESS_ID; success = !option_arg.getAsInteger(0, id); switch (short_option) { case 'p': { match_info.GetProcessInfo().SetProcessID(id); if (!success) error.SetErrorStringWithFormat("invalid process ID string: '%s'", option_arg.str().c_str()); break; } case 'P': match_info.GetProcessInfo().SetParentProcessID(id); if (!success) error.SetErrorStringWithFormat( "invalid parent process ID string: '%s'", option_arg.str().c_str()); break; case 'u': match_info.GetProcessInfo().SetUserID(success ? id : UINT32_MAX); if (!success) error.SetErrorStringWithFormat("invalid user ID string: '%s'", option_arg.str().c_str()); break; case 'U': match_info.GetProcessInfo().SetEffectiveUserID(success ? id : UINT32_MAX); if (!success) error.SetErrorStringWithFormat( "invalid effective user ID string: '%s'", option_arg.str().c_str()); break; case 'g': match_info.GetProcessInfo().SetGroupID(success ? id : UINT32_MAX); if (!success) error.SetErrorStringWithFormat("invalid group ID string: '%s'", option_arg.str().c_str()); break; case 'G': match_info.GetProcessInfo().SetEffectiveGroupID(success ? id : UINT32_MAX); if (!success) error.SetErrorStringWithFormat( "invalid effective group ID string: '%s'", option_arg.str().c_str()); break; case 'a': { TargetSP target_sp = execution_context ? execution_context->GetTargetSP() : TargetSP(); DebuggerSP debugger_sp = target_sp ? target_sp->GetDebugger().shared_from_this() : DebuggerSP(); PlatformSP platform_sp = debugger_sp ? debugger_sp->GetPlatformList().GetSelectedPlatform() : PlatformSP(); match_info.GetProcessInfo().GetArchitecture() = Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg); } break; case 'n': match_info.GetProcessInfo().GetExecutableFile().SetFile( option_arg, false, FileSpec::Style::native); match_info.SetNameMatchType(NameMatch::Equals); break; case 'e': match_info.GetProcessInfo().GetExecutableFile().SetFile( option_arg, false, FileSpec::Style::native); match_info.SetNameMatchType(NameMatch::EndsWith); break; case 's': match_info.GetProcessInfo().GetExecutableFile().SetFile( option_arg, false, FileSpec::Style::native); match_info.SetNameMatchType(NameMatch::StartsWith); break; case 'c': match_info.GetProcessInfo().GetExecutableFile().SetFile( option_arg, false, FileSpec::Style::native); match_info.SetNameMatchType(NameMatch::Contains); break; case 'r': match_info.GetProcessInfo().GetExecutableFile().SetFile( option_arg, false, FileSpec::Style::native); match_info.SetNameMatchType(NameMatch::RegularExpression); break; case 'A': show_args = true; break; case 'v': verbose = true; break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { match_info.Clear(); show_args = false; verbose = false; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_platform_process_list_options); } // Instance variables to hold the values for command options. ProcessInstanceInfoMatch match_info; bool show_args; bool verbose; }; CommandOptions m_options; }; //---------------------------------------------------------------------- // "platform process info" //---------------------------------------------------------------------- class CommandObjectPlatformProcessInfo : public CommandObjectParsed { public: CommandObjectPlatformProcessInfo(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "platform process info", "Get detailed information for one or more process by process ID.", "platform process info [ ...]", 0) { CommandArgumentEntry arg; CommandArgumentData pid_args; // Define the first (and only) variant of this arg. pid_args.arg_type = eArgTypePid; pid_args.arg_repetition = eArgRepeatStar; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(pid_args); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectPlatformProcessInfo() override = default; protected: bool DoExecute(Args &args, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); PlatformSP platform_sp; if (target) { platform_sp = target->GetPlatform(); } if (!platform_sp) { platform_sp = m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); } if (platform_sp) { const size_t argc = args.GetArgumentCount(); if (argc > 0) { Status error; if (platform_sp->IsConnected()) { Stream &ostrm = result.GetOutputStream(); for (auto &entry : args.entries()) { lldb::pid_t pid; if (entry.ref.getAsInteger(0, pid)) { result.AppendErrorWithFormat("invalid process ID argument '%s'", entry.ref.str().c_str()); result.SetStatus(eReturnStatusFailed); break; } else { ProcessInstanceInfo proc_info; if (platform_sp->GetProcessInfo(pid, proc_info)) { ostrm.Printf("Process information for process %" PRIu64 ":\n", pid); proc_info.Dump(ostrm, platform_sp.get()); } else { ostrm.Printf("error: no process information is available for " "process %" PRIu64 "\n", pid); } ostrm.EOL(); } } } else { // Not connected... result.AppendErrorWithFormat( "not connected to '%s'", platform_sp->GetPluginName().GetCString()); result.SetStatus(eReturnStatusFailed); } } else { // No args result.AppendError("one or more process id(s) must be specified"); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("no platform is currently selected"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; static OptionDefinition g_platform_process_attach_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "plugin", 'P', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePlugin, "Name of the process plugin you want to use." }, { LLDB_OPT_SET_1, false, "pid", 'p', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePid, "The process ID of an existing process to attach to." }, { LLDB_OPT_SET_2, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeProcessName, "The name of the process to attach to." }, { LLDB_OPT_SET_2, false, "waitfor", 'w', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Wait for the process with to launch." }, // clang-format on }; class CommandObjectPlatformProcessAttach : public CommandObjectParsed { public: class CommandOptions : public Options { public: CommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; char short_option = (char)m_getopt_table[option_idx].val; switch (short_option) { case 'p': { lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; if (option_arg.getAsInteger(0, pid)) { error.SetErrorStringWithFormat("invalid process ID '%s'", option_arg.str().c_str()); } else { attach_info.SetProcessID(pid); } } break; case 'P': attach_info.SetProcessPluginName(option_arg); break; case 'n': attach_info.GetExecutableFile().SetFile(option_arg, false, FileSpec::Style::native); break; case 'w': attach_info.SetWaitForLaunch(true); break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { attach_info.Clear(); } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_platform_process_attach_options); } bool HandleOptionArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector, int opt_element_index, CommandInterpreter &interpreter) override { int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos; int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index; // We are only completing the name option for now... if (GetDefinitions()[opt_defs_index].short_option == 'n') { // Are we in the name? // Look to see if there is a -P argument provided, and if so use that // plugin, otherwise use the default plugin. const char *partial_name = nullptr; partial_name = request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos); PlatformSP platform_sp(interpreter.GetPlatform(true)); if (platform_sp) { ProcessInstanceInfoList process_infos; ProcessInstanceInfoMatch match_info; if (partial_name) { match_info.GetProcessInfo().GetExecutableFile().SetFile( partial_name, false, FileSpec::Style::native); match_info.SetNameMatchType(NameMatch::StartsWith); } platform_sp->FindProcesses(match_info, process_infos); const uint32_t num_matches = process_infos.GetSize(); if (num_matches > 0) { for (uint32_t i = 0; i < num_matches; ++i) { - request.GetMatches().AppendString( + request.AddCompletion(llvm::StringRef( process_infos.GetProcessNameAtIndex(i), - process_infos.GetProcessNameLengthAtIndex(i)); + process_infos.GetProcessNameLengthAtIndex(i))); } } } } return false; } // Options table: Required for subclasses of Options. static OptionDefinition g_option_table[]; // Instance variables to hold the values for command options. ProcessAttachInfo attach_info; }; CommandObjectPlatformProcessAttach(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "platform process attach", "Attach to a process.", "platform process attach "), m_options() {} ~CommandObjectPlatformProcessAttach() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); if (platform_sp) { Status err; ProcessSP remote_process_sp = platform_sp->Attach( m_options.attach_info, m_interpreter.GetDebugger(), nullptr, err); if (err.Fail()) { result.AppendError(err.AsCString()); result.SetStatus(eReturnStatusFailed); } else if (!remote_process_sp) { result.AppendError("could not attach: unknown reason"); result.SetStatus(eReturnStatusFailed); } else result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendError("no platform is currently selected"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } Options *GetOptions() override { return &m_options; } protected: CommandOptions m_options; }; class CommandObjectPlatformProcess : public CommandObjectMultiword { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ CommandObjectPlatformProcess(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "platform process", "Commands to query, launch and attach to " "processes on the current platform.", "platform process [attach|launch|list] ...") { LoadSubCommand( "attach", CommandObjectSP(new CommandObjectPlatformProcessAttach(interpreter))); LoadSubCommand( "launch", CommandObjectSP(new CommandObjectPlatformProcessLaunch(interpreter))); LoadSubCommand("info", CommandObjectSP(new CommandObjectPlatformProcessInfo( interpreter))); LoadSubCommand("list", CommandObjectSP(new CommandObjectPlatformProcessList( interpreter))); } ~CommandObjectPlatformProcess() override = default; private: //------------------------------------------------------------------ // For CommandObjectPlatform only //------------------------------------------------------------------ DISALLOW_COPY_AND_ASSIGN(CommandObjectPlatformProcess); }; //---------------------------------------------------------------------- // "platform shell" //---------------------------------------------------------------------- static OptionDefinition g_platform_shell_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "timeout", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeValue, "Seconds to wait for the remote host to finish running the command." }, // clang-format on }; class CommandObjectPlatformShell : public CommandObjectRaw { public: class CommandOptions : public Options { public: CommandOptions() : Options() {} ~CommandOptions() override = default; llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_platform_shell_options); } Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const char short_option = (char)GetDefinitions()[option_idx].short_option; switch (short_option) { case 't': uint32_t timeout_sec; if (option_arg.getAsInteger(10, timeout_sec)) error.SetErrorStringWithFormat( "could not convert \"%s\" to a numeric value.", option_arg.str().c_str()); else timeout = std::chrono::seconds(timeout_sec); break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override {} Timeout timeout = std::chrono::seconds(10); }; CommandObjectPlatformShell(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "platform shell", "Run a shell command on the current platform.", "platform shell ", 0), m_options() {} ~CommandObjectPlatformShell() override = default; Options *GetOptions() override { return &m_options; } bool DoExecute(llvm::StringRef raw_command_line, CommandReturnObject &result) override { ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext(); m_options.NotifyOptionParsingStarting(&exe_ctx); // Print out an usage syntax on an empty command line. if (raw_command_line.empty()) { result.GetOutputStream().Printf("%s\n", this->GetSyntax().str().c_str()); return true; } OptionsWithRaw args(raw_command_line); const char *expr = args.GetRawPart().c_str(); if (args.HasArgs()) if (!ParseOptions(args.GetArgs(), result)) return false; PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); Status error; if (platform_sp) { FileSpec working_dir{}; std::string output; int status = -1; int signo = -1; error = (platform_sp->RunShellCommand(expr, working_dir, &status, &signo, &output, m_options.timeout)); if (!output.empty()) result.GetOutputStream().PutCString(output); if (status > 0) { if (signo > 0) { const char *signo_cstr = Host::GetSignalAsCString(signo); if (signo_cstr) result.GetOutputStream().Printf( "error: command returned with status %i and signal %s\n", status, signo_cstr); else result.GetOutputStream().Printf( "error: command returned with status %i and signal %i\n", status, signo); } else result.GetOutputStream().Printf( "error: command returned with status %i\n", status); } } else { result.GetOutputStream().Printf( "error: cannot run remote shell commands without a platform\n"); error.SetErrorString( "error: cannot run remote shell commands without a platform"); } if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } else { result.SetStatus(eReturnStatusSuccessFinishResult); } return true; } CommandOptions m_options; }; //---------------------------------------------------------------------- // "platform install" - install a target to a remote end //---------------------------------------------------------------------- class CommandObjectPlatformInstall : public CommandObjectParsed { public: CommandObjectPlatformInstall(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "platform target-install", "Install a target (bundle or executable file) to the remote end.", "platform target-install ", 0) {} ~CommandObjectPlatformInstall() override = default; bool DoExecute(Args &args, CommandReturnObject &result) override { if (args.GetArgumentCount() != 2) { result.AppendError("platform target-install takes two arguments"); result.SetStatus(eReturnStatusFailed); return false; } // TODO: move the bulk of this code over to the platform itself FileSpec src(args.GetArgumentAtIndex(0), true); FileSpec dst(args.GetArgumentAtIndex(1), false); if (!src.Exists()) { result.AppendError("source location does not exist or is not accessible"); result.SetStatus(eReturnStatusFailed); return false; } PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); if (!platform_sp) { result.AppendError("no platform currently selected"); result.SetStatus(eReturnStatusFailed); return false; } Status error = platform_sp->Install(src, dst); if (error.Success()) { result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendErrorWithFormat("install failed: %s", error.AsCString()); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "platform", "Commands to manage and create platforms.", "platform [connect|disconnect|info|list|status|select] ...") { LoadSubCommand("select", CommandObjectSP(new CommandObjectPlatformSelect(interpreter))); LoadSubCommand("list", CommandObjectSP(new CommandObjectPlatformList(interpreter))); LoadSubCommand("status", CommandObjectSP(new CommandObjectPlatformStatus(interpreter))); LoadSubCommand("connect", CommandObjectSP( new CommandObjectPlatformConnect(interpreter))); LoadSubCommand( "disconnect", CommandObjectSP(new CommandObjectPlatformDisconnect(interpreter))); LoadSubCommand("settings", CommandObjectSP(new CommandObjectPlatformSettings( interpreter))); LoadSubCommand("mkdir", CommandObjectSP(new CommandObjectPlatformMkDir(interpreter))); LoadSubCommand("file", CommandObjectSP(new CommandObjectPlatformFile(interpreter))); LoadSubCommand("get-file", CommandObjectSP(new CommandObjectPlatformGetFile( interpreter))); LoadSubCommand("get-size", CommandObjectSP(new CommandObjectPlatformGetSize( interpreter))); LoadSubCommand("put-file", CommandObjectSP(new CommandObjectPlatformPutFile( interpreter))); LoadSubCommand("process", CommandObjectSP( new CommandObjectPlatformProcess(interpreter))); LoadSubCommand("shell", CommandObjectSP(new CommandObjectPlatformShell(interpreter))); LoadSubCommand( "target-install", CommandObjectSP(new CommandObjectPlatformInstall(interpreter))); } CommandObjectPlatform::~CommandObjectPlatform() = default; Index: vendor/lldb/dist/source/Commands/CommandObjectPlugin.cpp =================================================================== --- vendor/lldb/dist/source/Commands/CommandObjectPlugin.cpp (revision 337146) +++ vendor/lldb/dist/source/Commands/CommandObjectPlugin.cpp (revision 337147) @@ -1,87 +1,87 @@ //===-- CommandObjectPlugin.cpp ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "CommandObjectPlugin.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" using namespace lldb; using namespace lldb_private; class CommandObjectPluginLoad : public CommandObjectParsed { public: CommandObjectPluginLoad(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "plugin load", "Import a dylib that implements an LLDB plugin.", nullptr) { CommandArgumentEntry arg1; CommandArgumentData cmd_arg; // Define the first (and only) variant of this arg. cmd_arg.arg_type = eArgTypeFilename; cmd_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(cmd_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); } ~CommandObjectPluginLoad() override = default; int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { size_t argc = command.GetArgumentCount(); if (argc != 1) { result.AppendError("'plugin load' requires one argument"); result.SetStatus(eReturnStatusFailed); return false; } Status error; FileSpec dylib_fspec(command[0].ref, true); if (m_interpreter.GetDebugger().LoadPlugin(dylib_fspec, error)) result.SetStatus(eReturnStatusSuccessFinishResult); else { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; CommandObjectPlugin::CommandObjectPlugin(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "plugin", "Commands for managing LLDB plugins.", "plugin []") { LoadSubCommand("load", CommandObjectSP(new CommandObjectPluginLoad(interpreter))); } CommandObjectPlugin::~CommandObjectPlugin() = default; Index: vendor/lldb/dist/source/Commands/CommandObjectProcess.cpp =================================================================== --- vendor/lldb/dist/source/Commands/CommandObjectProcess.cpp (revision 337146) +++ vendor/lldb/dist/source/Commands/CommandObjectProcess.cpp (revision 337147) @@ -1,1641 +1,1641 @@ //===-- CommandObjectProcess.cpp --------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "CommandObjectProcess.h" #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/BreakpointSite.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Host/Host.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/StringConvert.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Interpreter/Options.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/Args.h" using namespace lldb; using namespace lldb_private; class CommandObjectProcessLaunchOrAttach : public CommandObjectParsed { public: CommandObjectProcessLaunchOrAttach(CommandInterpreter &interpreter, const char *name, const char *help, const char *syntax, uint32_t flags, const char *new_process_action) : CommandObjectParsed(interpreter, name, help, syntax, flags), m_new_process_action(new_process_action) {} ~CommandObjectProcessLaunchOrAttach() override = default; protected: bool StopProcessIfNecessary(Process *process, StateType &state, CommandReturnObject &result) { state = eStateInvalid; if (process) { state = process->GetState(); if (process->IsAlive() && state != eStateConnected) { char message[1024]; if (process->GetState() == eStateAttaching) ::snprintf(message, sizeof(message), "There is a pending attach, abort it and %s?", m_new_process_action.c_str()); else if (process->GetShouldDetach()) ::snprintf(message, sizeof(message), "There is a running process, detach from it and %s?", m_new_process_action.c_str()); else ::snprintf(message, sizeof(message), "There is a running process, kill it and %s?", m_new_process_action.c_str()); if (!m_interpreter.Confirm(message, true)) { result.SetStatus(eReturnStatusFailed); return false; } else { if (process->GetShouldDetach()) { bool keep_stopped = false; Status detach_error(process->Detach(keep_stopped)); if (detach_error.Success()) { result.SetStatus(eReturnStatusSuccessFinishResult); process = nullptr; } else { result.AppendErrorWithFormat( "Failed to detach from process: %s\n", detach_error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { Status destroy_error(process->Destroy(false)); if (destroy_error.Success()) { result.SetStatus(eReturnStatusSuccessFinishResult); process = nullptr; } else { result.AppendErrorWithFormat("Failed to kill process: %s\n", destroy_error.AsCString()); result.SetStatus(eReturnStatusFailed); } } } } } return result.Succeeded(); } std::string m_new_process_action; }; //------------------------------------------------------------------------- // CommandObjectProcessLaunch //------------------------------------------------------------------------- #pragma mark CommandObjectProcessLaunch class CommandObjectProcessLaunch : public CommandObjectProcessLaunchOrAttach { public: CommandObjectProcessLaunch(CommandInterpreter &interpreter) : CommandObjectProcessLaunchOrAttach( interpreter, "process launch", "Launch the executable in the debugger.", nullptr, eCommandRequiresTarget, "restart"), m_options() { CommandArgumentEntry arg; CommandArgumentData run_args_arg; // Define the first (and only) variant of this arg. run_args_arg.arg_type = eArgTypeRunArgs; run_args_arg.arg_repetition = eArgRepeatOptional; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(run_args_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectProcessLaunch() override = default; int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } Options *GetOptions() override { return &m_options; } const char *GetRepeatCommand(Args ¤t_command_args, uint32_t index) override { // No repeat for "process launch"... return ""; } protected: bool DoExecute(Args &launch_args, CommandReturnObject &result) override { Debugger &debugger = m_interpreter.GetDebugger(); Target *target = debugger.GetSelectedTarget().get(); // If our listener is nullptr, users aren't allows to launch ModuleSP exe_module_sp = target->GetExecutableModule(); if (exe_module_sp == nullptr) { result.AppendError("no file in target, create a debug target using the " "'target create' command"); result.SetStatus(eReturnStatusFailed); return false; } StateType state = eStateInvalid; if (!StopProcessIfNecessary(m_exe_ctx.GetProcessPtr(), state, result)) return false; llvm::StringRef target_settings_argv0 = target->GetArg0(); // Determine whether we will disable ASLR or leave it in the default state // (i.e. enabled if the platform supports it). First check if the process // launch options explicitly turn on/off // disabling ASLR. If so, use that setting; // otherwise, use the 'settings target.disable-aslr' setting. bool disable_aslr = false; if (m_options.disable_aslr != eLazyBoolCalculate) { // The user specified an explicit setting on the process launch line. // Use it. disable_aslr = (m_options.disable_aslr == eLazyBoolYes); } else { // The user did not explicitly specify whether to disable ASLR. Fall // back to the target.disable-aslr setting. disable_aslr = target->GetDisableASLR(); } if (disable_aslr) m_options.launch_info.GetFlags().Set(eLaunchFlagDisableASLR); else m_options.launch_info.GetFlags().Clear(eLaunchFlagDisableASLR); if (target->GetDetachOnError()) m_options.launch_info.GetFlags().Set(eLaunchFlagDetachOnError); if (target->GetDisableSTDIO()) m_options.launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO); m_options.launch_info.GetEnvironment() = target->GetEnvironment(); if (!target_settings_argv0.empty()) { m_options.launch_info.GetArguments().AppendArgument( target_settings_argv0); m_options.launch_info.SetExecutableFile( exe_module_sp->GetPlatformFileSpec(), false); } else { m_options.launch_info.SetExecutableFile( exe_module_sp->GetPlatformFileSpec(), true); } if (launch_args.GetArgumentCount() == 0) { m_options.launch_info.GetArguments().AppendArguments( target->GetProcessLaunchInfo().GetArguments()); } else { m_options.launch_info.GetArguments().AppendArguments(launch_args); // Save the arguments for subsequent runs in the current target. target->SetRunArguments(launch_args); } StreamString stream; Status error = target->Launch(m_options.launch_info, &stream); if (error.Success()) { ProcessSP process_sp(target->GetProcessSP()); if (process_sp) { // There is a race condition where this thread will return up the call // stack to the main command handler and show an (lldb) prompt before // HandlePrivateEvent (from PrivateStateThread) has a chance to call // PushProcessIOHandler(). process_sp->SyncIOHandler(0, std::chrono::seconds(2)); llvm::StringRef data = stream.GetString(); if (!data.empty()) result.AppendMessage(data); const char *archname = exe_module_sp->GetArchitecture().GetArchitectureName(); result.AppendMessageWithFormat( "Process %" PRIu64 " launched: '%s' (%s)\n", process_sp->GetID(), exe_module_sp->GetFileSpec().GetPath().c_str(), archname); result.SetStatus(eReturnStatusSuccessFinishResult); result.SetDidChangeProcessState(true); } else { result.AppendError( "no error returned from Target::Launch, and target has no process"); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } protected: ProcessLaunchCommandOptions m_options; }; //#define SET1 LLDB_OPT_SET_1 //#define SET2 LLDB_OPT_SET_2 //#define SET3 LLDB_OPT_SET_3 // // OptionDefinition // CommandObjectProcessLaunch::CommandOptions::g_option_table[] = //{ // // clang-format off // {SET1 | SET2 | SET3, false, "stop-at-entry", 's', OptionParser::eNoArgument, // nullptr, 0, eArgTypeNone, "Stop at the entry point of the program // when launching a process."}, // {SET1, false, "stdin", 'i', // OptionParser::eRequiredArgument, nullptr, 0, eArgTypeDirectoryName, // "Redirect stdin for the process to ."}, // {SET1, false, "stdout", 'o', // OptionParser::eRequiredArgument, nullptr, 0, eArgTypeDirectoryName, // "Redirect stdout for the process to ."}, // {SET1, false, "stderr", 'e', // OptionParser::eRequiredArgument, nullptr, 0, eArgTypeDirectoryName, // "Redirect stderr for the process to ."}, // {SET1 | SET2 | SET3, false, "plugin", 'p', // OptionParser::eRequiredArgument, nullptr, 0, eArgTypePlugin, "Name of // the process plugin you want to use."}, // { SET2, false, "tty", 't', // OptionParser::eOptionalArgument, nullptr, 0, eArgTypeDirectoryName, "Start // the process in a terminal. If is specified, look for a terminal whose // name contains , else start the process in a new terminal."}, // { SET3, false, "no-stdio", 'n', OptionParser::eNoArgument, // nullptr, 0, eArgTypeNone, "Do not set up for terminal I/O to go to // running process."}, // {SET1 | SET2 | SET3, false, "working-dir", 'w', // OptionParser::eRequiredArgument, nullptr, 0, eArgTypeDirectoryName, "Set the // current working directory to when running the inferior."}, // {0, false, nullptr, 0, 0, nullptr, 0, eArgTypeNone, nullptr} // // clang-format on //}; // //#undef SET1 //#undef SET2 //#undef SET3 //------------------------------------------------------------------------- // CommandObjectProcessAttach //------------------------------------------------------------------------- static OptionDefinition g_process_attach_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "continue", 'c', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Immediately continue the process once attached." }, { LLDB_OPT_SET_ALL, false, "plugin", 'P', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePlugin, "Name of the process plugin you want to use." }, { LLDB_OPT_SET_1, false, "pid", 'p', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePid, "The process ID of an existing process to attach to." }, { LLDB_OPT_SET_2, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeProcessName, "The name of the process to attach to." }, { LLDB_OPT_SET_2, false, "include-existing", 'i', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Include existing processes when doing attach -w." }, { LLDB_OPT_SET_2, false, "waitfor", 'w', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Wait for the process with to launch." }, // clang-format on }; #pragma mark CommandObjectProcessAttach class CommandObjectProcessAttach : public CommandObjectProcessLaunchOrAttach { public: class CommandOptions : public Options { public: CommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'c': attach_info.SetContinueOnceAttached(true); break; case 'p': { lldb::pid_t pid; if (option_arg.getAsInteger(0, pid)) { error.SetErrorStringWithFormat("invalid process ID '%s'", option_arg.str().c_str()); } else { attach_info.SetProcessID(pid); } } break; case 'P': attach_info.SetProcessPluginName(option_arg); break; case 'n': attach_info.GetExecutableFile().SetFile(option_arg, false, FileSpec::Style::native); break; case 'w': attach_info.SetWaitForLaunch(true); break; case 'i': attach_info.SetIgnoreExisting(false); break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { attach_info.Clear(); } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_process_attach_options); } bool HandleOptionArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector, int opt_element_index, CommandInterpreter &interpreter) override { int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos; int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index; // We are only completing the name option for now... if (GetDefinitions()[opt_defs_index].short_option == 'n') { // Are we in the name? // Look to see if there is a -P argument provided, and if so use that // plugin, otherwise use the default plugin. const char *partial_name = nullptr; partial_name = request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos); PlatformSP platform_sp(interpreter.GetPlatform(true)); if (platform_sp) { ProcessInstanceInfoList process_infos; ProcessInstanceInfoMatch match_info; if (partial_name) { match_info.GetProcessInfo().GetExecutableFile().SetFile( partial_name, false, FileSpec::Style::native); match_info.SetNameMatchType(NameMatch::StartsWith); } platform_sp->FindProcesses(match_info, process_infos); const size_t num_matches = process_infos.GetSize(); if (num_matches > 0) { for (size_t i = 0; i < num_matches; ++i) { - request.GetMatches().AppendString( + request.AddCompletion(llvm::StringRef( process_infos.GetProcessNameAtIndex(i), - process_infos.GetProcessNameLengthAtIndex(i)); + process_infos.GetProcessNameLengthAtIndex(i))); } } } } return false; } // Instance variables to hold the values for command options. ProcessAttachInfo attach_info; }; CommandObjectProcessAttach(CommandInterpreter &interpreter) : CommandObjectProcessLaunchOrAttach( interpreter, "process attach", "Attach to a process.", "process attach ", 0, "attach"), m_options() {} ~CommandObjectProcessAttach() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { PlatformSP platform_sp( m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); // N.B. The attach should be synchronous. It doesn't help much to get the // prompt back between initiating the attach and the target actually // stopping. So even if the interpreter is set to be asynchronous, we wait // for the stop ourselves here. StateType state = eStateInvalid; Process *process = m_exe_ctx.GetProcessPtr(); if (!StopProcessIfNecessary(process, state, result)) return false; if (target == nullptr) { // If there isn't a current target create one. TargetSP new_target_sp; Status error; error = m_interpreter.GetDebugger().GetTargetList().CreateTarget( m_interpreter.GetDebugger(), "", "", false, nullptr, // No platform options new_target_sp); target = new_target_sp.get(); if (target == nullptr || error.Fail()) { result.AppendError(error.AsCString("Error creating target")); return false; } m_interpreter.GetDebugger().GetTargetList().SetSelectedTarget(target); } // Record the old executable module, we want to issue a warning if the // process of attaching changed the current executable (like somebody said // "file foo" then attached to a PID whose executable was bar.) ModuleSP old_exec_module_sp = target->GetExecutableModule(); ArchSpec old_arch_spec = target->GetArchitecture(); if (command.GetArgumentCount()) { result.AppendErrorWithFormat("Invalid arguments for '%s'.\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str()); result.SetStatus(eReturnStatusFailed); return false; } m_interpreter.UpdateExecutionContext(nullptr); StreamString stream; const auto error = target->Attach(m_options.attach_info, &stream); if (error.Success()) { ProcessSP process_sp(target->GetProcessSP()); if (process_sp) { result.AppendMessage(stream.GetString()); result.SetStatus(eReturnStatusSuccessFinishNoResult); result.SetDidChangeProcessState(true); result.SetAbnormalStopWasExpected(true); } else { result.AppendError( "no error returned from Target::Attach, and target has no process"); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat("attach failed: %s\n", error.AsCString()); result.SetStatus(eReturnStatusFailed); } if (!result.Succeeded()) return false; // Okay, we're done. Last step is to warn if the executable module has // changed: char new_path[PATH_MAX]; ModuleSP new_exec_module_sp(target->GetExecutableModule()); if (!old_exec_module_sp) { // We might not have a module if we attached to a raw pid... if (new_exec_module_sp) { new_exec_module_sp->GetFileSpec().GetPath(new_path, PATH_MAX); result.AppendMessageWithFormat("Executable module set to \"%s\".\n", new_path); } } else if (old_exec_module_sp->GetFileSpec() != new_exec_module_sp->GetFileSpec()) { char old_path[PATH_MAX]; old_exec_module_sp->GetFileSpec().GetPath(old_path, PATH_MAX); new_exec_module_sp->GetFileSpec().GetPath(new_path, PATH_MAX); result.AppendWarningWithFormat( "Executable module changed from \"%s\" to \"%s\".\n", old_path, new_path); } if (!old_arch_spec.IsValid()) { result.AppendMessageWithFormat( "Architecture set to: %s.\n", target->GetArchitecture().GetTriple().getTriple().c_str()); } else if (!old_arch_spec.IsExactMatch(target->GetArchitecture())) { result.AppendWarningWithFormat( "Architecture changed from %s to %s.\n", old_arch_spec.GetTriple().getTriple().c_str(), target->GetArchitecture().GetTriple().getTriple().c_str()); } // This supports the use-case scenario of immediately continuing the // process once attached. if (m_options.attach_info.GetContinueOnceAttached()) m_interpreter.HandleCommand("process continue", eLazyBoolNo, result); return result.Succeeded(); } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectProcessContinue //------------------------------------------------------------------------- static OptionDefinition g_process_continue_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "ignore-count",'i', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeUnsignedInteger, "Ignore crossings of the breakpoint (if it exists) for the currently selected thread." } // clang-format on }; #pragma mark CommandObjectProcessContinue class CommandObjectProcessContinue : public CommandObjectParsed { public: CommandObjectProcessContinue(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "process continue", "Continue execution of all threads in the current process.", "process continue", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() {} ~CommandObjectProcessContinue() override = default; protected: class CommandOptions : public Options { public: CommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'i': if (option_arg.getAsInteger(0, m_ignore)) error.SetErrorStringWithFormat( "invalid value for ignore option: \"%s\", should be a number.", option_arg.str().c_str()); break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_ignore = 0; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_process_continue_options); } uint32_t m_ignore; }; bool DoExecute(Args &command, CommandReturnObject &result) override { Process *process = m_exe_ctx.GetProcessPtr(); bool synchronous_execution = m_interpreter.GetSynchronous(); StateType state = process->GetState(); if (state == eStateStopped) { if (command.GetArgumentCount() != 0) { result.AppendErrorWithFormat( "The '%s' command does not take any arguments.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } if (m_options.m_ignore > 0) { ThreadSP sel_thread_sp(GetDefaultThread()->shared_from_this()); if (sel_thread_sp) { StopInfoSP stop_info_sp = sel_thread_sp->GetStopInfo(); if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonBreakpoint) { lldb::break_id_t bp_site_id = (lldb::break_id_t)stop_info_sp->GetValue(); BreakpointSiteSP bp_site_sp( process->GetBreakpointSiteList().FindByID(bp_site_id)); if (bp_site_sp) { const size_t num_owners = bp_site_sp->GetNumberOfOwners(); for (size_t i = 0; i < num_owners; i++) { Breakpoint &bp_ref = bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint(); if (!bp_ref.IsInternal()) { bp_ref.SetIgnoreCount(m_options.m_ignore); } } } } } } { // Scope for thread list mutex: std::lock_guard guard( process->GetThreadList().GetMutex()); const uint32_t num_threads = process->GetThreadList().GetSize(); // Set the actions that the threads should each take when resuming for (uint32_t idx = 0; idx < num_threads; ++idx) { const bool override_suspend = false; process->GetThreadList().GetThreadAtIndex(idx)->SetResumeState( eStateRunning, override_suspend); } } const uint32_t iohandler_id = process->GetIOHandlerID(); StreamString stream; Status error; if (synchronous_execution) error = process->ResumeSynchronous(&stream); else error = process->Resume(); if (error.Success()) { // There is a race condition where this thread will return up the call // stack to the main command handler and show an (lldb) prompt before // HandlePrivateEvent (from PrivateStateThread) has a chance to call // PushProcessIOHandler(). process->SyncIOHandler(iohandler_id, std::chrono::seconds(2)); result.AppendMessageWithFormat("Process %" PRIu64 " resuming\n", process->GetID()); if (synchronous_execution) { // If any state changed events had anything to say, add that to the // result result.AppendMessage(stream.GetString()); result.SetDidChangeProcessState(true); result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.SetStatus(eReturnStatusSuccessContinuingNoResult); } } else { result.AppendErrorWithFormat("Failed to resume process: %s.\n", error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat( "Process cannot be continued from its current state (%s).\n", StateAsCString(state)); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } Options *GetOptions() override { return &m_options; } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectProcessDetach //------------------------------------------------------------------------- static OptionDefinition g_process_detach_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "keep-stopped", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Whether or not the process should be kept stopped on detach (if possible)." }, // clang-format on }; #pragma mark CommandObjectProcessDetach class CommandObjectProcessDetach : public CommandObjectParsed { public: class CommandOptions : public Options { public: CommandOptions() : Options() { OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 's': bool tmp_result; bool success; tmp_result = OptionArgParser::ToBoolean(option_arg, false, &success); if (!success) error.SetErrorStringWithFormat("invalid boolean option: \"%s\"", option_arg.str().c_str()); else { if (tmp_result) m_keep_stopped = eLazyBoolYes; else m_keep_stopped = eLazyBoolNo; } break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_keep_stopped = eLazyBoolCalculate; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_process_detach_options); } // Instance variables to hold the values for command options. LazyBool m_keep_stopped; }; CommandObjectProcessDetach(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process detach", "Detach from the current target process.", "process detach", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched), m_options() {} ~CommandObjectProcessDetach() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Process *process = m_exe_ctx.GetProcessPtr(); // FIXME: This will be a Command Option: bool keep_stopped; if (m_options.m_keep_stopped == eLazyBoolCalculate) { // Check the process default: keep_stopped = process->GetDetachKeepsStopped(); } else if (m_options.m_keep_stopped == eLazyBoolYes) keep_stopped = true; else keep_stopped = false; Status error(process->Detach(keep_stopped)); if (error.Success()) { result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat("Detach failed: %s\n", error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } return result.Succeeded(); } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectProcessConnect //------------------------------------------------------------------------- static OptionDefinition g_process_connect_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "plugin", 'p', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePlugin, "Name of the process plugin you want to use." }, // clang-format on }; #pragma mark CommandObjectProcessConnect class CommandObjectProcessConnect : public CommandObjectParsed { public: class CommandOptions : public Options { public: CommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'p': plugin_name.assign(option_arg); break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { plugin_name.clear(); } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_process_connect_options); } // Instance variables to hold the values for command options. std::string plugin_name; }; CommandObjectProcessConnect(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process connect", "Connect to a remote debug service.", "process connect ", 0), m_options() {} ~CommandObjectProcessConnect() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { if (command.GetArgumentCount() != 1) { result.AppendErrorWithFormat( "'%s' takes exactly one argument:\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str()); result.SetStatus(eReturnStatusFailed); return false; } Process *process = m_exe_ctx.GetProcessPtr(); if (process && process->IsAlive()) { result.AppendErrorWithFormat( "Process %" PRIu64 " is currently being debugged, kill the process before connecting.\n", process->GetID()); result.SetStatus(eReturnStatusFailed); return false; } const char *plugin_name = nullptr; if (!m_options.plugin_name.empty()) plugin_name = m_options.plugin_name.c_str(); Status error; Debugger &debugger = m_interpreter.GetDebugger(); PlatformSP platform_sp = m_interpreter.GetPlatform(true); ProcessSP process_sp = platform_sp->ConnectProcess( command.GetArgumentAtIndex(0), plugin_name, debugger, debugger.GetSelectedTarget().get(), error); if (error.Fail() || process_sp == nullptr) { result.AppendError(error.AsCString("Error connecting to the process")); result.SetStatus(eReturnStatusFailed); return false; } return true; } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectProcessPlugin //------------------------------------------------------------------------- #pragma mark CommandObjectProcessPlugin class CommandObjectProcessPlugin : public CommandObjectProxy { public: CommandObjectProcessPlugin(CommandInterpreter &interpreter) : CommandObjectProxy( interpreter, "process plugin", "Send a custom command to the current target process plug-in.", "process plugin ", 0) {} ~CommandObjectProcessPlugin() override = default; CommandObject *GetProxyCommandObject() override { Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) return process->GetPluginCommandObject(); return nullptr; } }; //------------------------------------------------------------------------- // CommandObjectProcessLoad //------------------------------------------------------------------------- static OptionDefinition g_process_load_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "install", 'i', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypePath, "Install the shared library to the target. If specified without an argument then the library will installed in the current working directory." }, // clang-format on }; #pragma mark CommandObjectProcessLoad class CommandObjectProcessLoad : public CommandObjectParsed { public: class CommandOptions : public Options { public: CommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'i': do_install = true; if (!option_arg.empty()) install_path.SetFile(option_arg, false, FileSpec::Style::native); break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { do_install = false; install_path.Clear(); } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_process_load_options); } // Instance variables to hold the values for command options. bool do_install; FileSpec install_path; }; CommandObjectProcessLoad(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process load", "Load a shared library into the current process.", "process load [ ...]", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() {} ~CommandObjectProcessLoad() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Process *process = m_exe_ctx.GetProcessPtr(); for (auto &entry : command.entries()) { Status error; PlatformSP platform = process->GetTarget().GetPlatform(); llvm::StringRef image_path = entry.ref; uint32_t image_token = LLDB_INVALID_IMAGE_TOKEN; if (!m_options.do_install) { FileSpec image_spec(image_path, false); platform->ResolveRemotePath(image_spec, image_spec); image_token = platform->LoadImage(process, FileSpec(), image_spec, error); } else if (m_options.install_path) { FileSpec image_spec(image_path, true); platform->ResolveRemotePath(m_options.install_path, m_options.install_path); image_token = platform->LoadImage(process, image_spec, m_options.install_path, error); } else { FileSpec image_spec(image_path, true); image_token = platform->LoadImage(process, image_spec, FileSpec(), error); } if (image_token != LLDB_INVALID_IMAGE_TOKEN) { result.AppendMessageWithFormat( "Loading \"%s\"...ok\nImage %u loaded.\n", image_path.str().c_str(), image_token); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat("failed to load '%s': %s", image_path.str().c_str(), error.AsCString()); result.SetStatus(eReturnStatusFailed); } } return result.Succeeded(); } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectProcessUnload //------------------------------------------------------------------------- #pragma mark CommandObjectProcessUnload class CommandObjectProcessUnload : public CommandObjectParsed { public: CommandObjectProcessUnload(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "process unload", "Unload a shared library from the current process using the index " "returned by a previous call to \"process load\".", "process unload ", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {} ~CommandObjectProcessUnload() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Process *process = m_exe_ctx.GetProcessPtr(); for (auto &entry : command.entries()) { uint32_t image_token; if (entry.ref.getAsInteger(0, image_token)) { result.AppendErrorWithFormat("invalid image index argument '%s'", entry.ref.str().c_str()); result.SetStatus(eReturnStatusFailed); break; } else { Status error(process->GetTarget().GetPlatform()->UnloadImage( process, image_token)); if (error.Success()) { result.AppendMessageWithFormat( "Unloading shared library with index %u...ok\n", image_token); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat("failed to unload image: %s", error.AsCString()); result.SetStatus(eReturnStatusFailed); break; } } } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectProcessSignal //------------------------------------------------------------------------- #pragma mark CommandObjectProcessSignal class CommandObjectProcessSignal : public CommandObjectParsed { public: CommandObjectProcessSignal(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process signal", "Send a UNIX signal to the current target process.", nullptr, eCommandRequiresProcess | eCommandTryTargetAPILock) { CommandArgumentEntry arg; CommandArgumentData signal_arg; // Define the first (and only) variant of this arg. signal_arg.arg_type = eArgTypeUnixSignal; signal_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(signal_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectProcessSignal() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Process *process = m_exe_ctx.GetProcessPtr(); if (command.GetArgumentCount() == 1) { int signo = LLDB_INVALID_SIGNAL_NUMBER; const char *signal_name = command.GetArgumentAtIndex(0); if (::isxdigit(signal_name[0])) signo = StringConvert::ToSInt32(signal_name, LLDB_INVALID_SIGNAL_NUMBER, 0); else signo = process->GetUnixSignals()->GetSignalNumberFromName(signal_name); if (signo == LLDB_INVALID_SIGNAL_NUMBER) { result.AppendErrorWithFormat("Invalid signal argument '%s'.\n", command.GetArgumentAtIndex(0)); result.SetStatus(eReturnStatusFailed); } else { Status error(process->Signal(signo)); if (error.Success()) { result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat("Failed to send signal %i: %s\n", signo, error.AsCString()); result.SetStatus(eReturnStatusFailed); } } } else { result.AppendErrorWithFormat( "'%s' takes exactly one signal number argument:\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str()); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectProcessInterrupt //------------------------------------------------------------------------- #pragma mark CommandObjectProcessInterrupt class CommandObjectProcessInterrupt : public CommandObjectParsed { public: CommandObjectProcessInterrupt(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process interrupt", "Interrupt the current target process.", "process interrupt", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched) {} ~CommandObjectProcessInterrupt() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Process *process = m_exe_ctx.GetProcessPtr(); if (process == nullptr) { result.AppendError("no process to halt"); result.SetStatus(eReturnStatusFailed); return false; } if (command.GetArgumentCount() == 0) { bool clear_thread_plans = true; Status error(process->Halt(clear_thread_plans)); if (error.Success()) { result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat("Failed to halt process: %s\n", error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat("'%s' takes no arguments:\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str()); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectProcessKill //------------------------------------------------------------------------- #pragma mark CommandObjectProcessKill class CommandObjectProcessKill : public CommandObjectParsed { public: CommandObjectProcessKill(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process kill", "Terminate the current target process.", "process kill", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched) {} ~CommandObjectProcessKill() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Process *process = m_exe_ctx.GetProcessPtr(); if (process == nullptr) { result.AppendError("no process to kill"); result.SetStatus(eReturnStatusFailed); return false; } if (command.GetArgumentCount() == 0) { Status error(process->Destroy(true)); if (error.Success()) { result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat("Failed to kill process: %s\n", error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat("'%s' takes no arguments:\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str()); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectProcessSaveCore //------------------------------------------------------------------------- #pragma mark CommandObjectProcessSaveCore class CommandObjectProcessSaveCore : public CommandObjectParsed { public: CommandObjectProcessSaveCore(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process save-core", "Save the current process as a core file using an " "appropriate file type.", "process save-core FILE", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched) {} ~CommandObjectProcessSaveCore() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { ProcessSP process_sp = m_exe_ctx.GetProcessSP(); if (process_sp) { if (command.GetArgumentCount() == 1) { FileSpec output_file(command.GetArgumentAtIndex(0), false); Status error = PluginManager::SaveCore(process_sp, output_file); if (error.Success()) { result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat( "Failed to save core file for process: %s\n", error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat("'%s' takes one arguments:\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("invalid process"); result.SetStatus(eReturnStatusFailed); return false; } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectProcessStatus //------------------------------------------------------------------------- #pragma mark CommandObjectProcessStatus class CommandObjectProcessStatus : public CommandObjectParsed { public: CommandObjectProcessStatus(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "process status", "Show status and stop location for the current target process.", "process status", eCommandRequiresProcess | eCommandTryTargetAPILock) {} ~CommandObjectProcessStatus() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { Stream &strm = result.GetOutputStream(); result.SetStatus(eReturnStatusSuccessFinishNoResult); // No need to check "process" for validity as eCommandRequiresProcess // ensures it is valid Process *process = m_exe_ctx.GetProcessPtr(); const bool only_threads_with_stop_reason = true; const uint32_t start_frame = 0; const uint32_t num_frames = 1; const uint32_t num_frames_with_source = 1; const bool stop_format = true; process->GetStatus(strm); process->GetThreadStatus(strm, only_threads_with_stop_reason, start_frame, num_frames, num_frames_with_source, stop_format); return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectProcessHandle //------------------------------------------------------------------------- static OptionDefinition g_process_handle_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "stop", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Whether or not the process should be stopped if the signal is received." }, { LLDB_OPT_SET_1, false, "notify", 'n', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Whether or not the debugger should notify the user if the signal is received." }, { LLDB_OPT_SET_1, false, "pass", 'p', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Whether or not the signal should be passed to the process." } // clang-format on }; #pragma mark CommandObjectProcessHandle class CommandObjectProcessHandle : public CommandObjectParsed { public: class CommandOptions : public Options { public: CommandOptions() : Options() { OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 's': stop = option_arg; break; case 'n': notify = option_arg; break; case 'p': pass = option_arg; break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { stop.clear(); notify.clear(); pass.clear(); } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_process_handle_options); } // Instance variables to hold the values for command options. std::string stop; std::string notify; std::string pass; }; CommandObjectProcessHandle(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process handle", "Manage LLDB handling of OS signals for the " "current target process. Defaults to showing " "current policy.", nullptr), m_options() { SetHelpLong("\nIf no signals are specified, update them all. If no update " "option is specified, list the current values."); CommandArgumentEntry arg; CommandArgumentData signal_arg; signal_arg.arg_type = eArgTypeUnixSignal; signal_arg.arg_repetition = eArgRepeatStar; arg.push_back(signal_arg); m_arguments.push_back(arg); } ~CommandObjectProcessHandle() override = default; Options *GetOptions() override { return &m_options; } bool VerifyCommandOptionValue(const std::string &option, int &real_value) { bool okay = true; bool success = false; bool tmp_value = OptionArgParser::ToBoolean(option, false, &success); if (success && tmp_value) real_value = 1; else if (success && !tmp_value) real_value = 0; else { // If the value isn't 'true' or 'false', it had better be 0 or 1. real_value = StringConvert::ToUInt32(option.c_str(), 3); if (real_value != 0 && real_value != 1) okay = false; } return okay; } void PrintSignalHeader(Stream &str) { str.Printf("NAME PASS STOP NOTIFY\n"); str.Printf("=========== ===== ===== ======\n"); } void PrintSignal(Stream &str, int32_t signo, const char *sig_name, const UnixSignalsSP &signals_sp) { bool stop; bool suppress; bool notify; str.Printf("%-11s ", sig_name); if (signals_sp->GetSignalInfo(signo, suppress, stop, notify)) { bool pass = !suppress; str.Printf("%s %s %s", (pass ? "true " : "false"), (stop ? "true " : "false"), (notify ? "true " : "false")); } str.Printf("\n"); } void PrintSignalInformation(Stream &str, Args &signal_args, int num_valid_signals, const UnixSignalsSP &signals_sp) { PrintSignalHeader(str); if (num_valid_signals > 0) { size_t num_args = signal_args.GetArgumentCount(); for (size_t i = 0; i < num_args; ++i) { int32_t signo = signals_sp->GetSignalNumberFromName( signal_args.GetArgumentAtIndex(i)); if (signo != LLDB_INVALID_SIGNAL_NUMBER) PrintSignal(str, signo, signal_args.GetArgumentAtIndex(i), signals_sp); } } else // Print info for ALL signals { int32_t signo = signals_sp->GetFirstSignalNumber(); while (signo != LLDB_INVALID_SIGNAL_NUMBER) { PrintSignal(str, signo, signals_sp->GetSignalAsCString(signo), signals_sp); signo = signals_sp->GetNextSignalNumber(signo); } } } protected: bool DoExecute(Args &signal_args, CommandReturnObject &result) override { TargetSP target_sp = m_interpreter.GetDebugger().GetSelectedTarget(); if (!target_sp) { result.AppendError("No current target;" " cannot handle signals until you have a valid target " "and process.\n"); result.SetStatus(eReturnStatusFailed); return false; } ProcessSP process_sp = target_sp->GetProcessSP(); if (!process_sp) { result.AppendError("No current process; cannot handle signals until you " "have a valid process.\n"); result.SetStatus(eReturnStatusFailed); return false; } int stop_action = -1; // -1 means leave the current setting alone int pass_action = -1; // -1 means leave the current setting alone int notify_action = -1; // -1 means leave the current setting alone if (!m_options.stop.empty() && !VerifyCommandOptionValue(m_options.stop, stop_action)) { result.AppendError("Invalid argument for command option --stop; must be " "true or false.\n"); result.SetStatus(eReturnStatusFailed); return false; } if (!m_options.notify.empty() && !VerifyCommandOptionValue(m_options.notify, notify_action)) { result.AppendError("Invalid argument for command option --notify; must " "be true or false.\n"); result.SetStatus(eReturnStatusFailed); return false; } if (!m_options.pass.empty() && !VerifyCommandOptionValue(m_options.pass, pass_action)) { result.AppendError("Invalid argument for command option --pass; must be " "true or false.\n"); result.SetStatus(eReturnStatusFailed); return false; } size_t num_args = signal_args.GetArgumentCount(); UnixSignalsSP signals_sp = process_sp->GetUnixSignals(); int num_signals_set = 0; if (num_args > 0) { for (const auto &arg : signal_args) { int32_t signo = signals_sp->GetSignalNumberFromName(arg.c_str()); if (signo != LLDB_INVALID_SIGNAL_NUMBER) { // Casting the actions as bools here should be okay, because // VerifyCommandOptionValue guarantees the value is either 0 or 1. if (stop_action != -1) signals_sp->SetShouldStop(signo, stop_action); if (pass_action != -1) { bool suppress = !pass_action; signals_sp->SetShouldSuppress(signo, suppress); } if (notify_action != -1) signals_sp->SetShouldNotify(signo, notify_action); ++num_signals_set; } else { result.AppendErrorWithFormat("Invalid signal name '%s'\n", arg.c_str()); } } } else { // No signal specified, if any command options were specified, update ALL // signals. if ((notify_action != -1) || (stop_action != -1) || (pass_action != -1)) { if (m_interpreter.Confirm( "Do you really want to update all the signals?", false)) { int32_t signo = signals_sp->GetFirstSignalNumber(); while (signo != LLDB_INVALID_SIGNAL_NUMBER) { if (notify_action != -1) signals_sp->SetShouldNotify(signo, notify_action); if (stop_action != -1) signals_sp->SetShouldStop(signo, stop_action); if (pass_action != -1) { bool suppress = !pass_action; signals_sp->SetShouldSuppress(signo, suppress); } signo = signals_sp->GetNextSignalNumber(signo); } } } } PrintSignalInformation(result.GetOutputStream(), signal_args, num_signals_set, signals_sp); if (num_signals_set > 0) result.SetStatus(eReturnStatusSuccessFinishNoResult); else result.SetStatus(eReturnStatusFailed); return result.Succeeded(); } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectMultiwordProcess //------------------------------------------------------------------------- CommandObjectMultiwordProcess::CommandObjectMultiwordProcess( CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "process", "Commands for interacting with processes on the current platform.", "process []") { LoadSubCommand("attach", CommandObjectSP(new CommandObjectProcessAttach(interpreter))); LoadSubCommand("launch", CommandObjectSP(new CommandObjectProcessLaunch(interpreter))); LoadSubCommand("continue", CommandObjectSP(new CommandObjectProcessContinue( interpreter))); LoadSubCommand("connect", CommandObjectSP(new CommandObjectProcessConnect(interpreter))); LoadSubCommand("detach", CommandObjectSP(new CommandObjectProcessDetach(interpreter))); LoadSubCommand("load", CommandObjectSP(new CommandObjectProcessLoad(interpreter))); LoadSubCommand("unload", CommandObjectSP(new CommandObjectProcessUnload(interpreter))); LoadSubCommand("signal", CommandObjectSP(new CommandObjectProcessSignal(interpreter))); LoadSubCommand("handle", CommandObjectSP(new CommandObjectProcessHandle(interpreter))); LoadSubCommand("status", CommandObjectSP(new CommandObjectProcessStatus(interpreter))); LoadSubCommand("interrupt", CommandObjectSP(new CommandObjectProcessInterrupt( interpreter))); LoadSubCommand("kill", CommandObjectSP(new CommandObjectProcessKill(interpreter))); LoadSubCommand("plugin", CommandObjectSP(new CommandObjectProcessPlugin(interpreter))); LoadSubCommand("save-core", CommandObjectSP(new CommandObjectProcessSaveCore( interpreter))); } CommandObjectMultiwordProcess::~CommandObjectMultiwordProcess() = default; Index: vendor/lldb/dist/source/Commands/CommandObjectSettings.cpp =================================================================== --- vendor/lldb/dist/source/Commands/CommandObjectSettings.cpp (revision 337146) +++ vendor/lldb/dist/source/Commands/CommandObjectSettings.cpp (revision 337147) @@ -1,992 +1,992 @@ //===-- CommandObjectSettings.cpp -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "CommandObjectSettings.h" // C Includes // C++ Includes // Other libraries and framework includes #include "llvm/ADT/StringRef.h" // Project includes #include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionValueProperties.h" using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------------- // CommandObjectSettingsSet //------------------------------------------------------------------------- static OptionDefinition g_settings_set_options[] = { // clang-format off { LLDB_OPT_SET_2, false, "global", 'g', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Apply the new value to the global default value." } // clang-format on }; class CommandObjectSettingsSet : public CommandObjectRaw { public: CommandObjectSettingsSet(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "settings set", "Set the value of the specified debugger setting."), m_options() { CommandArgumentEntry arg1; CommandArgumentEntry arg2; CommandArgumentData var_name_arg; CommandArgumentData value_arg; // Define the first (and only) variant of this arg. var_name_arg.arg_type = eArgTypeSettingVariableName; var_name_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(var_name_arg); // Define the first (and only) variant of this arg. value_arg.arg_type = eArgTypeValue; value_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg2.push_back(value_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); m_arguments.push_back(arg2); SetHelpLong( "\nWhen setting a dictionary or array variable, you can set multiple entries \ at once by giving the values to the set command. For example:" R"( (lldb) settings set target.run-args value1 value2 value3 (lldb) settings set target.env-vars MYPATH=~/.:/usr/bin SOME_ENV_VAR=12345 (lldb) settings show target.run-args [0]: 'value1' [1]: 'value2' [3]: 'value3' (lldb) settings show target.env-vars 'MYPATH=~/.:/usr/bin' 'SOME_ENV_VAR=12345' )" "Warning: The 'set' command re-sets the entire array or dictionary. If you \ just want to add, remove or update individual values (or add something to \ the end), use one of the other settings sub-commands: append, replace, \ insert-before or insert-after."); } ~CommandObjectSettingsSet() override = default; // Overrides base class's behavior where WantsCompletion = // !WantsRawCommandString. bool WantsCompletion() override { return true; } Options *GetOptions() override { return &m_options; } class CommandOptions : public Options { public: CommandOptions() : Options(), m_global(false) {} ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'g': m_global = true; break; default: error.SetErrorStringWithFormat("unrecognized options '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_global = false; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_settings_set_options); } // Instance variables to hold the values for command options. bool m_global; }; int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { const size_t argc = request.GetParsedLine().GetArgumentCount(); const char *arg = nullptr; int setting_var_idx; for (setting_var_idx = 0; setting_var_idx < static_cast(argc); ++setting_var_idx) { arg = request.GetParsedLine().GetArgumentAtIndex(setting_var_idx); if (arg && arg[0] != '-') break; // We found our setting variable name index } if (request.GetCursorIndex() == setting_var_idx) { // Attempting to complete setting variable name CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion, request, nullptr); } else { arg = request.GetParsedLine().GetArgumentAtIndex(request.GetCursorIndex()); if (arg) { if (arg[0] == '-') { // Complete option name } else { // Complete setting value const char *setting_var_name = request.GetParsedLine().GetArgumentAtIndex(setting_var_idx); Status error; lldb::OptionValueSP value_sp( m_interpreter.GetDebugger().GetPropertyValue( &m_exe_ctx, setting_var_name, false, error)); if (value_sp) { value_sp->AutoComplete(m_interpreter, request); } } } } - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } protected: bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override { Args cmd_args(command); // Process possible options. if (!ParseOptions(cmd_args, result)) return false; const size_t argc = cmd_args.GetArgumentCount(); if ((argc < 2) && (!m_options.m_global)) { result.AppendError("'settings set' takes more arguments"); result.SetStatus(eReturnStatusFailed); return false; } const char *var_name = cmd_args.GetArgumentAtIndex(0); if ((var_name == nullptr) || (var_name[0] == '\0')) { result.AppendError( "'settings set' command requires a valid variable name"); result.SetStatus(eReturnStatusFailed); return false; } // Split the raw command into var_name and value pair. llvm::StringRef raw_str(command); std::string var_value_string = raw_str.split(var_name).second.str(); const char *var_value_cstr = Args::StripSpaces(var_value_string, true, false, false); Status error; if (m_options.m_global) { error = m_interpreter.GetDebugger().SetPropertyValue( nullptr, eVarSetOperationAssign, var_name, var_value_cstr); } if (error.Success()) { // FIXME this is the same issue as the one in commands script import // we could be setting target.load-script-from-symbol-file which would // cause Python scripts to be loaded, which could run LLDB commands (e.g. // settings set target.process.python-os-plugin-path) and cause a crash // if we did not clear the command's exe_ctx first ExecutionContext exe_ctx(m_exe_ctx); m_exe_ctx.Clear(); error = m_interpreter.GetDebugger().SetPropertyValue( &exe_ctx, eVarSetOperationAssign, var_name, var_value_cstr); } if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } else { result.SetStatus(eReturnStatusSuccessFinishResult); } return result.Succeeded(); } private: CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectSettingsShow -- Show current values //------------------------------------------------------------------------- class CommandObjectSettingsShow : public CommandObjectParsed { public: CommandObjectSettingsShow(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "settings show", "Show matching debugger settings and their current " "values. Defaults to showing all settings.", nullptr) { CommandArgumentEntry arg1; CommandArgumentData var_name_arg; // Define the first (and only) variant of this arg. var_name_arg.arg_type = eArgTypeSettingVariableName; var_name_arg.arg_repetition = eArgRepeatOptional; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(var_name_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); } ~CommandObjectSettingsShow() override = default; int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } protected: bool DoExecute(Args &args, CommandReturnObject &result) override { result.SetStatus(eReturnStatusSuccessFinishResult); if (!args.empty()) { for (const auto &arg : args) { Status error(m_interpreter.GetDebugger().DumpPropertyValue( &m_exe_ctx, result.GetOutputStream(), arg.ref, OptionValue::eDumpGroupValue)); if (error.Success()) { result.GetOutputStream().EOL(); } else { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } } } else { m_interpreter.GetDebugger().DumpAllPropertyValues( &m_exe_ctx, result.GetOutputStream(), OptionValue::eDumpGroupValue); } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectSettingsList -- List settable variables //------------------------------------------------------------------------- class CommandObjectSettingsList : public CommandObjectParsed { public: CommandObjectSettingsList(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "settings list", "List and describe matching debugger settings. " "Defaults to all listing all settings.", nullptr) { CommandArgumentEntry arg; CommandArgumentData var_name_arg; CommandArgumentData prefix_name_arg; // Define the first variant of this arg. var_name_arg.arg_type = eArgTypeSettingVariableName; var_name_arg.arg_repetition = eArgRepeatOptional; // Define the second variant of this arg. prefix_name_arg.arg_type = eArgTypeSettingPrefix; prefix_name_arg.arg_repetition = eArgRepeatOptional; arg.push_back(var_name_arg); arg.push_back(prefix_name_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectSettingsList() override = default; int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } protected: bool DoExecute(Args &args, CommandReturnObject &result) override { result.SetStatus(eReturnStatusSuccessFinishResult); const bool will_modify = false; const size_t argc = args.GetArgumentCount(); if (argc > 0) { const bool dump_qualified_name = true; // TODO: Convert to StringRef based enumeration. Requires converting // GetPropertyAtPath first. for (size_t i = 0; i < argc; ++i) { const char *property_path = args.GetArgumentAtIndex(i); const Property *property = m_interpreter.GetDebugger().GetValueProperties()->GetPropertyAtPath( &m_exe_ctx, will_modify, property_path); if (property) { property->DumpDescription(m_interpreter, result.GetOutputStream(), 0, dump_qualified_name); } else { result.AppendErrorWithFormat("invalid property path '%s'", property_path); result.SetStatus(eReturnStatusFailed); } } } else { m_interpreter.GetDebugger().DumpAllDescriptions(m_interpreter, result.GetOutputStream()); } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectSettingsRemove //------------------------------------------------------------------------- class CommandObjectSettingsRemove : public CommandObjectRaw { public: CommandObjectSettingsRemove(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "settings remove", "Remove a value from a setting, specified by array " "index or dictionary key.") { CommandArgumentEntry arg1; CommandArgumentEntry arg2; CommandArgumentData var_name_arg; CommandArgumentData index_arg; CommandArgumentData key_arg; // Define the first (and only) variant of this arg. var_name_arg.arg_type = eArgTypeSettingVariableName; var_name_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(var_name_arg); // Define the first variant of this arg. index_arg.arg_type = eArgTypeSettingIndex; index_arg.arg_repetition = eArgRepeatPlain; // Define the second variant of this arg. key_arg.arg_type = eArgTypeSettingKey; key_arg.arg_repetition = eArgRepeatPlain; // Push both variants into this arg arg2.push_back(index_arg); arg2.push_back(key_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); m_arguments.push_back(arg2); } ~CommandObjectSettingsRemove() override = default; int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { if (request.GetCursorIndex() < 2) CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } protected: bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override { result.SetStatus(eReturnStatusSuccessFinishNoResult); Args cmd_args(command); // Process possible options. if (!ParseOptions(cmd_args, result)) return false; const size_t argc = cmd_args.GetArgumentCount(); if (argc == 0) { result.AppendError("'settings set' takes an array or dictionary item, or " "an array followed by one or more indexes, or a " "dictionary followed by one or more key names to " "remove"); result.SetStatus(eReturnStatusFailed); return false; } const char *var_name = cmd_args.GetArgumentAtIndex(0); if ((var_name == nullptr) || (var_name[0] == '\0')) { result.AppendError( "'settings set' command requires a valid variable name"); result.SetStatus(eReturnStatusFailed); return false; } // Split the raw command into var_name and value pair. llvm::StringRef raw_str(command); std::string var_value_string = raw_str.split(var_name).second.str(); const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false); Status error(m_interpreter.GetDebugger().SetPropertyValue( &m_exe_ctx, eVarSetOperationRemove, var_name, var_value_cstr)); if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectSettingsReplace //------------------------------------------------------------------------- class CommandObjectSettingsReplace : public CommandObjectRaw { public: CommandObjectSettingsReplace(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "settings replace", "Replace the debugger setting value specified by " "array index or dictionary key.") { CommandArgumentEntry arg1; CommandArgumentEntry arg2; CommandArgumentEntry arg3; CommandArgumentData var_name_arg; CommandArgumentData index_arg; CommandArgumentData key_arg; CommandArgumentData value_arg; // Define the first (and only) variant of this arg. var_name_arg.arg_type = eArgTypeSettingVariableName; var_name_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(var_name_arg); // Define the first (variant of this arg. index_arg.arg_type = eArgTypeSettingIndex; index_arg.arg_repetition = eArgRepeatPlain; // Define the second (variant of this arg. key_arg.arg_type = eArgTypeSettingKey; key_arg.arg_repetition = eArgRepeatPlain; // Put both variants into this arg arg2.push_back(index_arg); arg2.push_back(key_arg); // Define the first (and only) variant of this arg. value_arg.arg_type = eArgTypeValue; value_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg3.push_back(value_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); m_arguments.push_back(arg2); m_arguments.push_back(arg3); } ~CommandObjectSettingsReplace() override = default; // Overrides base class's behavior where WantsCompletion = // !WantsRawCommandString. bool WantsCompletion() override { return true; } int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { // Attempting to complete variable name if (request.GetCursorIndex() < 2) CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } protected: bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override { result.SetStatus(eReturnStatusSuccessFinishNoResult); Args cmd_args(command); const char *var_name = cmd_args.GetArgumentAtIndex(0); if ((var_name == nullptr) || (var_name[0] == '\0')) { result.AppendError("'settings replace' command requires a valid variable " "name; No value supplied"); result.SetStatus(eReturnStatusFailed); return false; } // Split the raw command into var_name, index_value, and value triple. llvm::StringRef raw_str(command); std::string var_value_string = raw_str.split(var_name).second.str(); const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false); Status error(m_interpreter.GetDebugger().SetPropertyValue( &m_exe_ctx, eVarSetOperationReplace, var_name, var_value_cstr)); if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } else { result.SetStatus(eReturnStatusSuccessFinishNoResult); } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectSettingsInsertBefore //------------------------------------------------------------------------- class CommandObjectSettingsInsertBefore : public CommandObjectRaw { public: CommandObjectSettingsInsertBefore(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "settings insert-before", "Insert one or more values into an debugger array " "setting immediately before the specified element " "index.") { CommandArgumentEntry arg1; CommandArgumentEntry arg2; CommandArgumentEntry arg3; CommandArgumentData var_name_arg; CommandArgumentData index_arg; CommandArgumentData value_arg; // Define the first (and only) variant of this arg. var_name_arg.arg_type = eArgTypeSettingVariableName; var_name_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(var_name_arg); // Define the first (variant of this arg. index_arg.arg_type = eArgTypeSettingIndex; index_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg2.push_back(index_arg); // Define the first (and only) variant of this arg. value_arg.arg_type = eArgTypeValue; value_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg3.push_back(value_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); m_arguments.push_back(arg2); m_arguments.push_back(arg3); } ~CommandObjectSettingsInsertBefore() override = default; // Overrides base class's behavior where WantsCompletion = // !WantsRawCommandString. bool WantsCompletion() override { return true; } int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { // Attempting to complete variable name if (request.GetCursorIndex() < 2) CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } protected: bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override { result.SetStatus(eReturnStatusSuccessFinishNoResult); Args cmd_args(command); const size_t argc = cmd_args.GetArgumentCount(); if (argc < 3) { result.AppendError("'settings insert-before' takes more arguments"); result.SetStatus(eReturnStatusFailed); return false; } const char *var_name = cmd_args.GetArgumentAtIndex(0); if ((var_name == nullptr) || (var_name[0] == '\0')) { result.AppendError("'settings insert-before' command requires a valid " "variable name; No value supplied"); result.SetStatus(eReturnStatusFailed); return false; } // Split the raw command into var_name, index_value, and value triple. llvm::StringRef raw_str(command); std::string var_value_string = raw_str.split(var_name).second.str(); const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false); Status error(m_interpreter.GetDebugger().SetPropertyValue( &m_exe_ctx, eVarSetOperationInsertBefore, var_name, var_value_cstr)); if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectSettingInsertAfter //------------------------------------------------------------------------- class CommandObjectSettingsInsertAfter : public CommandObjectRaw { public: CommandObjectSettingsInsertAfter(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "settings insert-after", "Insert one or more values into a debugger array " "settings after the specified element index.") { CommandArgumentEntry arg1; CommandArgumentEntry arg2; CommandArgumentEntry arg3; CommandArgumentData var_name_arg; CommandArgumentData index_arg; CommandArgumentData value_arg; // Define the first (and only) variant of this arg. var_name_arg.arg_type = eArgTypeSettingVariableName; var_name_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(var_name_arg); // Define the first (variant of this arg. index_arg.arg_type = eArgTypeSettingIndex; index_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg2.push_back(index_arg); // Define the first (and only) variant of this arg. value_arg.arg_type = eArgTypeValue; value_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg3.push_back(value_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); m_arguments.push_back(arg2); m_arguments.push_back(arg3); } ~CommandObjectSettingsInsertAfter() override = default; // Overrides base class's behavior where WantsCompletion = // !WantsRawCommandString. bool WantsCompletion() override { return true; } int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { // Attempting to complete variable name if (request.GetCursorIndex() < 2) CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } protected: bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override { result.SetStatus(eReturnStatusSuccessFinishNoResult); Args cmd_args(command); const size_t argc = cmd_args.GetArgumentCount(); if (argc < 3) { result.AppendError("'settings insert-after' takes more arguments"); result.SetStatus(eReturnStatusFailed); return false; } const char *var_name = cmd_args.GetArgumentAtIndex(0); if ((var_name == nullptr) || (var_name[0] == '\0')) { result.AppendError("'settings insert-after' command requires a valid " "variable name; No value supplied"); result.SetStatus(eReturnStatusFailed); return false; } // Split the raw command into var_name, index_value, and value triple. llvm::StringRef raw_str(command); std::string var_value_string = raw_str.split(var_name).second.str(); const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false); Status error(m_interpreter.GetDebugger().SetPropertyValue( &m_exe_ctx, eVarSetOperationInsertAfter, var_name, var_value_cstr)); if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectSettingsAppend //------------------------------------------------------------------------- class CommandObjectSettingsAppend : public CommandObjectRaw { public: CommandObjectSettingsAppend(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "settings append", "Append one or more values to a debugger array, " "dictionary, or string setting.") { CommandArgumentEntry arg1; CommandArgumentEntry arg2; CommandArgumentData var_name_arg; CommandArgumentData value_arg; // Define the first (and only) variant of this arg. var_name_arg.arg_type = eArgTypeSettingVariableName; var_name_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg1.push_back(var_name_arg); // Define the first (and only) variant of this arg. value_arg.arg_type = eArgTypeValue; value_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg2.push_back(value_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg1); m_arguments.push_back(arg2); } ~CommandObjectSettingsAppend() override = default; // Overrides base class's behavior where WantsCompletion = // !WantsRawCommandString. bool WantsCompletion() override { return true; } int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { // Attempting to complete variable name if (request.GetCursorIndex() < 2) CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } protected: bool DoExecute(llvm::StringRef command, CommandReturnObject &result) override { result.SetStatus(eReturnStatusSuccessFinishNoResult); Args cmd_args(command); const size_t argc = cmd_args.GetArgumentCount(); if (argc < 2) { result.AppendError("'settings append' takes more arguments"); result.SetStatus(eReturnStatusFailed); return false; } const char *var_name = cmd_args.GetArgumentAtIndex(0); if ((var_name == nullptr) || (var_name[0] == '\0')) { result.AppendError("'settings append' command requires a valid variable " "name; No value supplied"); result.SetStatus(eReturnStatusFailed); return false; } // Do not perform cmd_args.Shift() since StringRef is manipulating the raw // character string later on. // Split the raw command into var_name and value pair. llvm::StringRef raw_str(command); std::string var_value_string = raw_str.split(var_name).second.str(); const char *var_value_cstr = Args::StripSpaces(var_value_string, true, true, false); Status error(m_interpreter.GetDebugger().SetPropertyValue( &m_exe_ctx, eVarSetOperationAppend, var_name, var_value_cstr)); if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectSettingsClear //------------------------------------------------------------------------- class CommandObjectSettingsClear : public CommandObjectParsed { public: CommandObjectSettingsClear(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "settings clear", "Clear a debugger setting array, dictionary, or string.", nullptr) { CommandArgumentEntry arg; CommandArgumentData var_name_arg; // Define the first (and only) variant of this arg. var_name_arg.arg_type = eArgTypeSettingVariableName; var_name_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(var_name_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectSettingsClear() override = default; int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { // Attempting to complete variable name if (request.GetCursorIndex() < 2) CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eSettingsNameCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { result.SetStatus(eReturnStatusSuccessFinishNoResult); const size_t argc = command.GetArgumentCount(); if (argc != 1) { result.AppendError("'settings clear' takes exactly one argument"); result.SetStatus(eReturnStatusFailed); return false; } const char *var_name = command.GetArgumentAtIndex(0); if ((var_name == nullptr) || (var_name[0] == '\0')) { result.AppendError("'settings clear' command requires a valid variable " "name; No value supplied"); result.SetStatus(eReturnStatusFailed); return false; } Status error(m_interpreter.GetDebugger().SetPropertyValue( &m_exe_ctx, eVarSetOperationClear, var_name, llvm::StringRef())); if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectMultiwordSettings //------------------------------------------------------------------------- CommandObjectMultiwordSettings::CommandObjectMultiwordSettings( CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "settings", "Commands for managing LLDB settings.", "settings []") { LoadSubCommand("set", CommandObjectSP(new CommandObjectSettingsSet(interpreter))); LoadSubCommand("show", CommandObjectSP(new CommandObjectSettingsShow(interpreter))); LoadSubCommand("list", CommandObjectSP(new CommandObjectSettingsList(interpreter))); LoadSubCommand("remove", CommandObjectSP(new CommandObjectSettingsRemove(interpreter))); LoadSubCommand("replace", CommandObjectSP( new CommandObjectSettingsReplace(interpreter))); LoadSubCommand( "insert-before", CommandObjectSP(new CommandObjectSettingsInsertBefore(interpreter))); LoadSubCommand( "insert-after", CommandObjectSP(new CommandObjectSettingsInsertAfter(interpreter))); LoadSubCommand("append", CommandObjectSP(new CommandObjectSettingsAppend(interpreter))); LoadSubCommand("clear", CommandObjectSP(new CommandObjectSettingsClear(interpreter))); } CommandObjectMultiwordSettings::~CommandObjectMultiwordSettings() = default; Index: vendor/lldb/dist/source/Commands/CommandObjectTarget.cpp =================================================================== --- vendor/lldb/dist/source/Commands/CommandObjectTarget.cpp (revision 337146) +++ vendor/lldb/dist/source/Commands/CommandObjectTarget.cpp (revision 337147) @@ -1,4900 +1,4900 @@ //===-- CommandObjectTarget.cpp ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "CommandObjectTarget.h" // Project includes #include "lldb/Core/Debugger.h" #include "lldb/Core/IOHandler.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/Section.h" #include "lldb/Core/State.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/DataFormatters/ValueObjectPrinter.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/StringConvert.h" #include "lldb/Host/Symbols.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Interpreter/OptionGroupArchitecture.h" #include "lldb/Interpreter/OptionGroupBoolean.h" #include "lldb/Interpreter/OptionGroupFile.h" #include "lldb/Interpreter/OptionGroupFormat.h" #include "lldb/Interpreter/OptionGroupPlatform.h" #include "lldb/Interpreter/OptionGroupString.h" #include "lldb/Interpreter/OptionGroupUInt64.h" #include "lldb/Interpreter/OptionGroupUUID.h" #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" #include "lldb/Interpreter/OptionGroupVariable.h" #include "lldb/Interpreter/Options.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/FuncUnwinders.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ABI.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadSpec.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/Timer.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatAdapters.h" // C Includes // C++ Includes #include using namespace lldb; using namespace lldb_private; static void DumpTargetInfo(uint32_t target_idx, Target *target, const char *prefix_cstr, bool show_stopped_process_status, Stream &strm) { const ArchSpec &target_arch = target->GetArchitecture(); Module *exe_module = target->GetExecutableModulePointer(); char exe_path[PATH_MAX]; bool exe_valid = false; if (exe_module) exe_valid = exe_module->GetFileSpec().GetPath(exe_path, sizeof(exe_path)); if (!exe_valid) ::strcpy(exe_path, ""); strm.Printf("%starget #%u: %s", prefix_cstr ? prefix_cstr : "", target_idx, exe_path); uint32_t properties = 0; if (target_arch.IsValid()) { strm.Printf("%sarch=", properties++ > 0 ? ", " : " ( "); target_arch.DumpTriple(strm); properties++; } PlatformSP platform_sp(target->GetPlatform()); if (platform_sp) strm.Printf("%splatform=%s", properties++ > 0 ? ", " : " ( ", platform_sp->GetName().GetCString()); ProcessSP process_sp(target->GetProcessSP()); bool show_process_status = false; if (process_sp) { lldb::pid_t pid = process_sp->GetID(); StateType state = process_sp->GetState(); if (show_stopped_process_status) show_process_status = StateIsStoppedState(state, true); const char *state_cstr = StateAsCString(state); if (pid != LLDB_INVALID_PROCESS_ID) strm.Printf("%spid=%" PRIu64, properties++ > 0 ? ", " : " ( ", pid); strm.Printf("%sstate=%s", properties++ > 0 ? ", " : " ( ", state_cstr); } if (properties > 0) strm.PutCString(" )\n"); else strm.EOL(); if (show_process_status) { const bool only_threads_with_stop_reason = true; const uint32_t start_frame = 0; const uint32_t num_frames = 1; const uint32_t num_frames_with_source = 1; const bool stop_format = false; process_sp->GetStatus(strm); process_sp->GetThreadStatus(strm, only_threads_with_stop_reason, start_frame, num_frames, num_frames_with_source, stop_format); } } static uint32_t DumpTargetList(TargetList &target_list, bool show_stopped_process_status, Stream &strm) { const uint32_t num_targets = target_list.GetNumTargets(); if (num_targets) { TargetSP selected_target_sp(target_list.GetSelectedTarget()); strm.PutCString("Current targets:\n"); for (uint32_t i = 0; i < num_targets; ++i) { TargetSP target_sp(target_list.GetTargetAtIndex(i)); if (target_sp) { bool is_selected = target_sp.get() == selected_target_sp.get(); DumpTargetInfo(i, target_sp.get(), is_selected ? "* " : " ", show_stopped_process_status, strm); } } } return num_targets; } #pragma mark CommandObjectTargetCreate //------------------------------------------------------------------------- // "target create" //------------------------------------------------------------------------- class CommandObjectTargetCreate : public CommandObjectParsed { public: CommandObjectTargetCreate(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "target create", "Create a target using the argument as the main executable.", nullptr), m_option_group(), m_arch_option(), m_core_file(LLDB_OPT_SET_1, false, "core", 'c', 0, eArgTypeFilename, "Fullpath to a core file to use for this target."), m_platform_path(LLDB_OPT_SET_1, false, "platform-path", 'P', 0, eArgTypePath, "Path to the remote file to use for this target."), m_symbol_file(LLDB_OPT_SET_1, false, "symfile", 's', 0, eArgTypeFilename, "Fullpath to a stand alone debug " "symbols file for when debug symbols " "are not in the executable."), m_remote_file( LLDB_OPT_SET_1, false, "remote-file", 'r', 0, eArgTypeFilename, "Fullpath to the file on the remote host if debugging remotely."), m_add_dependents(LLDB_OPT_SET_1, false, "no-dependents", 'd', "Don't load dependent files when creating the target, " "just add the specified executable.", true, true) { CommandArgumentEntry arg; CommandArgumentData file_arg; // Define the first (and only) variant of this arg. file_arg.arg_type = eArgTypeFilename; file_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(file_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); m_option_group.Append(&m_arch_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_core_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_platform_path, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_remote_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_add_dependents, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectTargetCreate() override = default; Options *GetOptions() override { return &m_option_group; } int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); FileSpec core_file(m_core_file.GetOptionValue().GetCurrentValue()); FileSpec remote_file(m_remote_file.GetOptionValue().GetCurrentValue()); if (core_file) { if (!core_file.Exists()) { result.AppendErrorWithFormat("core file '%s' doesn't exist", core_file.GetPath().c_str()); result.SetStatus(eReturnStatusFailed); return false; } if (!core_file.Readable()) { result.AppendErrorWithFormat("core file '%s' is not readable", core_file.GetPath().c_str()); result.SetStatus(eReturnStatusFailed); return false; } } if (argc == 1 || core_file || remote_file) { FileSpec symfile(m_symbol_file.GetOptionValue().GetCurrentValue()); if (symfile) { if (symfile.Exists()) { if (!symfile.Readable()) { result.AppendErrorWithFormat("symbol file '%s' is not readable", symfile.GetPath().c_str()); result.SetStatus(eReturnStatusFailed); return false; } } else { char symfile_path[PATH_MAX]; symfile.GetPath(symfile_path, sizeof(symfile_path)); result.AppendErrorWithFormat("invalid symbol file path '%s'", symfile_path); result.SetStatus(eReturnStatusFailed); return false; } } const char *file_path = command.GetArgumentAtIndex(0); static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, "(lldb) target create '%s'", file_path); FileSpec file_spec; if (file_path) file_spec.SetFile(file_path, true, FileSpec::Style::native); bool must_set_platform_path = false; Debugger &debugger = m_interpreter.GetDebugger(); TargetSP target_sp; llvm::StringRef arch_cstr = m_arch_option.GetArchitectureName(); const bool get_dependent_files = m_add_dependents.GetOptionValue().GetCurrentValue(); Status error(debugger.GetTargetList().CreateTarget( debugger, file_path, arch_cstr, get_dependent_files, nullptr, target_sp)); if (target_sp) { // Only get the platform after we create the target because we might // have switched platforms depending on what the arguments were to // CreateTarget() we can't rely on the selected platform. PlatformSP platform_sp = target_sp->GetPlatform(); if (remote_file) { if (platform_sp) { // I have a remote file.. two possible cases if (file_spec && file_spec.Exists()) { // if the remote file does not exist, push it there if (!platform_sp->GetFileExists(remote_file)) { Status err = platform_sp->PutFile(file_spec, remote_file); if (err.Fail()) { result.AppendError(err.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } } } else { // there is no local file and we need one // in order to make the remote ---> local transfer we need a // platform // TODO: if the user has passed in a --platform argument, use it // to fetch the right platform if (!platform_sp) { result.AppendError( "unable to perform remote debugging without a platform"); result.SetStatus(eReturnStatusFailed); return false; } if (file_path) { // copy the remote file to the local file Status err = platform_sp->GetFile(remote_file, file_spec); if (err.Fail()) { result.AppendError(err.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } } else { // make up a local file result.AppendError("remote --> local transfer without local " "path is not implemented yet"); result.SetStatus(eReturnStatusFailed); return false; } } } else { result.AppendError("no platform found for target"); result.SetStatus(eReturnStatusFailed); return false; } } if (symfile || remote_file) { ModuleSP module_sp(target_sp->GetExecutableModule()); if (module_sp) { if (symfile) module_sp->SetSymbolFileFileSpec(symfile); if (remote_file) { std::string remote_path = remote_file.GetPath(); target_sp->SetArg0(remote_path.c_str()); module_sp->SetPlatformFileSpec(remote_file); } } } debugger.GetTargetList().SetSelectedTarget(target_sp.get()); if (must_set_platform_path) { ModuleSpec main_module_spec(file_spec); ModuleSP module_sp = target_sp->GetSharedModule(main_module_spec); if (module_sp) module_sp->SetPlatformFileSpec(remote_file); } if (core_file) { char core_path[PATH_MAX]; core_file.GetPath(core_path, sizeof(core_path)); if (core_file.Exists()) { if (!core_file.Readable()) { result.AppendMessageWithFormat( "Core file '%s' is not readable.\n", core_path); result.SetStatus(eReturnStatusFailed); return false; } FileSpec core_file_dir; core_file_dir.GetDirectory() = core_file.GetDirectory(); target_sp->GetExecutableSearchPaths().Append(core_file_dir); ProcessSP process_sp(target_sp->CreateProcess( m_interpreter.GetDebugger().GetListener(), llvm::StringRef(), &core_file)); if (process_sp) { // Seems weird that we Launch a core file, but that is what we // do! error = process_sp->LoadCore(); if (error.Fail()) { result.AppendError( error.AsCString("can't find plug-in for core file")); result.SetStatus(eReturnStatusFailed); return false; } else { result.AppendMessageWithFormat( "Core file '%s' (%s) was loaded.\n", core_path, target_sp->GetArchitecture().GetArchitectureName()); result.SetStatus(eReturnStatusSuccessFinishNoResult); } } else { result.AppendErrorWithFormat( "Unable to find process plug-in for core file '%s'\n", core_path); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat("Core file '%s' does not exist\n", core_path); result.SetStatus(eReturnStatusFailed); } } else { result.AppendMessageWithFormat( "Current executable set to '%s' (%s).\n", file_path, target_sp->GetArchitecture().GetArchitectureName()); result.SetStatus(eReturnStatusSuccessFinishNoResult); } } else { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat("'%s' takes exactly one executable path " "argument, or use the --core option.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } private: OptionGroupOptions m_option_group; OptionGroupArchitecture m_arch_option; OptionGroupFile m_core_file; OptionGroupFile m_platform_path; OptionGroupFile m_symbol_file; OptionGroupFile m_remote_file; OptionGroupBoolean m_add_dependents; }; #pragma mark CommandObjectTargetList //---------------------------------------------------------------------- // "target list" //---------------------------------------------------------------------- class CommandObjectTargetList : public CommandObjectParsed { public: CommandObjectTargetList(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "target list", "List all current targets in the current debug session.", nullptr) { } ~CommandObjectTargetList() override = default; protected: bool DoExecute(Args &args, CommandReturnObject &result) override { if (args.GetArgumentCount() == 0) { Stream &strm = result.GetOutputStream(); bool show_stopped_process_status = false; if (DumpTargetList(m_interpreter.GetDebugger().GetTargetList(), show_stopped_process_status, strm) == 0) { strm.PutCString("No targets.\n"); } result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendError("the 'target list' command takes no arguments\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetSelect //---------------------------------------------------------------------- // "target select" //---------------------------------------------------------------------- class CommandObjectTargetSelect : public CommandObjectParsed { public: CommandObjectTargetSelect(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "target select", "Select a target as the current target by target index.", nullptr) { } ~CommandObjectTargetSelect() override = default; protected: bool DoExecute(Args &args, CommandReturnObject &result) override { if (args.GetArgumentCount() == 1) { bool success = false; const char *target_idx_arg = args.GetArgumentAtIndex(0); uint32_t target_idx = StringConvert::ToUInt32(target_idx_arg, UINT32_MAX, 0, &success); if (success) { TargetList &target_list = m_interpreter.GetDebugger().GetTargetList(); const uint32_t num_targets = target_list.GetNumTargets(); if (target_idx < num_targets) { TargetSP target_sp(target_list.GetTargetAtIndex(target_idx)); if (target_sp) { Stream &strm = result.GetOutputStream(); target_list.SetSelectedTarget(target_sp.get()); bool show_stopped_process_status = false; DumpTargetList(target_list, show_stopped_process_status, strm); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat("target #%u is NULL in target list\n", target_idx); result.SetStatus(eReturnStatusFailed); } } else { if (num_targets > 0) { result.AppendErrorWithFormat( "index %u is out of range, valid target indexes are 0 - %u\n", target_idx, num_targets - 1); } else { result.AppendErrorWithFormat( "index %u is out of range since there are no active targets\n", target_idx); } result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat("invalid index string value '%s'\n", target_idx_arg); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError( "'target select' takes a single argument: a target index\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetSelect //---------------------------------------------------------------------- // "target delete" //---------------------------------------------------------------------- class CommandObjectTargetDelete : public CommandObjectParsed { public: CommandObjectTargetDelete(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "target delete", "Delete one or more targets by target index.", nullptr), m_option_group(), m_all_option(LLDB_OPT_SET_1, false, "all", 'a', "Delete all targets.", false, true), m_cleanup_option( LLDB_OPT_SET_1, false, "clean", 'c', "Perform extra cleanup to minimize memory consumption after " "deleting the target. " "By default, LLDB will keep in memory any modules previously " "loaded by the target as well " "as all of its debug info. Specifying --clean will unload all of " "these shared modules and " "cause them to be reparsed again the next time the target is run", false, true) { m_option_group.Append(&m_all_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_cleanup_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectTargetDelete() override = default; Options *GetOptions() override { return &m_option_group; } protected: bool DoExecute(Args &args, CommandReturnObject &result) override { const size_t argc = args.GetArgumentCount(); std::vector delete_target_list; TargetList &target_list = m_interpreter.GetDebugger().GetTargetList(); TargetSP target_sp; if (m_all_option.GetOptionValue()) { for (int i = 0; i < target_list.GetNumTargets(); ++i) delete_target_list.push_back(target_list.GetTargetAtIndex(i)); } else if (argc > 0) { const uint32_t num_targets = target_list.GetNumTargets(); // Bail out if don't have any targets. if (num_targets == 0) { result.AppendError("no targets to delete"); result.SetStatus(eReturnStatusFailed); return false; } for (auto &entry : args.entries()) { uint32_t target_idx; if (entry.ref.getAsInteger(0, target_idx)) { result.AppendErrorWithFormat("invalid target index '%s'\n", entry.c_str()); result.SetStatus(eReturnStatusFailed); return false; } if (target_idx < num_targets) { target_sp = target_list.GetTargetAtIndex(target_idx); if (target_sp) { delete_target_list.push_back(target_sp); continue; } } if (num_targets > 1) result.AppendErrorWithFormat("target index %u is out of range, valid " "target indexes are 0 - %u\n", target_idx, num_targets - 1); else result.AppendErrorWithFormat( "target index %u is out of range, the only valid index is 0\n", target_idx); result.SetStatus(eReturnStatusFailed); return false; } } else { target_sp = target_list.GetSelectedTarget(); if (!target_sp) { result.AppendErrorWithFormat("no target is currently selected\n"); result.SetStatus(eReturnStatusFailed); return false; } delete_target_list.push_back(target_sp); } const size_t num_targets_to_delete = delete_target_list.size(); for (size_t idx = 0; idx < num_targets_to_delete; ++idx) { target_sp = delete_target_list[idx]; target_list.DeleteTarget(target_sp); target_sp->Destroy(); } // If "--clean" was specified, prune any orphaned shared modules from the // global shared module list if (m_cleanup_option.GetOptionValue()) { const bool mandatory = true; ModuleList::RemoveOrphanSharedModules(mandatory); } result.GetOutputStream().Printf("%u targets deleted.\n", (uint32_t)num_targets_to_delete); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } OptionGroupOptions m_option_group; OptionGroupBoolean m_all_option; OptionGroupBoolean m_cleanup_option; }; #pragma mark CommandObjectTargetVariable //---------------------------------------------------------------------- // "target variable" //---------------------------------------------------------------------- class CommandObjectTargetVariable : public CommandObjectParsed { static const uint32_t SHORT_OPTION_FILE = 0x66696c65; // 'file' static const uint32_t SHORT_OPTION_SHLB = 0x73686c62; // 'shlb' public: CommandObjectTargetVariable(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "target variable", "Read global variables for the current target, " "before or while running a process.", nullptr, eCommandRequiresTarget), m_option_group(), m_option_variable(false), // Don't include frame options m_option_format(eFormatDefault), m_option_compile_units(LLDB_OPT_SET_1, false, "file", SHORT_OPTION_FILE, 0, eArgTypeFilename, "A basename or fullpath to a file that contains " "global variables. This option can be " "specified multiple times."), m_option_shared_libraries( LLDB_OPT_SET_1, false, "shlib", SHORT_OPTION_SHLB, 0, eArgTypeFilename, "A basename or fullpath to a shared library to use in the search " "for global " "variables. This option can be specified multiple times."), m_varobj_options() { CommandArgumentEntry arg; CommandArgumentData var_name_arg; // Define the first (and only) variant of this arg. var_name_arg.arg_type = eArgTypeVarName; var_name_arg.arg_repetition = eArgRepeatPlus; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(var_name_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_option_format, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_1); m_option_group.Append(&m_option_compile_units, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_option_shared_libraries, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectTargetVariable() override = default; void DumpValueObject(Stream &s, VariableSP &var_sp, ValueObjectSP &valobj_sp, const char *root_name) { DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions()); if (!valobj_sp->GetTargetSP()->GetDisplayRuntimeSupportValues() && valobj_sp->IsRuntimeSupportValue()) return; switch (var_sp->GetScope()) { case eValueTypeVariableGlobal: if (m_option_variable.show_scope) s.PutCString("GLOBAL: "); break; case eValueTypeVariableStatic: if (m_option_variable.show_scope) s.PutCString("STATIC: "); break; case eValueTypeVariableArgument: if (m_option_variable.show_scope) s.PutCString(" ARG: "); break; case eValueTypeVariableLocal: if (m_option_variable.show_scope) s.PutCString(" LOCAL: "); break; case eValueTypeVariableThreadLocal: if (m_option_variable.show_scope) s.PutCString("THREAD: "); break; default: break; } if (m_option_variable.show_decl) { bool show_fullpaths = false; bool show_module = true; if (var_sp->DumpDeclaration(&s, show_fullpaths, show_module)) s.PutCString(": "); } const Format format = m_option_format.GetFormat(); if (format != eFormatDefault) options.SetFormat(format); options.SetRootValueObjectName(root_name); valobj_sp->Dump(s, options); } static size_t GetVariableCallback(void *baton, const char *name, VariableList &variable_list) { Target *target = static_cast(baton); if (target) { return target->GetImages().FindGlobalVariables(ConstString(name), UINT32_MAX, variable_list); } return 0; } Options *GetOptions() override { return &m_option_group; } protected: void DumpGlobalVariableList(const ExecutionContext &exe_ctx, const SymbolContext &sc, const VariableList &variable_list, Stream &s) { size_t count = variable_list.GetSize(); if (count > 0) { if (sc.module_sp) { if (sc.comp_unit) { s.Printf("Global variables for %s in %s:\n", sc.comp_unit->GetPath().c_str(), sc.module_sp->GetFileSpec().GetPath().c_str()); } else { s.Printf("Global variables for %s\n", sc.module_sp->GetFileSpec().GetPath().c_str()); } } else if (sc.comp_unit) { s.Printf("Global variables for %s\n", sc.comp_unit->GetPath().c_str()); } for (uint32_t i = 0; i < count; ++i) { VariableSP var_sp(variable_list.GetVariableAtIndex(i)); if (var_sp) { ValueObjectSP valobj_sp(ValueObjectVariable::Create( exe_ctx.GetBestExecutionContextScope(), var_sp)); if (valobj_sp) DumpValueObject(s, var_sp, valobj_sp, var_sp->GetName().GetCString()); } } } } bool DoExecute(Args &args, CommandReturnObject &result) override { Target *target = m_exe_ctx.GetTargetPtr(); const size_t argc = args.GetArgumentCount(); Stream &s = result.GetOutputStream(); if (argc > 0) { // TODO: Convert to entry-based iteration. Requires converting // DumpValueObject. for (size_t idx = 0; idx < argc; ++idx) { VariableList variable_list; ValueObjectList valobj_list; const char *arg = args.GetArgumentAtIndex(idx); size_t matches = 0; bool use_var_name = false; if (m_option_variable.use_regex) { RegularExpression regex(llvm::StringRef::withNullAsEmpty(arg)); if (!regex.IsValid()) { result.GetErrorStream().Printf( "error: invalid regular expression: '%s'\n", arg); result.SetStatus(eReturnStatusFailed); return false; } use_var_name = true; matches = target->GetImages().FindGlobalVariables(regex, UINT32_MAX, variable_list); } else { Status error(Variable::GetValuesForVariableExpressionPath( arg, m_exe_ctx.GetBestExecutionContextScope(), GetVariableCallback, target, variable_list, valobj_list)); matches = variable_list.GetSize(); } if (matches == 0) { result.GetErrorStream().Printf( "error: can't find global variable '%s'\n", arg); result.SetStatus(eReturnStatusFailed); return false; } else { for (uint32_t global_idx = 0; global_idx < matches; ++global_idx) { VariableSP var_sp(variable_list.GetVariableAtIndex(global_idx)); if (var_sp) { ValueObjectSP valobj_sp( valobj_list.GetValueObjectAtIndex(global_idx)); if (!valobj_sp) valobj_sp = ValueObjectVariable::Create( m_exe_ctx.GetBestExecutionContextScope(), var_sp); if (valobj_sp) DumpValueObject(s, var_sp, valobj_sp, use_var_name ? var_sp->GetName().GetCString() : arg); } } } } } else { const FileSpecList &compile_units = m_option_compile_units.GetOptionValue().GetCurrentValue(); const FileSpecList &shlibs = m_option_shared_libraries.GetOptionValue().GetCurrentValue(); SymbolContextList sc_list; const size_t num_compile_units = compile_units.GetSize(); const size_t num_shlibs = shlibs.GetSize(); if (num_compile_units == 0 && num_shlibs == 0) { bool success = false; StackFrame *frame = m_exe_ctx.GetFramePtr(); CompileUnit *comp_unit = nullptr; if (frame) { SymbolContext sc = frame->GetSymbolContext(eSymbolContextCompUnit); if (sc.comp_unit) { const bool can_create = true; VariableListSP comp_unit_varlist_sp( sc.comp_unit->GetVariableList(can_create)); if (comp_unit_varlist_sp) { size_t count = comp_unit_varlist_sp->GetSize(); if (count > 0) { DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp, s); success = true; } } } } if (!success) { if (frame) { if (comp_unit) result.AppendErrorWithFormat( "no global variables in current compile unit: %s\n", comp_unit->GetPath().c_str()); else result.AppendErrorWithFormat( "no debug information for frame %u\n", frame->GetFrameIndex()); } else result.AppendError("'target variable' takes one or more global " "variable names as arguments\n"); result.SetStatus(eReturnStatusFailed); } } else { SymbolContextList sc_list; const bool append = true; // We have one or more compile unit or shlib if (num_shlibs > 0) { for (size_t shlib_idx = 0; shlib_idx < num_shlibs; ++shlib_idx) { const FileSpec module_file(shlibs.GetFileSpecAtIndex(shlib_idx)); ModuleSpec module_spec(module_file); ModuleSP module_sp( target->GetImages().FindFirstModule(module_spec)); if (module_sp) { if (num_compile_units > 0) { for (size_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) module_sp->FindCompileUnits( compile_units.GetFileSpecAtIndex(cu_idx), append, sc_list); } else { SymbolContext sc; sc.module_sp = module_sp; sc_list.Append(sc); } } else { // Didn't find matching shlib/module in target... result.AppendErrorWithFormat( "target doesn't contain the specified shared library: %s\n", module_file.GetPath().c_str()); } } } else { // No shared libraries, we just want to find globals for the compile // units files that were specified for (size_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) target->GetImages().FindCompileUnits( compile_units.GetFileSpecAtIndex(cu_idx), append, sc_list); } const uint32_t num_scs = sc_list.GetSize(); if (num_scs > 0) { SymbolContext sc; for (uint32_t sc_idx = 0; sc_idx < num_scs; ++sc_idx) { if (sc_list.GetContextAtIndex(sc_idx, sc)) { if (sc.comp_unit) { const bool can_create = true; VariableListSP comp_unit_varlist_sp( sc.comp_unit->GetVariableList(can_create)); if (comp_unit_varlist_sp) DumpGlobalVariableList(m_exe_ctx, sc, *comp_unit_varlist_sp, s); } else if (sc.module_sp) { // Get all global variables for this module lldb_private::RegularExpression all_globals_regex( llvm::StringRef( ".")); // Any global with at least one character VariableList variable_list; sc.module_sp->FindGlobalVariables(all_globals_regex, UINT32_MAX, variable_list); DumpGlobalVariableList(m_exe_ctx, sc, variable_list, s); } } } } } } if (m_interpreter.TruncationWarningNecessary()) { result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(), m_cmd_name.c_str()); m_interpreter.TruncationWarningGiven(); } return result.Succeeded(); } OptionGroupOptions m_option_group; OptionGroupVariable m_option_variable; OptionGroupFormat m_option_format; OptionGroupFileList m_option_compile_units; OptionGroupFileList m_option_shared_libraries; OptionGroupValueObjectDisplay m_varobj_options; }; #pragma mark CommandObjectTargetModulesSearchPathsAdd class CommandObjectTargetModulesSearchPathsAdd : public CommandObjectParsed { public: CommandObjectTargetModulesSearchPathsAdd(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "target modules search-paths add", "Add new image search paths substitution pairs to " "the current target.", nullptr) { CommandArgumentEntry arg; CommandArgumentData old_prefix_arg; CommandArgumentData new_prefix_arg; // Define the first variant of this arg pair. old_prefix_arg.arg_type = eArgTypeOldPathPrefix; old_prefix_arg.arg_repetition = eArgRepeatPairPlus; // Define the first variant of this arg pair. new_prefix_arg.arg_type = eArgTypeNewPathPrefix; new_prefix_arg.arg_repetition = eArgRepeatPairPlus; // There are two required arguments that must always occur together, i.e. // an argument "pair". Because they must always occur together, they are // treated as two variants of one argument rather than two independent // arguments. Push them both into the first argument position for // m_arguments... arg.push_back(old_prefix_arg); arg.push_back(new_prefix_arg); m_arguments.push_back(arg); } ~CommandObjectTargetModulesSearchPathsAdd() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) { const size_t argc = command.GetArgumentCount(); if (argc & 1) { result.AppendError("add requires an even number of arguments\n"); result.SetStatus(eReturnStatusFailed); } else { for (size_t i = 0; i < argc; i += 2) { const char *from = command.GetArgumentAtIndex(i); const char *to = command.GetArgumentAtIndex(i + 1); if (from[0] && to[0]) { Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); if (log) { log->Printf("target modules search path adding ImageSearchPath " "pair: '%s' -> '%s'", from, to); } bool last_pair = ((argc - i) == 2); target->GetImageSearchPathList().Append( ConstString(from), ConstString(to), last_pair); // Notify if this is the last pair result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { if (from[0]) result.AppendError(" can't be empty\n"); else result.AppendError(" can't be empty\n"); result.SetStatus(eReturnStatusFailed); } } } } else { result.AppendError("invalid target\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesSearchPathsClear class CommandObjectTargetModulesSearchPathsClear : public CommandObjectParsed { public: CommandObjectTargetModulesSearchPathsClear(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "target modules search-paths clear", "Clear all current image search path substitution " "pairs from the current target.", "target modules search-paths clear") {} ~CommandObjectTargetModulesSearchPathsClear() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) { bool notify = true; target->GetImageSearchPathList().Clear(notify); result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendError("invalid target\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesSearchPathsInsert class CommandObjectTargetModulesSearchPathsInsert : public CommandObjectParsed { public: CommandObjectTargetModulesSearchPathsInsert(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "target modules search-paths insert", "Insert a new image search path substitution pair " "into the current target at the specified index.", nullptr) { CommandArgumentEntry arg1; CommandArgumentEntry arg2; CommandArgumentData index_arg; CommandArgumentData old_prefix_arg; CommandArgumentData new_prefix_arg; // Define the first and only variant of this arg. index_arg.arg_type = eArgTypeIndex; index_arg.arg_repetition = eArgRepeatPlain; // Put the one and only variant into the first arg for m_arguments: arg1.push_back(index_arg); // Define the first variant of this arg pair. old_prefix_arg.arg_type = eArgTypeOldPathPrefix; old_prefix_arg.arg_repetition = eArgRepeatPairPlus; // Define the first variant of this arg pair. new_prefix_arg.arg_type = eArgTypeNewPathPrefix; new_prefix_arg.arg_repetition = eArgRepeatPairPlus; // There are two required arguments that must always occur together, i.e. // an argument "pair". Because they must always occur together, they are // treated as two variants of one argument rather than two independent // arguments. Push them both into the same argument position for // m_arguments... arg2.push_back(old_prefix_arg); arg2.push_back(new_prefix_arg); // Add arguments to m_arguments. m_arguments.push_back(arg1); m_arguments.push_back(arg2); } ~CommandObjectTargetModulesSearchPathsInsert() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) { size_t argc = command.GetArgumentCount(); // check for at least 3 arguments and an odd number of parameters if (argc >= 3 && argc & 1) { bool success = false; uint32_t insert_idx = StringConvert::ToUInt32( command.GetArgumentAtIndex(0), UINT32_MAX, 0, &success); if (!success) { result.AppendErrorWithFormat( " parameter is not an integer: '%s'.\n", command.GetArgumentAtIndex(0)); result.SetStatus(eReturnStatusFailed); return result.Succeeded(); } // shift off the index command.Shift(); argc = command.GetArgumentCount(); for (uint32_t i = 0; i < argc; i += 2, ++insert_idx) { const char *from = command.GetArgumentAtIndex(i); const char *to = command.GetArgumentAtIndex(i + 1); if (from[0] && to[0]) { bool last_pair = ((argc - i) == 2); target->GetImageSearchPathList().Insert( ConstString(from), ConstString(to), insert_idx, last_pair); result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { if (from[0]) result.AppendError(" can't be empty\n"); else result.AppendError(" can't be empty\n"); result.SetStatus(eReturnStatusFailed); return false; } } } else { result.AppendError("insert requires at least three arguments\n"); result.SetStatus(eReturnStatusFailed); return result.Succeeded(); } } else { result.AppendError("invalid target\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesSearchPathsList class CommandObjectTargetModulesSearchPathsList : public CommandObjectParsed { public: CommandObjectTargetModulesSearchPathsList(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "target modules search-paths list", "List all current image search path substitution " "pairs in the current target.", "target modules search-paths list") {} ~CommandObjectTargetModulesSearchPathsList() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) { if (command.GetArgumentCount() != 0) { result.AppendError("list takes no arguments\n"); result.SetStatus(eReturnStatusFailed); return result.Succeeded(); } target->GetImageSearchPathList().Dump(&result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendError("invalid target\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesSearchPathsQuery class CommandObjectTargetModulesSearchPathsQuery : public CommandObjectParsed { public: CommandObjectTargetModulesSearchPathsQuery(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "target modules search-paths query", "Transform a path using the first applicable image search path.", nullptr) { CommandArgumentEntry arg; CommandArgumentData path_arg; // Define the first (and only) variant of this arg. path_arg.arg_type = eArgTypeDirectoryName; path_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(path_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectTargetModulesSearchPathsQuery() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) { if (command.GetArgumentCount() != 1) { result.AppendError("query requires one argument\n"); result.SetStatus(eReturnStatusFailed); return result.Succeeded(); } ConstString orig(command.GetArgumentAtIndex(0)); ConstString transformed; if (target->GetImageSearchPathList().RemapPath(orig, transformed)) result.GetOutputStream().Printf("%s\n", transformed.GetCString()); else result.GetOutputStream().Printf("%s\n", orig.GetCString()); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendError("invalid target\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; //---------------------------------------------------------------------- // Static Helper functions //---------------------------------------------------------------------- static void DumpModuleArchitecture(Stream &strm, Module *module, bool full_triple, uint32_t width) { if (module) { StreamString arch_strm; if (full_triple) module->GetArchitecture().DumpTriple(arch_strm); else arch_strm.PutCString(module->GetArchitecture().GetArchitectureName()); std::string arch_str = arch_strm.GetString(); if (width) strm.Printf("%-*s", width, arch_str.c_str()); else strm.PutCString(arch_str); } } static void DumpModuleUUID(Stream &strm, Module *module) { if (module && module->GetUUID().IsValid()) module->GetUUID().Dump(&strm); else strm.PutCString(" "); } static uint32_t DumpCompileUnitLineTable(CommandInterpreter &interpreter, Stream &strm, Module *module, const FileSpec &file_spec, bool load_addresses) { uint32_t num_matches = 0; if (module) { SymbolContextList sc_list; num_matches = module->ResolveSymbolContextsForFileSpec( file_spec, 0, false, eSymbolContextCompUnit, sc_list); for (uint32_t i = 0; i < num_matches; ++i) { SymbolContext sc; if (sc_list.GetContextAtIndex(i, sc)) { if (i > 0) strm << "\n\n"; strm << "Line table for " << *static_cast(sc.comp_unit) << " in `" << module->GetFileSpec().GetFilename() << "\n"; LineTable *line_table = sc.comp_unit->GetLineTable(); if (line_table) line_table->GetDescription( &strm, interpreter.GetExecutionContext().GetTargetPtr(), lldb::eDescriptionLevelBrief); else strm << "No line table"; } } } return num_matches; } static void DumpFullpath(Stream &strm, const FileSpec *file_spec_ptr, uint32_t width) { if (file_spec_ptr) { if (width > 0) { std::string fullpath = file_spec_ptr->GetPath(); strm.Printf("%-*s", width, fullpath.c_str()); return; } else { file_spec_ptr->Dump(&strm); return; } } // Keep the width spacing correct if things go wrong... if (width > 0) strm.Printf("%-*s", width, ""); } static void DumpDirectory(Stream &strm, const FileSpec *file_spec_ptr, uint32_t width) { if (file_spec_ptr) { if (width > 0) strm.Printf("%-*s", width, file_spec_ptr->GetDirectory().AsCString("")); else file_spec_ptr->GetDirectory().Dump(&strm); return; } // Keep the width spacing correct if things go wrong... if (width > 0) strm.Printf("%-*s", width, ""); } static void DumpBasename(Stream &strm, const FileSpec *file_spec_ptr, uint32_t width) { if (file_spec_ptr) { if (width > 0) strm.Printf("%-*s", width, file_spec_ptr->GetFilename().AsCString("")); else file_spec_ptr->GetFilename().Dump(&strm); return; } // Keep the width spacing correct if things go wrong... if (width > 0) strm.Printf("%-*s", width, ""); } static size_t DumpModuleObjfileHeaders(Stream &strm, ModuleList &module_list) { size_t num_dumped = 0; std::lock_guard guard(module_list.GetMutex()); const size_t num_modules = module_list.GetSize(); if (num_modules > 0) { strm.Printf("Dumping headers for %" PRIu64 " module(s).\n", static_cast(num_modules)); strm.IndentMore(); for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { Module *module = module_list.GetModulePointerAtIndexUnlocked(image_idx); if (module) { if (num_dumped++ > 0) { strm.EOL(); strm.EOL(); } ObjectFile *objfile = module->GetObjectFile(); if (objfile) objfile->Dump(&strm); else { strm.Format("No object file for module: {0:F}\n", module->GetFileSpec()); } } } strm.IndentLess(); } return num_dumped; } static void DumpModuleSymtab(CommandInterpreter &interpreter, Stream &strm, Module *module, SortOrder sort_order) { if (module) { SymbolVendor *sym_vendor = module->GetSymbolVendor(); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) symtab->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(), sort_order); } } } static void DumpModuleSections(CommandInterpreter &interpreter, Stream &strm, Module *module) { if (module) { SectionList *section_list = module->GetSectionList(); if (section_list) { strm.Printf("Sections for '%s' (%s):\n", module->GetSpecificationDescription().c_str(), module->GetArchitecture().GetArchitectureName()); strm.IndentMore(); section_list->Dump(&strm, interpreter.GetExecutionContext().GetTargetPtr(), true, UINT32_MAX); strm.IndentLess(); } } } static bool DumpModuleSymbolVendor(Stream &strm, Module *module) { if (module) { SymbolVendor *symbol_vendor = module->GetSymbolVendor(true); if (symbol_vendor) { symbol_vendor->Dump(&strm); return true; } } return false; } static void DumpAddress(ExecutionContextScope *exe_scope, const Address &so_addr, bool verbose, Stream &strm) { strm.IndentMore(); strm.Indent(" Address: "); so_addr.Dump(&strm, exe_scope, Address::DumpStyleModuleWithFileAddress); strm.PutCString(" ("); so_addr.Dump(&strm, exe_scope, Address::DumpStyleSectionNameOffset); strm.PutCString(")\n"); strm.Indent(" Summary: "); const uint32_t save_indent = strm.GetIndentLevel(); strm.SetIndentLevel(save_indent + 13); so_addr.Dump(&strm, exe_scope, Address::DumpStyleResolvedDescription); strm.SetIndentLevel(save_indent); // Print out detailed address information when verbose is enabled if (verbose) { strm.EOL(); so_addr.Dump(&strm, exe_scope, Address::DumpStyleDetailedSymbolContext); } strm.IndentLess(); } static bool LookupAddressInModule(CommandInterpreter &interpreter, Stream &strm, Module *module, uint32_t resolve_mask, lldb::addr_t raw_addr, lldb::addr_t offset, bool verbose) { if (module) { lldb::addr_t addr = raw_addr - offset; Address so_addr; SymbolContext sc; Target *target = interpreter.GetExecutionContext().GetTargetPtr(); if (target && !target->GetSectionLoadList().IsEmpty()) { if (!target->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) return false; else if (so_addr.GetModule().get() != module) return false; } else { if (!module->ResolveFileAddress(addr, so_addr)) return false; } ExecutionContextScope *exe_scope = interpreter.GetExecutionContext().GetBestExecutionContextScope(); DumpAddress(exe_scope, so_addr, verbose, strm); // strm.IndentMore(); // strm.Indent (" Address: "); // so_addr.Dump (&strm, exe_scope, // Address::DumpStyleModuleWithFileAddress); // strm.PutCString (" ("); // so_addr.Dump (&strm, exe_scope, // Address::DumpStyleSectionNameOffset); // strm.PutCString (")\n"); // strm.Indent (" Summary: "); // const uint32_t save_indent = strm.GetIndentLevel (); // strm.SetIndentLevel (save_indent + 13); // so_addr.Dump (&strm, exe_scope, // Address::DumpStyleResolvedDescription); // strm.SetIndentLevel (save_indent); // // Print out detailed address information when verbose is enabled // if (verbose) // { // strm.EOL(); // so_addr.Dump (&strm, exe_scope, // Address::DumpStyleDetailedSymbolContext); // } // strm.IndentLess(); return true; } return false; } static uint32_t LookupSymbolInModule(CommandInterpreter &interpreter, Stream &strm, Module *module, const char *name, bool name_is_regex, bool verbose) { if (module) { SymbolContext sc; SymbolVendor *sym_vendor = module->GetSymbolVendor(); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) { std::vector match_indexes; ConstString symbol_name(name); uint32_t num_matches = 0; if (name_is_regex) { RegularExpression name_regexp(symbol_name.GetStringRef()); num_matches = symtab->AppendSymbolIndexesMatchingRegExAndType( name_regexp, eSymbolTypeAny, match_indexes); } else { num_matches = symtab->AppendSymbolIndexesWithName(symbol_name, match_indexes); } if (num_matches > 0) { strm.Indent(); strm.Printf("%u symbols match %s'%s' in ", num_matches, name_is_regex ? "the regular expression " : "", name); DumpFullpath(strm, &module->GetFileSpec(), 0); strm.PutCString(":\n"); strm.IndentMore(); for (uint32_t i = 0; i < num_matches; ++i) { Symbol *symbol = symtab->SymbolAtIndex(match_indexes[i]); if (symbol && symbol->ValueIsAddress()) { DumpAddress(interpreter.GetExecutionContext() .GetBestExecutionContextScope(), symbol->GetAddressRef(), verbose, strm); } } strm.IndentLess(); return num_matches; } } } } return 0; } static void DumpSymbolContextList(ExecutionContextScope *exe_scope, Stream &strm, SymbolContextList &sc_list, bool verbose) { strm.IndentMore(); const uint32_t num_matches = sc_list.GetSize(); for (uint32_t i = 0; i < num_matches; ++i) { SymbolContext sc; if (sc_list.GetContextAtIndex(i, sc)) { AddressRange range; sc.GetAddressRange(eSymbolContextEverything, 0, true, range); DumpAddress(exe_scope, range.GetBaseAddress(), verbose, strm); } } strm.IndentLess(); } static size_t LookupFunctionInModule(CommandInterpreter &interpreter, Stream &strm, Module *module, const char *name, bool name_is_regex, bool include_inlines, bool include_symbols, bool verbose) { if (module && name && name[0]) { SymbolContextList sc_list; const bool append = true; size_t num_matches = 0; if (name_is_regex) { RegularExpression function_name_regex((llvm::StringRef(name))); num_matches = module->FindFunctions(function_name_regex, include_symbols, include_inlines, append, sc_list); } else { ConstString function_name(name); num_matches = module->FindFunctions( function_name, nullptr, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); } if (num_matches) { strm.Indent(); strm.Printf("%" PRIu64 " match%s found in ", (uint64_t)num_matches, num_matches > 1 ? "es" : ""); DumpFullpath(strm, &module->GetFileSpec(), 0); strm.PutCString(":\n"); DumpSymbolContextList( interpreter.GetExecutionContext().GetBestExecutionContextScope(), strm, sc_list, verbose); } return num_matches; } return 0; } static size_t LookupTypeInModule(CommandInterpreter &interpreter, Stream &strm, Module *module, const char *name_cstr, bool name_is_regex) { if (module && name_cstr && name_cstr[0]) { TypeList type_list; const uint32_t max_num_matches = UINT32_MAX; size_t num_matches = 0; bool name_is_fully_qualified = false; SymbolContext sc; ConstString name(name_cstr); llvm::DenseSet searched_symbol_files; num_matches = module->FindTypes(sc, name, name_is_fully_qualified, max_num_matches, searched_symbol_files, type_list); if (num_matches) { strm.Indent(); strm.Printf("%" PRIu64 " match%s found in ", (uint64_t)num_matches, num_matches > 1 ? "es" : ""); DumpFullpath(strm, &module->GetFileSpec(), 0); strm.PutCString(":\n"); for (TypeSP type_sp : type_list.Types()) { if (type_sp) { // Resolve the clang type so that any forward references to types // that haven't yet been parsed will get parsed. type_sp->GetFullCompilerType(); type_sp->GetDescription(&strm, eDescriptionLevelFull, true); // Print all typedef chains TypeSP typedef_type_sp(type_sp); TypeSP typedefed_type_sp(typedef_type_sp->GetTypedefType()); while (typedefed_type_sp) { strm.EOL(); strm.Printf(" typedef '%s': ", typedef_type_sp->GetName().GetCString()); typedefed_type_sp->GetFullCompilerType(); typedefed_type_sp->GetDescription(&strm, eDescriptionLevelFull, true); typedef_type_sp = typedefed_type_sp; typedefed_type_sp = typedef_type_sp->GetTypedefType(); } } strm.EOL(); } } return num_matches; } return 0; } static size_t LookupTypeHere(CommandInterpreter &interpreter, Stream &strm, const SymbolContext &sym_ctx, const char *name_cstr, bool name_is_regex) { if (!sym_ctx.module_sp) return 0; TypeList type_list; const uint32_t max_num_matches = UINT32_MAX; size_t num_matches = 1; bool name_is_fully_qualified = false; ConstString name(name_cstr); llvm::DenseSet searched_symbol_files; num_matches = sym_ctx.module_sp->FindTypes( sym_ctx, name, name_is_fully_qualified, max_num_matches, searched_symbol_files, type_list); if (num_matches) { strm.Indent(); strm.PutCString("Best match found in "); DumpFullpath(strm, &sym_ctx.module_sp->GetFileSpec(), 0); strm.PutCString(":\n"); TypeSP type_sp(type_list.GetTypeAtIndex(0)); if (type_sp) { // Resolve the clang type so that any forward references to types that // haven't yet been parsed will get parsed. type_sp->GetFullCompilerType(); type_sp->GetDescription(&strm, eDescriptionLevelFull, true); // Print all typedef chains TypeSP typedef_type_sp(type_sp); TypeSP typedefed_type_sp(typedef_type_sp->GetTypedefType()); while (typedefed_type_sp) { strm.EOL(); strm.Printf(" typedef '%s': ", typedef_type_sp->GetName().GetCString()); typedefed_type_sp->GetFullCompilerType(); typedefed_type_sp->GetDescription(&strm, eDescriptionLevelFull, true); typedef_type_sp = typedefed_type_sp; typedefed_type_sp = typedef_type_sp->GetTypedefType(); } } strm.EOL(); } return num_matches; } static uint32_t LookupFileAndLineInModule(CommandInterpreter &interpreter, Stream &strm, Module *module, const FileSpec &file_spec, uint32_t line, bool check_inlines, bool verbose) { if (module && file_spec) { SymbolContextList sc_list; const uint32_t num_matches = module->ResolveSymbolContextsForFileSpec( file_spec, line, check_inlines, eSymbolContextEverything, sc_list); if (num_matches > 0) { strm.Indent(); strm.Printf("%u match%s found in ", num_matches, num_matches > 1 ? "es" : ""); strm << file_spec; if (line > 0) strm.Printf(":%u", line); strm << " in "; DumpFullpath(strm, &module->GetFileSpec(), 0); strm.PutCString(":\n"); DumpSymbolContextList( interpreter.GetExecutionContext().GetBestExecutionContextScope(), strm, sc_list, verbose); return num_matches; } } return 0; } static size_t FindModulesByName(Target *target, const char *module_name, ModuleList &module_list, bool check_global_list) { FileSpec module_file_spec(module_name, false); ModuleSpec module_spec(module_file_spec); const size_t initial_size = module_list.GetSize(); if (check_global_list) { // Check the global list std::lock_guard guard( Module::GetAllocationModuleCollectionMutex()); const size_t num_modules = Module::GetNumberAllocatedModules(); ModuleSP module_sp; for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { Module *module = Module::GetAllocatedModuleAtIndex(image_idx); if (module) { if (module->MatchesModuleSpec(module_spec)) { module_sp = module->shared_from_this(); module_list.AppendIfNeeded(module_sp); } } } } else { if (target) { const size_t num_matches = target->GetImages().FindModules(module_spec, module_list); // Not found in our module list for our target, check the main shared // module list in case it is a extra file used somewhere else if (num_matches == 0) { module_spec.GetArchitecture() = target->GetArchitecture(); ModuleList::FindSharedModules(module_spec, module_list); } } else { ModuleList::FindSharedModules(module_spec, module_list); } } return module_list.GetSize() - initial_size; } #pragma mark CommandObjectTargetModulesModuleAutoComplete //---------------------------------------------------------------------- // A base command object class that can auto complete with module file // paths //---------------------------------------------------------------------- class CommandObjectTargetModulesModuleAutoComplete : public CommandObjectParsed { public: CommandObjectTargetModulesModuleAutoComplete(CommandInterpreter &interpreter, const char *name, const char *help, const char *syntax) : CommandObjectParsed(interpreter, name, help, syntax) { CommandArgumentEntry arg; CommandArgumentData file_arg; // Define the first (and only) variant of this arg. file_arg.arg_type = eArgTypeFilename; file_arg.arg_repetition = eArgRepeatStar; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(file_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectTargetModulesModuleAutoComplete() override = default; int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eModuleCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } }; #pragma mark CommandObjectTargetModulesSourceFileAutoComplete //---------------------------------------------------------------------- // A base command object class that can auto complete with module source // file paths //---------------------------------------------------------------------- class CommandObjectTargetModulesSourceFileAutoComplete : public CommandObjectParsed { public: CommandObjectTargetModulesSourceFileAutoComplete( CommandInterpreter &interpreter, const char *name, const char *help, const char *syntax, uint32_t flags) : CommandObjectParsed(interpreter, name, help, syntax, flags) { CommandArgumentEntry arg; CommandArgumentData source_file_arg; // Define the first (and only) variant of this arg. source_file_arg.arg_type = eArgTypeSourceFile; source_file_arg.arg_repetition = eArgRepeatPlus; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(source_file_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectTargetModulesSourceFileAutoComplete() override = default; int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eSourceFileCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } }; #pragma mark CommandObjectTargetModulesDumpObjfile class CommandObjectTargetModulesDumpObjfile : public CommandObjectTargetModulesModuleAutoComplete { public: CommandObjectTargetModulesDumpObjfile(CommandInterpreter &interpreter) : CommandObjectTargetModulesModuleAutoComplete( interpreter, "target modules dump objfile", "Dump the object file headers from one or more target modules.", nullptr) {} ~CommandObjectTargetModulesDumpObjfile() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == nullptr) { result.AppendError("invalid target, create a debug target using the " "'target create' command"); result.SetStatus(eReturnStatusFailed); return false; } uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); result.GetOutputStream().SetAddressByteSize(addr_byte_size); result.GetErrorStream().SetAddressByteSize(addr_byte_size); size_t num_dumped = 0; if (command.GetArgumentCount() == 0) { // Dump all headers for all modules images num_dumped = DumpModuleObjfileHeaders(result.GetOutputStream(), target->GetImages()); if (num_dumped == 0) { result.AppendError("the target has no associated executable images"); result.SetStatus(eReturnStatusFailed); } } else { // Find the modules that match the basename or full path. ModuleList module_list; const char *arg_cstr; for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; ++arg_idx) { size_t num_matched = FindModulesByName(target, arg_cstr, module_list, true); if (num_matched == 0) { result.AppendWarningWithFormat( "Unable to find an image that matches '%s'.\n", arg_cstr); } } // Dump all the modules we found. num_dumped = DumpModuleObjfileHeaders(result.GetOutputStream(), module_list); } if (num_dumped > 0) { result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendError("no matching executable images found"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesDumpSymtab static OptionEnumValueElement g_sort_option_enumeration[4] = { {eSortOrderNone, "none", "No sorting, use the original symbol table order."}, {eSortOrderByAddress, "address", "Sort output by symbol address."}, {eSortOrderByName, "name", "Sort output by symbol name."}, {0, nullptr, nullptr}}; static OptionDefinition g_target_modules_dump_symtab_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "sort", 's', OptionParser::eRequiredArgument, nullptr, g_sort_option_enumeration, 0, eArgTypeSortOrder, "Supply a sort order when dumping the symbol table." } // clang-format on }; class CommandObjectTargetModulesDumpSymtab : public CommandObjectTargetModulesModuleAutoComplete { public: CommandObjectTargetModulesDumpSymtab(CommandInterpreter &interpreter) : CommandObjectTargetModulesModuleAutoComplete( interpreter, "target modules dump symtab", "Dump the symbol table from one or more target modules.", nullptr), m_options() {} ~CommandObjectTargetModulesDumpSymtab() override = default; Options *GetOptions() override { return &m_options; } class CommandOptions : public Options { public: CommandOptions() : Options(), m_sort_order(eSortOrderNone) {} ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 's': m_sort_order = (SortOrder)OptionArgParser::ToOptionEnum( option_arg, GetDefinitions()[option_idx].enum_values, eSortOrderNone, error); break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_sort_order = eSortOrderNone; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_target_modules_dump_symtab_options); } SortOrder m_sort_order; }; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == nullptr) { result.AppendError("invalid target, create a debug target using the " "'target create' command"); result.SetStatus(eReturnStatusFailed); return false; } else { uint32_t num_dumped = 0; uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); result.GetOutputStream().SetAddressByteSize(addr_byte_size); result.GetErrorStream().SetAddressByteSize(addr_byte_size); if (command.GetArgumentCount() == 0) { // Dump all sections for all modules images std::lock_guard guard( target->GetImages().GetMutex()); const size_t num_modules = target->GetImages().GetSize(); if (num_modules > 0) { result.GetOutputStream().Printf("Dumping symbol table for %" PRIu64 " modules.\n", (uint64_t)num_modules); for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { if (num_dumped > 0) { result.GetOutputStream().EOL(); result.GetOutputStream().EOL(); } if (m_interpreter.WasInterrupted()) break; num_dumped++; DumpModuleSymtab( m_interpreter, result.GetOutputStream(), target->GetImages().GetModulePointerAtIndexUnlocked(image_idx), m_options.m_sort_order); } } else { result.AppendError("the target has no associated executable images"); result.SetStatus(eReturnStatusFailed); return false; } } else { // Dump specified images (by basename or fullpath) const char *arg_cstr; for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; ++arg_idx) { ModuleList module_list; const size_t num_matches = FindModulesByName(target, arg_cstr, module_list, true); if (num_matches > 0) { for (size_t i = 0; i < num_matches; ++i) { Module *module = module_list.GetModulePointerAtIndex(i); if (module) { if (num_dumped > 0) { result.GetOutputStream().EOL(); result.GetOutputStream().EOL(); } if (m_interpreter.WasInterrupted()) break; num_dumped++; DumpModuleSymtab(m_interpreter, result.GetOutputStream(), module, m_options.m_sort_order); } } } else result.AppendWarningWithFormat( "Unable to find an image that matches '%s'.\n", arg_cstr); } } if (num_dumped > 0) result.SetStatus(eReturnStatusSuccessFinishResult); else { result.AppendError("no matching executable images found"); result.SetStatus(eReturnStatusFailed); } } return result.Succeeded(); } CommandOptions m_options; }; #pragma mark CommandObjectTargetModulesDumpSections //---------------------------------------------------------------------- // Image section dumping command //---------------------------------------------------------------------- class CommandObjectTargetModulesDumpSections : public CommandObjectTargetModulesModuleAutoComplete { public: CommandObjectTargetModulesDumpSections(CommandInterpreter &interpreter) : CommandObjectTargetModulesModuleAutoComplete( interpreter, "target modules dump sections", "Dump the sections from one or more target modules.", //"target modules dump sections [ ...]") nullptr) {} ~CommandObjectTargetModulesDumpSections() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == nullptr) { result.AppendError("invalid target, create a debug target using the " "'target create' command"); result.SetStatus(eReturnStatusFailed); return false; } else { uint32_t num_dumped = 0; uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); result.GetOutputStream().SetAddressByteSize(addr_byte_size); result.GetErrorStream().SetAddressByteSize(addr_byte_size); if (command.GetArgumentCount() == 0) { // Dump all sections for all modules images const size_t num_modules = target->GetImages().GetSize(); if (num_modules > 0) { result.GetOutputStream().Printf("Dumping sections for %" PRIu64 " modules.\n", (uint64_t)num_modules); for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { if (m_interpreter.WasInterrupted()) break; num_dumped++; DumpModuleSections( m_interpreter, result.GetOutputStream(), target->GetImages().GetModulePointerAtIndex(image_idx)); } } else { result.AppendError("the target has no associated executable images"); result.SetStatus(eReturnStatusFailed); return false; } } else { // Dump specified images (by basename or fullpath) const char *arg_cstr; for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; ++arg_idx) { ModuleList module_list; const size_t num_matches = FindModulesByName(target, arg_cstr, module_list, true); if (num_matches > 0) { for (size_t i = 0; i < num_matches; ++i) { if (m_interpreter.WasInterrupted()) break; Module *module = module_list.GetModulePointerAtIndex(i); if (module) { num_dumped++; DumpModuleSections(m_interpreter, result.GetOutputStream(), module); } } } else { // Check the global list std::lock_guard guard( Module::GetAllocationModuleCollectionMutex()); result.AppendWarningWithFormat( "Unable to find an image that matches '%s'.\n", arg_cstr); } } } if (num_dumped > 0) result.SetStatus(eReturnStatusSuccessFinishResult); else { result.AppendError("no matching executable images found"); result.SetStatus(eReturnStatusFailed); } } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesDumpSymfile //---------------------------------------------------------------------- // Image debug symbol dumping command //---------------------------------------------------------------------- class CommandObjectTargetModulesDumpSymfile : public CommandObjectTargetModulesModuleAutoComplete { public: CommandObjectTargetModulesDumpSymfile(CommandInterpreter &interpreter) : CommandObjectTargetModulesModuleAutoComplete( interpreter, "target modules dump symfile", "Dump the debug symbol file for one or more target modules.", //"target modules dump symfile [ ...]") nullptr) {} ~CommandObjectTargetModulesDumpSymfile() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == nullptr) { result.AppendError("invalid target, create a debug target using the " "'target create' command"); result.SetStatus(eReturnStatusFailed); return false; } else { uint32_t num_dumped = 0; uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); result.GetOutputStream().SetAddressByteSize(addr_byte_size); result.GetErrorStream().SetAddressByteSize(addr_byte_size); if (command.GetArgumentCount() == 0) { // Dump all sections for all modules images const ModuleList &target_modules = target->GetImages(); std::lock_guard guard(target_modules.GetMutex()); const size_t num_modules = target_modules.GetSize(); if (num_modules > 0) { result.GetOutputStream().Printf("Dumping debug symbols for %" PRIu64 " modules.\n", (uint64_t)num_modules); for (uint32_t image_idx = 0; image_idx < num_modules; ++image_idx) { if (m_interpreter.WasInterrupted()) break; if (DumpModuleSymbolVendor( result.GetOutputStream(), target_modules.GetModulePointerAtIndexUnlocked(image_idx))) num_dumped++; } } else { result.AppendError("the target has no associated executable images"); result.SetStatus(eReturnStatusFailed); return false; } } else { // Dump specified images (by basename or fullpath) const char *arg_cstr; for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; ++arg_idx) { ModuleList module_list; const size_t num_matches = FindModulesByName(target, arg_cstr, module_list, true); if (num_matches > 0) { for (size_t i = 0; i < num_matches; ++i) { if (m_interpreter.WasInterrupted()) break; Module *module = module_list.GetModulePointerAtIndex(i); if (module) { if (DumpModuleSymbolVendor(result.GetOutputStream(), module)) num_dumped++; } } } else result.AppendWarningWithFormat( "Unable to find an image that matches '%s'.\n", arg_cstr); } } if (num_dumped > 0) result.SetStatus(eReturnStatusSuccessFinishResult); else { result.AppendError("no matching executable images found"); result.SetStatus(eReturnStatusFailed); } } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesDumpLineTable //---------------------------------------------------------------------- // Image debug line table dumping command //---------------------------------------------------------------------- class CommandObjectTargetModulesDumpLineTable : public CommandObjectTargetModulesSourceFileAutoComplete { public: CommandObjectTargetModulesDumpLineTable(CommandInterpreter &interpreter) : CommandObjectTargetModulesSourceFileAutoComplete( interpreter, "target modules dump line-table", "Dump the line table for one or more compilation units.", nullptr, eCommandRequiresTarget) {} ~CommandObjectTargetModulesDumpLineTable() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = m_exe_ctx.GetTargetPtr(); uint32_t total_num_dumped = 0; uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); result.GetOutputStream().SetAddressByteSize(addr_byte_size); result.GetErrorStream().SetAddressByteSize(addr_byte_size); if (command.GetArgumentCount() == 0) { result.AppendError("file option must be specified."); result.SetStatus(eReturnStatusFailed); return result.Succeeded(); } else { // Dump specified images (by basename or fullpath) const char *arg_cstr; for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != nullptr; ++arg_idx) { FileSpec file_spec(arg_cstr, false); const ModuleList &target_modules = target->GetImages(); std::lock_guard guard(target_modules.GetMutex()); const size_t num_modules = target_modules.GetSize(); if (num_modules > 0) { uint32_t num_dumped = 0; for (uint32_t i = 0; i < num_modules; ++i) { if (m_interpreter.WasInterrupted()) break; if (DumpCompileUnitLineTable( m_interpreter, result.GetOutputStream(), target_modules.GetModulePointerAtIndexUnlocked(i), file_spec, m_exe_ctx.GetProcessPtr() && m_exe_ctx.GetProcessRef().IsAlive())) num_dumped++; } if (num_dumped == 0) result.AppendWarningWithFormat( "No source filenames matched '%s'.\n", arg_cstr); else total_num_dumped += num_dumped; } } } if (total_num_dumped > 0) result.SetStatus(eReturnStatusSuccessFinishResult); else { result.AppendError("no source filenames matched any command arguments"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetModulesDump //---------------------------------------------------------------------- // Dump multi-word command for target modules //---------------------------------------------------------------------- class CommandObjectTargetModulesDump : public CommandObjectMultiword { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ CommandObjectTargetModulesDump(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "target modules dump", "Commands for dumping information about one or " "more target modules.", "target modules dump " "[headers|symtab|sections|symfile|line-table] " "[ ...]") { LoadSubCommand("objfile", CommandObjectSP( new CommandObjectTargetModulesDumpObjfile(interpreter))); LoadSubCommand( "symtab", CommandObjectSP(new CommandObjectTargetModulesDumpSymtab(interpreter))); LoadSubCommand("sections", CommandObjectSP(new CommandObjectTargetModulesDumpSections( interpreter))); LoadSubCommand("symfile", CommandObjectSP( new CommandObjectTargetModulesDumpSymfile(interpreter))); LoadSubCommand("line-table", CommandObjectSP(new CommandObjectTargetModulesDumpLineTable( interpreter))); } ~CommandObjectTargetModulesDump() override = default; }; class CommandObjectTargetModulesAdd : public CommandObjectParsed { public: CommandObjectTargetModulesAdd(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "target modules add", "Add a new module to the current target's modules.", "target modules add []"), m_option_group(), m_symbol_file(LLDB_OPT_SET_1, false, "symfile", 's', 0, eArgTypeFilename, "Fullpath to a stand alone debug " "symbols file for when debug symbols " "are not in the executable.") { m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_symbol_file, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectTargetModulesAdd() override = default; Options *GetOptions() override { return &m_option_group; } int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } protected: OptionGroupOptions m_option_group; OptionGroupUUID m_uuid_option_group; OptionGroupFile m_symbol_file; bool DoExecute(Args &args, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == nullptr) { result.AppendError("invalid target, create a debug target using the " "'target create' command"); result.SetStatus(eReturnStatusFailed); return false; } else { bool flush = false; const size_t argc = args.GetArgumentCount(); if (argc == 0) { if (m_uuid_option_group.GetOptionValue().OptionWasSet()) { // We are given a UUID only, go locate the file ModuleSpec module_spec; module_spec.GetUUID() = m_uuid_option_group.GetOptionValue().GetCurrentValue(); if (m_symbol_file.GetOptionValue().OptionWasSet()) module_spec.GetSymbolFileSpec() = m_symbol_file.GetOptionValue().GetCurrentValue(); if (Symbols::DownloadObjectAndSymbolFile(module_spec)) { ModuleSP module_sp(target->GetSharedModule(module_spec)); if (module_sp) { result.SetStatus(eReturnStatusSuccessFinishResult); return true; } else { StreamString strm; module_spec.GetUUID().Dump(&strm); if (module_spec.GetFileSpec()) { if (module_spec.GetSymbolFileSpec()) { result.AppendErrorWithFormat( "Unable to create the executable or symbol file with " "UUID %s with path %s and symbol file %s", strm.GetData(), module_spec.GetFileSpec().GetPath().c_str(), module_spec.GetSymbolFileSpec().GetPath().c_str()); } else { result.AppendErrorWithFormat( "Unable to create the executable or symbol file with " "UUID %s with path %s", strm.GetData(), module_spec.GetFileSpec().GetPath().c_str()); } } else { result.AppendErrorWithFormat("Unable to create the executable " "or symbol file with UUID %s", strm.GetData()); } result.SetStatus(eReturnStatusFailed); return false; } } else { StreamString strm; module_spec.GetUUID().Dump(&strm); result.AppendErrorWithFormat( "Unable to locate the executable or symbol file with UUID %s", strm.GetData()); result.SetStatus(eReturnStatusFailed); return false; } } else { result.AppendError( "one or more executable image paths must be specified"); result.SetStatus(eReturnStatusFailed); return false; } } else { for (auto &entry : args.entries()) { if (entry.ref.empty()) continue; FileSpec file_spec(entry.ref, true); if (file_spec.Exists()) { ModuleSpec module_spec(file_spec); if (m_uuid_option_group.GetOptionValue().OptionWasSet()) module_spec.GetUUID() = m_uuid_option_group.GetOptionValue().GetCurrentValue(); if (m_symbol_file.GetOptionValue().OptionWasSet()) module_spec.GetSymbolFileSpec() = m_symbol_file.GetOptionValue().GetCurrentValue(); if (!module_spec.GetArchitecture().IsValid()) module_spec.GetArchitecture() = target->GetArchitecture(); Status error; ModuleSP module_sp(target->GetSharedModule(module_spec, &error)); if (!module_sp) { const char *error_cstr = error.AsCString(); if (error_cstr) result.AppendError(error_cstr); else result.AppendErrorWithFormat("unsupported module: %s", entry.c_str()); result.SetStatus(eReturnStatusFailed); return false; } else { flush = true; } result.SetStatus(eReturnStatusSuccessFinishResult); } else { std::string resolved_path = file_spec.GetPath(); result.SetStatus(eReturnStatusFailed); if (resolved_path != entry.ref) { result.AppendErrorWithFormat( "invalid module path '%s' with resolved path '%s'\n", entry.ref.str().c_str(), resolved_path.c_str()); break; } result.AppendErrorWithFormat("invalid module path '%s'\n", entry.c_str()); break; } } } if (flush) { ProcessSP process = target->GetProcessSP(); if (process) process->Flush(); } } return result.Succeeded(); } }; class CommandObjectTargetModulesLoad : public CommandObjectTargetModulesModuleAutoComplete { public: CommandObjectTargetModulesLoad(CommandInterpreter &interpreter) : CommandObjectTargetModulesModuleAutoComplete( interpreter, "target modules load", "Set the load addresses for " "one or more sections in a " "target module.", "target modules load [--file --uuid ] " "
[
....]"), m_option_group(), m_file_option(LLDB_OPT_SET_1, false, "file", 'f', 0, eArgTypeName, "Fullpath or basename for module to load.", ""), m_load_option(LLDB_OPT_SET_1, false, "load", 'l', "Write file contents to the memory.", false, true), m_pc_option(LLDB_OPT_SET_1, false, "set-pc-to-entry", 'p', "Set PC to the entry point." " Only applicable with '--load' option.", false, true), m_slide_option(LLDB_OPT_SET_1, false, "slide", 's', 0, eArgTypeOffset, "Set the load address for all sections to be the " "virtual address in the file plus the offset.", 0) { m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_load_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_pc_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_slide_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectTargetModulesLoad() override = default; Options *GetOptions() override { return &m_option_group; } protected: bool DoExecute(Args &args, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); const bool load = m_load_option.GetOptionValue().GetCurrentValue(); const bool set_pc = m_pc_option.GetOptionValue().GetCurrentValue(); if (target == nullptr) { result.AppendError("invalid target, create a debug target using the " "'target create' command"); result.SetStatus(eReturnStatusFailed); return false; } else { const size_t argc = args.GetArgumentCount(); ModuleSpec module_spec; bool search_using_module_spec = false; // Allow "load" option to work without --file or --uuid option. if (load) { if (!m_file_option.GetOptionValue().OptionWasSet() && !m_uuid_option_group.GetOptionValue().OptionWasSet()) { ModuleList &module_list = target->GetImages(); if (module_list.GetSize() == 1) { search_using_module_spec = true; module_spec.GetFileSpec() = module_list.GetModuleAtIndex(0)->GetFileSpec(); } } } if (m_file_option.GetOptionValue().OptionWasSet()) { search_using_module_spec = true; const char *arg_cstr = m_file_option.GetOptionValue().GetCurrentValue(); const bool use_global_module_list = true; ModuleList module_list; const size_t num_matches = FindModulesByName( target, arg_cstr, module_list, use_global_module_list); if (num_matches == 1) { module_spec.GetFileSpec() = module_list.GetModuleAtIndex(0)->GetFileSpec(); } else if (num_matches > 1) { search_using_module_spec = false; result.AppendErrorWithFormat( "more than 1 module matched by name '%s'\n", arg_cstr); result.SetStatus(eReturnStatusFailed); } else { search_using_module_spec = false; result.AppendErrorWithFormat("no object file for module '%s'\n", arg_cstr); result.SetStatus(eReturnStatusFailed); } } if (m_uuid_option_group.GetOptionValue().OptionWasSet()) { search_using_module_spec = true; module_spec.GetUUID() = m_uuid_option_group.GetOptionValue().GetCurrentValue(); } if (search_using_module_spec) { ModuleList matching_modules; const size_t num_matches = target->GetImages().FindModules(module_spec, matching_modules); char path[PATH_MAX]; if (num_matches == 1) { Module *module = matching_modules.GetModulePointerAtIndex(0); if (module) { ObjectFile *objfile = module->GetObjectFile(); if (objfile) { SectionList *section_list = module->GetSectionList(); if (section_list) { bool changed = false; if (argc == 0) { if (m_slide_option.GetOptionValue().OptionWasSet()) { const addr_t slide = m_slide_option.GetOptionValue().GetCurrentValue(); const bool slide_is_offset = true; module->SetLoadAddress(*target, slide, slide_is_offset, changed); } else { result.AppendError("one or more section name + load " "address pair must be specified"); result.SetStatus(eReturnStatusFailed); return false; } } else { if (m_slide_option.GetOptionValue().OptionWasSet()) { result.AppendError("The \"--slide \" option can't " "be used in conjunction with setting " "section load addresses.\n"); result.SetStatus(eReturnStatusFailed); return false; } for (size_t i = 0; i < argc; i += 2) { const char *sect_name = args.GetArgumentAtIndex(i); const char *load_addr_cstr = args.GetArgumentAtIndex(i + 1); if (sect_name && load_addr_cstr) { ConstString const_sect_name(sect_name); bool success = false; addr_t load_addr = StringConvert::ToUInt64( load_addr_cstr, LLDB_INVALID_ADDRESS, 0, &success); if (success) { SectionSP section_sp( section_list->FindSectionByName(const_sect_name)); if (section_sp) { if (section_sp->IsThreadSpecific()) { result.AppendErrorWithFormat( "thread specific sections are not yet " "supported (section '%s')\n", sect_name); result.SetStatus(eReturnStatusFailed); break; } else { if (target->GetSectionLoadList() .SetSectionLoadAddress(section_sp, load_addr)) changed = true; result.AppendMessageWithFormat( "section '%s' loaded at 0x%" PRIx64 "\n", sect_name, load_addr); } } else { result.AppendErrorWithFormat("no section found that " "matches the section " "name '%s'\n", sect_name); result.SetStatus(eReturnStatusFailed); break; } } else { result.AppendErrorWithFormat( "invalid load address string '%s'\n", load_addr_cstr); result.SetStatus(eReturnStatusFailed); break; } } else { if (sect_name) result.AppendError("section names must be followed by " "a load address.\n"); else result.AppendError("one or more section name + load " "address pair must be specified.\n"); result.SetStatus(eReturnStatusFailed); break; } } } if (changed) { target->ModulesDidLoad(matching_modules); Process *process = m_exe_ctx.GetProcessPtr(); if (process) process->Flush(); } if (load) { ProcessSP process = target->CalculateProcess(); Address file_entry = objfile->GetEntryPointAddress(); if (!process) { result.AppendError("No process"); return false; } if (set_pc && !file_entry.IsValid()) { result.AppendError("No entry address in object file"); return false; } std::vector loadables( objfile->GetLoadableData(*target)); if (loadables.size() == 0) { result.AppendError("No loadable sections"); return false; } Status error = process->WriteObjectFile(std::move(loadables)); if (error.Fail()) { result.AppendError(error.AsCString()); return false; } if (set_pc) { ThreadList &thread_list = process->GetThreadList(); ThreadSP curr_thread(thread_list.GetSelectedThread()); RegisterContextSP reg_context( curr_thread->GetRegisterContext()); reg_context->SetPC(file_entry.GetLoadAddress(target)); } } } else { module->GetFileSpec().GetPath(path, sizeof(path)); result.AppendErrorWithFormat( "no sections in object file '%s'\n", path); result.SetStatus(eReturnStatusFailed); } } else { module->GetFileSpec().GetPath(path, sizeof(path)); result.AppendErrorWithFormat("no object file for module '%s'\n", path); result.SetStatus(eReturnStatusFailed); } } else { FileSpec *module_spec_file = module_spec.GetFileSpecPtr(); if (module_spec_file) { module_spec_file->GetPath(path, sizeof(path)); result.AppendErrorWithFormat("invalid module '%s'.\n", path); } else result.AppendError("no module spec"); result.SetStatus(eReturnStatusFailed); } } else { std::string uuid_str; if (module_spec.GetFileSpec()) module_spec.GetFileSpec().GetPath(path, sizeof(path)); else path[0] = '\0'; if (module_spec.GetUUIDPtr()) uuid_str = module_spec.GetUUID().GetAsString(); if (num_matches > 1) { result.AppendErrorWithFormat( "multiple modules match%s%s%s%s:\n", path[0] ? " file=" : "", path, !uuid_str.empty() ? " uuid=" : "", uuid_str.c_str()); for (size_t i = 0; i < num_matches; ++i) { if (matching_modules.GetModulePointerAtIndex(i) ->GetFileSpec() .GetPath(path, sizeof(path))) result.AppendMessageWithFormat("%s\n", path); } } else { result.AppendErrorWithFormat( "no modules were found that match%s%s%s%s.\n", path[0] ? " file=" : "", path, !uuid_str.empty() ? " uuid=" : "", uuid_str.c_str()); } result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("either the \"--file \" or the \"--uuid " "\" option must be specified.\n"); result.SetStatus(eReturnStatusFailed); return false; } } return result.Succeeded(); } OptionGroupOptions m_option_group; OptionGroupUUID m_uuid_option_group; OptionGroupString m_file_option; OptionGroupBoolean m_load_option; OptionGroupBoolean m_pc_option; OptionGroupUInt64 m_slide_option; }; //---------------------------------------------------------------------- // List images with associated information //---------------------------------------------------------------------- static OptionDefinition g_target_modules_list_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Display the image at this address." }, { LLDB_OPT_SET_1, false, "arch", 'A', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth, "Display the architecture when listing images." }, { LLDB_OPT_SET_1, false, "triple", 't', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth, "Display the triple when listing images." }, { LLDB_OPT_SET_1, false, "header", 'h', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display the image header address as a load address if debugging, a file address otherwise." }, { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display the image header address offset from the header file address (the slide amount)." }, { LLDB_OPT_SET_1, false, "uuid", 'u', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display the UUID when listing images." }, { LLDB_OPT_SET_1, false, "fullpath", 'f', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth, "Display the fullpath to the image object file." }, { LLDB_OPT_SET_1, false, "directory", 'd', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth, "Display the directory with optional width for the image object file." }, { LLDB_OPT_SET_1, false, "basename", 'b', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth, "Display the basename with optional width for the image object file." }, { LLDB_OPT_SET_1, false, "symfile", 's', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth, "Display the fullpath to the image symbol file with optional width." }, { LLDB_OPT_SET_1, false, "symfile-unique", 'S', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth, "Display the symbol file with optional width only if it is different from the executable object file." }, { LLDB_OPT_SET_1, false, "mod-time", 'm', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth, "Display the modification time with optional width of the module." }, { LLDB_OPT_SET_1, false, "ref-count", 'r', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeWidth, "Display the reference count if the module is still in the shared module cache." }, { LLDB_OPT_SET_1, false, "pointer", 'p', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeNone, "Display the module pointer." }, { LLDB_OPT_SET_1, false, "global", 'g', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display the modules from the global module list, not just the current target." } // clang-format on }; class CommandObjectTargetModulesList : public CommandObjectParsed { public: class CommandOptions : public Options { public: CommandOptions() : Options(), m_format_array(), m_use_global_module_list(false), m_module_addr(LLDB_INVALID_ADDRESS) {} ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; if (short_option == 'g') { m_use_global_module_list = true; } else if (short_option == 'a') { m_module_addr = OptionArgParser::ToAddress( execution_context, option_arg, LLDB_INVALID_ADDRESS, &error); } else { unsigned long width = 0; option_arg.getAsInteger(0, width); m_format_array.push_back(std::make_pair(short_option, width)); } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_format_array.clear(); m_use_global_module_list = false; m_module_addr = LLDB_INVALID_ADDRESS; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_target_modules_list_options); } // Instance variables to hold the values for command options. typedef std::vector> FormatWidthCollection; FormatWidthCollection m_format_array; bool m_use_global_module_list; lldb::addr_t m_module_addr; }; CommandObjectTargetModulesList(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "target modules list", "List current executable and dependent shared library images.", "target modules list []"), m_options() {} ~CommandObjectTargetModulesList() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); const bool use_global_module_list = m_options.m_use_global_module_list; // Define a local module list here to ensure it lives longer than any // "locker" object which might lock its contents below (through the // "module_list_ptr" variable). ModuleList module_list; if (target == nullptr && !use_global_module_list) { result.AppendError("invalid target, create a debug target using the " "'target create' command"); result.SetStatus(eReturnStatusFailed); return false; } else { if (target) { uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); result.GetOutputStream().SetAddressByteSize(addr_byte_size); result.GetErrorStream().SetAddressByteSize(addr_byte_size); } // Dump all sections for all modules images Stream &strm = result.GetOutputStream(); if (m_options.m_module_addr != LLDB_INVALID_ADDRESS) { if (target) { Address module_address; if (module_address.SetLoadAddress(m_options.m_module_addr, target)) { ModuleSP module_sp(module_address.GetModule()); if (module_sp) { PrintModule(target, module_sp.get(), 0, strm); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat( "Couldn't find module matching address: 0x%" PRIx64 ".", m_options.m_module_addr); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat( "Couldn't find module containing address: 0x%" PRIx64 ".", m_options.m_module_addr); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError( "Can only look up modules by address with a valid target."); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } size_t num_modules = 0; // This locker will be locked on the mutex in module_list_ptr if it is // non-nullptr. Otherwise it will lock the // AllocationModuleCollectionMutex when accessing the global module list // directly. std::unique_lock guard( Module::GetAllocationModuleCollectionMutex(), std::defer_lock); const ModuleList *module_list_ptr = nullptr; const size_t argc = command.GetArgumentCount(); if (argc == 0) { if (use_global_module_list) { guard.lock(); num_modules = Module::GetNumberAllocatedModules(); } else { module_list_ptr = &target->GetImages(); } } else { // TODO: Convert to entry based iteration. Requires converting // FindModulesByName. for (size_t i = 0; i < argc; ++i) { // Dump specified images (by basename or fullpath) const char *arg_cstr = command.GetArgumentAtIndex(i); const size_t num_matches = FindModulesByName( target, arg_cstr, module_list, use_global_module_list); if (num_matches == 0) { if (argc == 1) { result.AppendErrorWithFormat("no modules found that match '%s'", arg_cstr); result.SetStatus(eReturnStatusFailed); return false; } } } module_list_ptr = &module_list; } std::unique_lock lock; if (module_list_ptr != nullptr) { lock = std::unique_lock(module_list_ptr->GetMutex()); num_modules = module_list_ptr->GetSize(); } if (num_modules > 0) { for (uint32_t image_idx = 0; image_idx < num_modules; ++image_idx) { ModuleSP module_sp; Module *module; if (module_list_ptr) { module_sp = module_list_ptr->GetModuleAtIndexUnlocked(image_idx); module = module_sp.get(); } else { module = Module::GetAllocatedModuleAtIndex(image_idx); module_sp = module->shared_from_this(); } const size_t indent = strm.Printf("[%3u] ", image_idx); PrintModule(target, module, indent, strm); } result.SetStatus(eReturnStatusSuccessFinishResult); } else { if (argc) { if (use_global_module_list) result.AppendError( "the global module list has no matching modules"); else result.AppendError("the target has no matching modules"); } else { if (use_global_module_list) result.AppendError("the global module list is empty"); else result.AppendError( "the target has no associated executable images"); } result.SetStatus(eReturnStatusFailed); return false; } } return result.Succeeded(); } void PrintModule(Target *target, Module *module, int indent, Stream &strm) { if (module == nullptr) { strm.PutCString("Null module"); return; } bool dump_object_name = false; if (m_options.m_format_array.empty()) { m_options.m_format_array.push_back(std::make_pair('u', 0)); m_options.m_format_array.push_back(std::make_pair('h', 0)); m_options.m_format_array.push_back(std::make_pair('f', 0)); m_options.m_format_array.push_back(std::make_pair('S', 0)); } const size_t num_entries = m_options.m_format_array.size(); bool print_space = false; for (size_t i = 0; i < num_entries; ++i) { if (print_space) strm.PutChar(' '); print_space = true; const char format_char = m_options.m_format_array[i].first; uint32_t width = m_options.m_format_array[i].second; switch (format_char) { case 'A': DumpModuleArchitecture(strm, module, false, width); break; case 't': DumpModuleArchitecture(strm, module, true, width); break; case 'f': DumpFullpath(strm, &module->GetFileSpec(), width); dump_object_name = true; break; case 'd': DumpDirectory(strm, &module->GetFileSpec(), width); break; case 'b': DumpBasename(strm, &module->GetFileSpec(), width); dump_object_name = true; break; case 'h': case 'o': // Image header address { uint32_t addr_nibble_width = target ? (target->GetArchitecture().GetAddressByteSize() * 2) : 16; ObjectFile *objfile = module->GetObjectFile(); if (objfile) { Address header_addr(objfile->GetHeaderAddress()); if (header_addr.IsValid()) { if (target && !target->GetSectionLoadList().IsEmpty()) { lldb::addr_t header_load_addr = header_addr.GetLoadAddress(target); if (header_load_addr == LLDB_INVALID_ADDRESS) { header_addr.Dump(&strm, target, Address::DumpStyleModuleWithFileAddress, Address::DumpStyleFileAddress); } else { if (format_char == 'o') { // Show the offset of slide for the image strm.Printf( "0x%*.*" PRIx64, addr_nibble_width, addr_nibble_width, header_load_addr - header_addr.GetFileAddress()); } else { // Show the load address of the image strm.Printf("0x%*.*" PRIx64, addr_nibble_width, addr_nibble_width, header_load_addr); } } break; } // The address was valid, but the image isn't loaded, output the // address in an appropriate format header_addr.Dump(&strm, target, Address::DumpStyleFileAddress); break; } } strm.Printf("%*s", addr_nibble_width + 2, ""); } break; case 'r': { size_t ref_count = 0; ModuleSP module_sp(module->shared_from_this()); if (module_sp) { // Take one away to make sure we don't count our local "module_sp" ref_count = module_sp.use_count() - 1; } if (width) strm.Printf("{%*" PRIu64 "}", width, (uint64_t)ref_count); else strm.Printf("{%" PRIu64 "}", (uint64_t)ref_count); } break; case 's': case 'S': { const SymbolVendor *symbol_vendor = module->GetSymbolVendor(); if (symbol_vendor) { const FileSpec symfile_spec = symbol_vendor->GetMainFileSpec(); if (format_char == 'S') { // Dump symbol file only if different from module file if (!symfile_spec || symfile_spec == module->GetFileSpec()) { print_space = false; break; } // Add a newline and indent past the index strm.Printf("\n%*s", indent, ""); } DumpFullpath(strm, &symfile_spec, width); dump_object_name = true; break; } strm.Printf("%.*s", width, ""); } break; case 'm': strm.Format("{0:%c}", llvm::fmt_align(module->GetModificationTime(), llvm::AlignStyle::Left, width)); break; case 'p': strm.Printf("%p", static_cast(module)); break; case 'u': DumpModuleUUID(strm, module); break; default: break; } } if (dump_object_name) { const char *object_name = module->GetObjectName().GetCString(); if (object_name) strm.Printf("(%s)", object_name); } strm.EOL(); } CommandOptions m_options; }; #pragma mark CommandObjectTargetModulesShowUnwind //---------------------------------------------------------------------- // Lookup unwind information in images //---------------------------------------------------------------------- static OptionDefinition g_target_modules_show_unwind_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFunctionName, "Show unwind instructions for a function or symbol name." }, { LLDB_OPT_SET_2, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Show unwind instructions for a function or symbol containing an address" } // clang-format on }; class CommandObjectTargetModulesShowUnwind : public CommandObjectParsed { public: enum { eLookupTypeInvalid = -1, eLookupTypeAddress = 0, eLookupTypeSymbol, eLookupTypeFunction, eLookupTypeFunctionOrSymbol, kNumLookupTypes }; class CommandOptions : public Options { public: CommandOptions() : Options(), m_type(eLookupTypeInvalid), m_str(), m_addr(LLDB_INVALID_ADDRESS) {} ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'a': { m_str = option_arg; m_type = eLookupTypeAddress; m_addr = OptionArgParser::ToAddress(execution_context, option_arg, LLDB_INVALID_ADDRESS, &error); if (m_addr == LLDB_INVALID_ADDRESS) error.SetErrorStringWithFormat("invalid address string '%s'", option_arg.str().c_str()); break; } case 'n': m_str = option_arg; m_type = eLookupTypeFunctionOrSymbol; break; default: error.SetErrorStringWithFormat("unrecognized option %c.", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_type = eLookupTypeInvalid; m_str.clear(); m_addr = LLDB_INVALID_ADDRESS; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_target_modules_show_unwind_options); } // Instance variables to hold the values for command options. int m_type; // Should be a eLookupTypeXXX enum after parsing options std::string m_str; // Holds name lookup lldb::addr_t m_addr; // Holds the address to lookup }; CommandObjectTargetModulesShowUnwind(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "target modules show-unwind", "Show synthesized unwind instructions for a function.", nullptr, eCommandRequiresTarget | eCommandRequiresProcess | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() {} ~CommandObjectTargetModulesShowUnwind() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = m_exe_ctx.GetTargetPtr(); Process *process = m_exe_ctx.GetProcessPtr(); ABI *abi = nullptr; if (process) abi = process->GetABI().get(); if (process == nullptr) { result.AppendError( "You must have a process running to use this command."); result.SetStatus(eReturnStatusFailed); return false; } ThreadList threads(process->GetThreadList()); if (threads.GetSize() == 0) { result.AppendError("The process must be paused to use this command."); result.SetStatus(eReturnStatusFailed); return false; } ThreadSP thread(threads.GetThreadAtIndex(0)); if (!thread) { result.AppendError("The process must be paused to use this command."); result.SetStatus(eReturnStatusFailed); return false; } SymbolContextList sc_list; if (m_options.m_type == eLookupTypeFunctionOrSymbol) { ConstString function_name(m_options.m_str.c_str()); target->GetImages().FindFunctions(function_name, eFunctionNameTypeAuto, true, false, true, sc_list); } else if (m_options.m_type == eLookupTypeAddress && target) { Address addr; if (target->GetSectionLoadList().ResolveLoadAddress(m_options.m_addr, addr)) { SymbolContext sc; ModuleSP module_sp(addr.GetModule()); module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc); if (sc.function || sc.symbol) { sc_list.Append(sc); } } } else { result.AppendError( "address-expression or function name option must be specified."); result.SetStatus(eReturnStatusFailed); return false; } size_t num_matches = sc_list.GetSize(); if (num_matches == 0) { result.AppendErrorWithFormat("no unwind data found that matches '%s'.", m_options.m_str.c_str()); result.SetStatus(eReturnStatusFailed); return false; } for (uint32_t idx = 0; idx < num_matches; idx++) { SymbolContext sc; sc_list.GetContextAtIndex(idx, sc); if (sc.symbol == nullptr && sc.function == nullptr) continue; if (!sc.module_sp || sc.module_sp->GetObjectFile() == nullptr) continue; AddressRange range; if (!sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0, false, range)) continue; if (!range.GetBaseAddress().IsValid()) continue; ConstString funcname(sc.GetFunctionName()); if (funcname.IsEmpty()) continue; addr_t start_addr = range.GetBaseAddress().GetLoadAddress(target); if (abi) start_addr = abi->FixCodeAddress(start_addr); FuncUnwindersSP func_unwinders_sp( sc.module_sp->GetObjectFile() ->GetUnwindTable() .GetUncachedFuncUnwindersContainingAddress(start_addr, sc)); if (!func_unwinders_sp) continue; result.GetOutputStream().Printf( "UNWIND PLANS for %s`%s (start addr 0x%" PRIx64 ")\n\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); UnwindPlanSP non_callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtNonCallSite(*target, *thread, -1); if (non_callsite_unwind_plan) { result.GetOutputStream().Printf( "Asynchronous (not restricted to call-sites) UnwindPlan is '%s'\n", non_callsite_unwind_plan->GetSourceName().AsCString()); } UnwindPlanSP callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(*target, -1); if (callsite_unwind_plan) { result.GetOutputStream().Printf( "Synchronous (restricted to call-sites) UnwindPlan is '%s'\n", callsite_unwind_plan->GetSourceName().AsCString()); } UnwindPlanSP fast_unwind_plan = func_unwinders_sp->GetUnwindPlanFastUnwind(*target, *thread); if (fast_unwind_plan) { result.GetOutputStream().Printf( "Fast UnwindPlan is '%s'\n", fast_unwind_plan->GetSourceName().AsCString()); } result.GetOutputStream().Printf("\n"); UnwindPlanSP assembly_sp = func_unwinders_sp->GetAssemblyUnwindPlan(*target, *thread, 0); if (assembly_sp) { result.GetOutputStream().Printf( "Assembly language inspection UnwindPlan:\n"); assembly_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } UnwindPlanSP ehframe_sp = func_unwinders_sp->GetEHFrameUnwindPlan(*target, 0); if (ehframe_sp) { result.GetOutputStream().Printf("eh_frame UnwindPlan:\n"); ehframe_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } UnwindPlanSP ehframe_augmented_sp = func_unwinders_sp->GetEHFrameAugmentedUnwindPlan(*target, *thread, 0); if (ehframe_augmented_sp) { result.GetOutputStream().Printf("eh_frame augmented UnwindPlan:\n"); ehframe_augmented_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } if (UnwindPlanSP plan_sp = func_unwinders_sp->GetDebugFrameUnwindPlan(*target, 0)) { result.GetOutputStream().Printf("debug_frame UnwindPlan:\n"); plan_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } if (UnwindPlanSP plan_sp = func_unwinders_sp->GetDebugFrameAugmentedUnwindPlan(*target, *thread, 0)) { result.GetOutputStream().Printf("debug_frame augmented UnwindPlan:\n"); plan_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } UnwindPlanSP arm_unwind_sp = func_unwinders_sp->GetArmUnwindUnwindPlan(*target, 0); if (arm_unwind_sp) { result.GetOutputStream().Printf("ARM.exidx unwind UnwindPlan:\n"); arm_unwind_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } UnwindPlanSP compact_unwind_sp = func_unwinders_sp->GetCompactUnwindUnwindPlan(*target, 0); if (compact_unwind_sp) { result.GetOutputStream().Printf("Compact unwind UnwindPlan:\n"); compact_unwind_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } if (fast_unwind_plan) { result.GetOutputStream().Printf("Fast UnwindPlan:\n"); fast_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } ABISP abi_sp = process->GetABI(); if (abi_sp) { UnwindPlan arch_default(lldb::eRegisterKindGeneric); if (abi_sp->CreateDefaultUnwindPlan(arch_default)) { result.GetOutputStream().Printf("Arch default UnwindPlan:\n"); arch_default.Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } UnwindPlan arch_entry(lldb::eRegisterKindGeneric); if (abi_sp->CreateFunctionEntryUnwindPlan(arch_entry)) { result.GetOutputStream().Printf( "Arch default at entry point UnwindPlan:\n"); arch_entry.Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); result.GetOutputStream().Printf("\n"); } } result.GetOutputStream().Printf("\n"); } return result.Succeeded(); } CommandOptions m_options; }; //---------------------------------------------------------------------- // Lookup information in images //---------------------------------------------------------------------- static OptionDefinition g_target_modules_lookup_options[] = { // clang-format off { LLDB_OPT_SET_1, true, "address", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Lookup an address in one or more target modules." }, { LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeOffset, "When looking up an address subtract from any addresses before doing the lookup." }, /* FIXME: re-enable regex for types when the LookupTypeInModule actually uses the regex option: | LLDB_OPT_SET_6 */ { LLDB_OPT_SET_2 | LLDB_OPT_SET_4 | LLDB_OPT_SET_5, false, "regex", 'r', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "The argument for name lookups are regular expressions." }, { LLDB_OPT_SET_2, true, "symbol", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeSymbol, "Lookup a symbol by name in the symbol tables in one or more target modules." }, { LLDB_OPT_SET_3, true, "file", 'f', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFilename, "Lookup a file by fullpath or basename in one or more target modules." }, { LLDB_OPT_SET_3, false, "line", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLineNum, "Lookup a line number in a file (must be used in conjunction with --file)." }, { LLDB_OPT_SET_FROM_TO(3,5), false, "no-inlines", 'i', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Ignore inline entries (must be used in conjunction with --file or --function)." }, { LLDB_OPT_SET_4, true, "function", 'F', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFunctionName, "Lookup a function by name in the debug symbols in one or more target modules." }, { LLDB_OPT_SET_5, true, "name", 'n', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFunctionOrSymbol, "Lookup a function or symbol by name in one or more target modules." }, { LLDB_OPT_SET_6, true, "type", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeName, "Lookup a type by name in the debug symbols in one or more target modules." }, { LLDB_OPT_SET_ALL, false, "verbose", 'v', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Enable verbose lookup information." }, { LLDB_OPT_SET_ALL, false, "all", 'A', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Print all matches, not just the best match, if a best match is available." }, // clang-format on }; class CommandObjectTargetModulesLookup : public CommandObjectParsed { public: enum { eLookupTypeInvalid = -1, eLookupTypeAddress = 0, eLookupTypeSymbol, eLookupTypeFileLine, // Line is optional eLookupTypeFunction, eLookupTypeFunctionOrSymbol, eLookupTypeType, kNumLookupTypes }; class CommandOptions : public Options { public: CommandOptions() : Options() { OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'a': { m_type = eLookupTypeAddress; m_addr = OptionArgParser::ToAddress(execution_context, option_arg, LLDB_INVALID_ADDRESS, &error); } break; case 'o': if (option_arg.getAsInteger(0, m_offset)) error.SetErrorStringWithFormat("invalid offset string '%s'", option_arg.str().c_str()); break; case 's': m_str = option_arg; m_type = eLookupTypeSymbol; break; case 'f': m_file.SetFile(option_arg, false, FileSpec::Style::native); m_type = eLookupTypeFileLine; break; case 'i': m_include_inlines = false; break; case 'l': if (option_arg.getAsInteger(0, m_line_number)) error.SetErrorStringWithFormat("invalid line number string '%s'", option_arg.str().c_str()); else if (m_line_number == 0) error.SetErrorString("zero is an invalid line number"); m_type = eLookupTypeFileLine; break; case 'F': m_str = option_arg; m_type = eLookupTypeFunction; break; case 'n': m_str = option_arg; m_type = eLookupTypeFunctionOrSymbol; break; case 't': m_str = option_arg; m_type = eLookupTypeType; break; case 'v': m_verbose = 1; break; case 'A': m_print_all = true; break; case 'r': m_use_regex = true; break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_type = eLookupTypeInvalid; m_str.clear(); m_file.Clear(); m_addr = LLDB_INVALID_ADDRESS; m_offset = 0; m_line_number = 0; m_use_regex = false; m_include_inlines = true; m_verbose = false; m_print_all = false; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_target_modules_lookup_options); } int m_type; // Should be a eLookupTypeXXX enum after parsing options std::string m_str; // Holds name lookup FileSpec m_file; // Files for file lookups lldb::addr_t m_addr; // Holds the address to lookup lldb::addr_t m_offset; // Subtract this offset from m_addr before doing lookups. uint32_t m_line_number; // Line number for file+line lookups bool m_use_regex; // Name lookups in m_str are regular expressions. bool m_include_inlines; // Check for inline entries when looking up by // file/line. bool m_verbose; // Enable verbose lookup info bool m_print_all; // Print all matches, even in cases where there's a best // match. }; CommandObjectTargetModulesLookup(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "target modules lookup", "Look up information within executable and " "dependent shared library images.", nullptr, eCommandRequiresTarget), m_options() { CommandArgumentEntry arg; CommandArgumentData file_arg; // Define the first (and only) variant of this arg. file_arg.arg_type = eArgTypeFilename; file_arg.arg_repetition = eArgRepeatStar; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(file_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectTargetModulesLookup() override = default; Options *GetOptions() override { return &m_options; } bool LookupHere(CommandInterpreter &interpreter, CommandReturnObject &result, bool &syntax_error) { switch (m_options.m_type) { case eLookupTypeAddress: case eLookupTypeFileLine: case eLookupTypeFunction: case eLookupTypeFunctionOrSymbol: case eLookupTypeSymbol: default: return false; case eLookupTypeType: break; } StackFrameSP frame = m_exe_ctx.GetFrameSP(); if (!frame) return false; const SymbolContext &sym_ctx(frame->GetSymbolContext(eSymbolContextModule)); if (!sym_ctx.module_sp) return false; switch (m_options.m_type) { default: return false; case eLookupTypeType: if (!m_options.m_str.empty()) { if (LookupTypeHere(m_interpreter, result.GetOutputStream(), sym_ctx, m_options.m_str.c_str(), m_options.m_use_regex)) { result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } break; } return true; } bool LookupInModule(CommandInterpreter &interpreter, Module *module, CommandReturnObject &result, bool &syntax_error) { switch (m_options.m_type) { case eLookupTypeAddress: if (m_options.m_addr != LLDB_INVALID_ADDRESS) { if (LookupAddressInModule( m_interpreter, result.GetOutputStream(), module, eSymbolContextEverything | (m_options.m_verbose ? static_cast(eSymbolContextVariable) : 0), m_options.m_addr, m_options.m_offset, m_options.m_verbose)) { result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } break; case eLookupTypeSymbol: if (!m_options.m_str.empty()) { if (LookupSymbolInModule(m_interpreter, result.GetOutputStream(), module, m_options.m_str.c_str(), m_options.m_use_regex, m_options.m_verbose)) { result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } break; case eLookupTypeFileLine: if (m_options.m_file) { if (LookupFileAndLineInModule( m_interpreter, result.GetOutputStream(), module, m_options.m_file, m_options.m_line_number, m_options.m_include_inlines, m_options.m_verbose)) { result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } break; case eLookupTypeFunctionOrSymbol: case eLookupTypeFunction: if (!m_options.m_str.empty()) { if (LookupFunctionInModule( m_interpreter, result.GetOutputStream(), module, m_options.m_str.c_str(), m_options.m_use_regex, m_options.m_include_inlines, m_options.m_type == eLookupTypeFunctionOrSymbol, // include symbols m_options.m_verbose)) { result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } break; case eLookupTypeType: if (!m_options.m_str.empty()) { if (LookupTypeInModule(m_interpreter, result.GetOutputStream(), module, m_options.m_str.c_str(), m_options.m_use_regex)) { result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } break; default: m_options.GenerateOptionUsage( result.GetErrorStream(), this, GetCommandInterpreter().GetDebugger().GetTerminalWidth()); syntax_error = true; break; } result.SetStatus(eReturnStatusFailed); return false; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == nullptr) { result.AppendError("invalid target, create a debug target using the " "'target create' command"); result.SetStatus(eReturnStatusFailed); return false; } else { bool syntax_error = false; uint32_t i; uint32_t num_successful_lookups = 0; uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); result.GetOutputStream().SetAddressByteSize(addr_byte_size); result.GetErrorStream().SetAddressByteSize(addr_byte_size); // Dump all sections for all modules images if (command.GetArgumentCount() == 0) { ModuleSP current_module; // Where it is possible to look in the current symbol context first, // try that. If this search was successful and --all was not passed, // don't print anything else. if (LookupHere(m_interpreter, result, syntax_error)) { result.GetOutputStream().EOL(); num_successful_lookups++; if (!m_options.m_print_all) { result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } } // Dump all sections for all other modules const ModuleList &target_modules = target->GetImages(); std::lock_guard guard(target_modules.GetMutex()); const size_t num_modules = target_modules.GetSize(); if (num_modules > 0) { for (i = 0; i < num_modules && !syntax_error; ++i) { Module *module_pointer = target_modules.GetModulePointerAtIndexUnlocked(i); if (module_pointer != current_module.get() && LookupInModule( m_interpreter, target_modules.GetModulePointerAtIndexUnlocked(i), result, syntax_error)) { result.GetOutputStream().EOL(); num_successful_lookups++; } } } else { result.AppendError("the target has no associated executable images"); result.SetStatus(eReturnStatusFailed); return false; } } else { // Dump specified images (by basename or fullpath) const char *arg_cstr; for (i = 0; (arg_cstr = command.GetArgumentAtIndex(i)) != nullptr && !syntax_error; ++i) { ModuleList module_list; const size_t num_matches = FindModulesByName(target, arg_cstr, module_list, false); if (num_matches > 0) { for (size_t j = 0; j < num_matches; ++j) { Module *module = module_list.GetModulePointerAtIndex(j); if (module) { if (LookupInModule(m_interpreter, module, result, syntax_error)) { result.GetOutputStream().EOL(); num_successful_lookups++; } } } } else result.AppendWarningWithFormat( "Unable to find an image that matches '%s'.\n", arg_cstr); } } if (num_successful_lookups > 0) result.SetStatus(eReturnStatusSuccessFinishResult); else result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } CommandOptions m_options; }; #pragma mark CommandObjectMultiwordImageSearchPaths //------------------------------------------------------------------------- // CommandObjectMultiwordImageSearchPaths //------------------------------------------------------------------------- class CommandObjectTargetModulesImageSearchPaths : public CommandObjectMultiword { public: CommandObjectTargetModulesImageSearchPaths(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "target modules search-paths", "Commands for managing module search paths for a target.", "target modules search-paths []") { LoadSubCommand( "add", CommandObjectSP( new CommandObjectTargetModulesSearchPathsAdd(interpreter))); LoadSubCommand( "clear", CommandObjectSP(new CommandObjectTargetModulesSearchPathsClear( interpreter))); LoadSubCommand( "insert", CommandObjectSP( new CommandObjectTargetModulesSearchPathsInsert(interpreter))); LoadSubCommand( "list", CommandObjectSP(new CommandObjectTargetModulesSearchPathsList( interpreter))); LoadSubCommand( "query", CommandObjectSP(new CommandObjectTargetModulesSearchPathsQuery( interpreter))); } ~CommandObjectTargetModulesImageSearchPaths() override = default; }; #pragma mark CommandObjectTargetModules //------------------------------------------------------------------------- // CommandObjectTargetModules //------------------------------------------------------------------------- class CommandObjectTargetModules : public CommandObjectMultiword { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ CommandObjectTargetModules(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "target modules", "Commands for accessing information for one or " "more target modules.", "target modules ...") { LoadSubCommand( "add", CommandObjectSP(new CommandObjectTargetModulesAdd(interpreter))); LoadSubCommand("load", CommandObjectSP(new CommandObjectTargetModulesLoad( interpreter))); LoadSubCommand("dump", CommandObjectSP(new CommandObjectTargetModulesDump( interpreter))); LoadSubCommand("list", CommandObjectSP(new CommandObjectTargetModulesList( interpreter))); LoadSubCommand( "lookup", CommandObjectSP(new CommandObjectTargetModulesLookup(interpreter))); LoadSubCommand( "search-paths", CommandObjectSP( new CommandObjectTargetModulesImageSearchPaths(interpreter))); LoadSubCommand( "show-unwind", CommandObjectSP(new CommandObjectTargetModulesShowUnwind(interpreter))); } ~CommandObjectTargetModules() override = default; private: //------------------------------------------------------------------ // For CommandObjectTargetModules only //------------------------------------------------------------------ DISALLOW_COPY_AND_ASSIGN(CommandObjectTargetModules); }; class CommandObjectTargetSymbolsAdd : public CommandObjectParsed { public: CommandObjectTargetSymbolsAdd(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "target symbols add", "Add a debug symbol file to one of the target's current modules by " "specifying a path to a debug symbols file, or using the options " "to specify a module to download symbols for.", "target symbols add []", eCommandRequiresTarget), m_option_group(), m_file_option( LLDB_OPT_SET_1, false, "shlib", 's', CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Fullpath or basename for module to find debug symbols for."), m_current_frame_option( LLDB_OPT_SET_2, false, "frame", 'F', "Locate the debug symbols the currently selected frame.", false, true) { m_option_group.Append(&m_uuid_option_group, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_file_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_current_frame_option, LLDB_OPT_SET_2, LLDB_OPT_SET_2); m_option_group.Finalize(); } ~CommandObjectTargetSymbolsAdd() override = default; int HandleArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector) override { CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } Options *GetOptions() override { return &m_option_group; } protected: bool AddModuleSymbols(Target *target, ModuleSpec &module_spec, bool &flush, CommandReturnObject &result) { const FileSpec &symbol_fspec = module_spec.GetSymbolFileSpec(); if (symbol_fspec) { char symfile_path[PATH_MAX]; symbol_fspec.GetPath(symfile_path, sizeof(symfile_path)); if (!module_spec.GetUUID().IsValid()) { if (!module_spec.GetFileSpec() && !module_spec.GetPlatformFileSpec()) module_spec.GetFileSpec().GetFilename() = symbol_fspec.GetFilename(); } // We now have a module that represents a symbol file that can be used // for a module that might exist in the current target, so we need to // find that module in the target ModuleList matching_module_list; size_t num_matches = 0; // First extract all module specs from the symbol file lldb_private::ModuleSpecList symfile_module_specs; if (ObjectFile::GetModuleSpecifications(module_spec.GetSymbolFileSpec(), 0, 0, symfile_module_specs)) { // Now extract the module spec that matches the target architecture ModuleSpec target_arch_module_spec; ModuleSpec symfile_module_spec; target_arch_module_spec.GetArchitecture() = target->GetArchitecture(); if (symfile_module_specs.FindMatchingModuleSpec(target_arch_module_spec, symfile_module_spec)) { // See if it has a UUID? if (symfile_module_spec.GetUUID().IsValid()) { // It has a UUID, look for this UUID in the target modules ModuleSpec symfile_uuid_module_spec; symfile_uuid_module_spec.GetUUID() = symfile_module_spec.GetUUID(); num_matches = target->GetImages().FindModules( symfile_uuid_module_spec, matching_module_list); } } if (num_matches == 0) { // No matches yet, iterate through the module specs to find a UUID // value that we can match up to an image in our target const size_t num_symfile_module_specs = symfile_module_specs.GetSize(); for (size_t i = 0; i < num_symfile_module_specs && num_matches == 0; ++i) { if (symfile_module_specs.GetModuleSpecAtIndex( i, symfile_module_spec)) { if (symfile_module_spec.GetUUID().IsValid()) { // It has a UUID, look for this UUID in the target modules ModuleSpec symfile_uuid_module_spec; symfile_uuid_module_spec.GetUUID() = symfile_module_spec.GetUUID(); num_matches = target->GetImages().FindModules( symfile_uuid_module_spec, matching_module_list); } } } } } // Just try to match up the file by basename if we have no matches at // this point if (num_matches == 0) num_matches = target->GetImages().FindModules(module_spec, matching_module_list); while (num_matches == 0) { ConstString filename_no_extension( module_spec.GetFileSpec().GetFileNameStrippingExtension()); // Empty string returned, lets bail if (!filename_no_extension) break; // Check if there was no extension to strip and the basename is the // same if (filename_no_extension == module_spec.GetFileSpec().GetFilename()) break; // Replace basename with one less extension module_spec.GetFileSpec().GetFilename() = filename_no_extension; num_matches = target->GetImages().FindModules(module_spec, matching_module_list); } if (num_matches > 1) { result.AppendErrorWithFormat("multiple modules match symbol file '%s', " "use the --uuid option to resolve the " "ambiguity.\n", symfile_path); } else if (num_matches == 1) { ModuleSP module_sp(matching_module_list.GetModuleAtIndex(0)); // The module has not yet created its symbol vendor, we can just give // the existing target module the symfile path to use for when it // decides to create it! module_sp->SetSymbolFileFileSpec(symbol_fspec); SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(true, &result.GetErrorStream()); if (symbol_vendor) { SymbolFile *symbol_file = symbol_vendor->GetSymbolFile(); if (symbol_file) { ObjectFile *object_file = symbol_file->GetObjectFile(); if (object_file && object_file->GetFileSpec() == symbol_fspec) { // Provide feedback that the symfile has been successfully added. const FileSpec &module_fs = module_sp->GetFileSpec(); result.AppendMessageWithFormat( "symbol file '%s' has been added to '%s'\n", symfile_path, module_fs.GetPath().c_str()); // Let clients know something changed in the module if it is // currently loaded ModuleList module_list; module_list.Append(module_sp); target->SymbolsDidLoad(module_list); // Make sure we load any scripting resources that may be embedded // in the debug info files in case the platform supports that. Status error; StreamString feedback_stream; module_sp->LoadScriptingResourceInTarget(target, error, &feedback_stream); if (error.Fail() && error.AsCString()) result.AppendWarningWithFormat( "unable to load scripting data for module %s - error " "reported was %s", module_sp->GetFileSpec() .GetFileNameStrippingExtension() .GetCString(), error.AsCString()); else if (feedback_stream.GetSize()) result.AppendWarningWithFormat("%s", feedback_stream.GetData()); flush = true; result.SetStatus(eReturnStatusSuccessFinishResult); return true; } } } // Clear the symbol file spec if anything went wrong module_sp->SetSymbolFileFileSpec(FileSpec()); } namespace fs = llvm::sys::fs; if (module_spec.GetUUID().IsValid()) { StreamString ss_symfile_uuid; module_spec.GetUUID().Dump(&ss_symfile_uuid); result.AppendErrorWithFormat( "symbol file '%s' (%s) does not match any existing module%s\n", symfile_path, ss_symfile_uuid.GetData(), !fs::is_regular_file(symbol_fspec.GetPath()) ? "\n please specify the full path to the symbol file" : ""); } else { result.AppendErrorWithFormat( "symbol file '%s' does not match any existing module%s\n", symfile_path, !fs::is_regular_file(symbol_fspec.GetPath()) ? "\n please specify the full path to the symbol file" : ""); } } else { result.AppendError( "one or more executable image paths must be specified"); } result.SetStatus(eReturnStatusFailed); return false; } bool DoExecute(Args &args, CommandReturnObject &result) override { Target *target = m_exe_ctx.GetTargetPtr(); result.SetStatus(eReturnStatusFailed); bool flush = false; ModuleSpec module_spec; const bool uuid_option_set = m_uuid_option_group.GetOptionValue().OptionWasSet(); const bool file_option_set = m_file_option.GetOptionValue().OptionWasSet(); const bool frame_option_set = m_current_frame_option.GetOptionValue().OptionWasSet(); const size_t argc = args.GetArgumentCount(); if (argc == 0) { if (uuid_option_set || file_option_set || frame_option_set) { bool success = false; bool error_set = false; if (frame_option_set) { Process *process = m_exe_ctx.GetProcessPtr(); if (process) { const StateType process_state = process->GetState(); if (StateIsStoppedState(process_state, true)) { StackFrame *frame = m_exe_ctx.GetFramePtr(); if (frame) { ModuleSP frame_module_sp( frame->GetSymbolContext(eSymbolContextModule).module_sp); if (frame_module_sp) { if (frame_module_sp->GetPlatformFileSpec().Exists()) { module_spec.GetArchitecture() = frame_module_sp->GetArchitecture(); module_spec.GetFileSpec() = frame_module_sp->GetPlatformFileSpec(); } module_spec.GetUUID() = frame_module_sp->GetUUID(); success = module_spec.GetUUID().IsValid() || module_spec.GetFileSpec(); } else { result.AppendError("frame has no module"); error_set = true; } } else { result.AppendError("invalid current frame"); error_set = true; } } else { result.AppendErrorWithFormat("process is not stopped: %s", StateAsCString(process_state)); error_set = true; } } else { result.AppendError( "a process must exist in order to use the --frame option"); error_set = true; } } else { if (uuid_option_set) { module_spec.GetUUID() = m_uuid_option_group.GetOptionValue().GetCurrentValue(); success |= module_spec.GetUUID().IsValid(); } else if (file_option_set) { module_spec.GetFileSpec() = m_file_option.GetOptionValue().GetCurrentValue(); ModuleSP module_sp( target->GetImages().FindFirstModule(module_spec)); if (module_sp) { module_spec.GetFileSpec() = module_sp->GetFileSpec(); module_spec.GetPlatformFileSpec() = module_sp->GetPlatformFileSpec(); module_spec.GetUUID() = module_sp->GetUUID(); module_spec.GetArchitecture() = module_sp->GetArchitecture(); } else { module_spec.GetArchitecture() = target->GetArchitecture(); } success |= module_spec.GetUUID().IsValid() || module_spec.GetFileSpec().Exists(); } } if (success) { if (Symbols::DownloadObjectAndSymbolFile(module_spec)) { if (module_spec.GetSymbolFileSpec()) success = AddModuleSymbols(target, module_spec, flush, result); } } if (!success && !error_set) { StreamString error_strm; if (uuid_option_set) { error_strm.PutCString("unable to find debug symbols for UUID "); module_spec.GetUUID().Dump(&error_strm); } else if (file_option_set) { error_strm.PutCString( "unable to find debug symbols for the executable file "); error_strm << module_spec.GetFileSpec(); } else if (frame_option_set) { error_strm.PutCString( "unable to find debug symbols for the current frame"); } result.AppendError(error_strm.GetString()); } } else { result.AppendError("one or more symbol file paths must be specified, " "or options must be specified"); } } else { if (uuid_option_set) { result.AppendError("specify either one or more paths to symbol files " "or use the --uuid option without arguments"); } else if (frame_option_set) { result.AppendError("specify either one or more paths to symbol files " "or use the --frame option without arguments"); } else if (file_option_set && argc > 1) { result.AppendError("specify at most one symbol file path when " "--shlib option is set"); } else { PlatformSP platform_sp(target->GetPlatform()); for (auto &entry : args.entries()) { if (!entry.ref.empty()) { module_spec.GetSymbolFileSpec().SetFile(entry.ref, true, FileSpec::Style::native); if (file_option_set) { module_spec.GetFileSpec() = m_file_option.GetOptionValue().GetCurrentValue(); } if (platform_sp) { FileSpec symfile_spec; if (platform_sp ->ResolveSymbolFile(*target, module_spec, symfile_spec) .Success()) module_spec.GetSymbolFileSpec() = symfile_spec; } ArchSpec arch; bool symfile_exists = module_spec.GetSymbolFileSpec().Exists(); if (symfile_exists) { if (!AddModuleSymbols(target, module_spec, flush, result)) break; } else { std::string resolved_symfile_path = module_spec.GetSymbolFileSpec().GetPath(); if (resolved_symfile_path != entry.ref) { result.AppendErrorWithFormat( "invalid module path '%s' with resolved path '%s'\n", entry.c_str(), resolved_symfile_path.c_str()); break; } result.AppendErrorWithFormat("invalid module path '%s'\n", entry.c_str()); break; } } } } } if (flush) { Process *process = m_exe_ctx.GetProcessPtr(); if (process) process->Flush(); } return result.Succeeded(); } OptionGroupOptions m_option_group; OptionGroupUUID m_uuid_option_group; OptionGroupFile m_file_option; OptionGroupBoolean m_current_frame_option; }; #pragma mark CommandObjectTargetSymbols //------------------------------------------------------------------------- // CommandObjectTargetSymbols //------------------------------------------------------------------------- class CommandObjectTargetSymbols : public CommandObjectMultiword { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ CommandObjectTargetSymbols(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "target symbols", "Commands for adding and managing debug symbol files.", "target symbols ...") { LoadSubCommand( "add", CommandObjectSP(new CommandObjectTargetSymbolsAdd(interpreter))); } ~CommandObjectTargetSymbols() override = default; private: //------------------------------------------------------------------ // For CommandObjectTargetModules only //------------------------------------------------------------------ DISALLOW_COPY_AND_ASSIGN(CommandObjectTargetSymbols); }; #pragma mark CommandObjectTargetStopHookAdd //------------------------------------------------------------------------- // CommandObjectTargetStopHookAdd //------------------------------------------------------------------------- static OptionDefinition g_target_stop_hook_add_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "one-liner", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeOneLiner, "Specify a one-line breakpoint command inline. Be sure to surround it with quotes." }, { LLDB_OPT_SET_ALL, false, "shlib", 's', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Set the module within which the stop-hook is to be run." }, { LLDB_OPT_SET_ALL, false, "thread-index", 'x', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadIndex, "The stop hook is run only for the thread whose index matches this argument." }, { LLDB_OPT_SET_ALL, false, "thread-id", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadID, "The stop hook is run only for the thread whose TID matches this argument." }, { LLDB_OPT_SET_ALL, false, "thread-name", 'T', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadName, "The stop hook is run only for the thread whose thread name matches this argument." }, { LLDB_OPT_SET_ALL, false, "queue-name", 'q', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeQueueName, "The stop hook is run only for threads in the queue whose name is given by this argument." }, { LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "Specify the source file within which the stop-hook is to be run." }, { LLDB_OPT_SET_1, false, "start-line", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLineNum, "Set the start of the line range for which the stop-hook is to be run." }, { LLDB_OPT_SET_1, false, "end-line", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLineNum, "Set the end of the line range for which the stop-hook is to be run." }, { LLDB_OPT_SET_2, false, "classname", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeClassName, "Specify the class within which the stop-hook is to be run." }, { LLDB_OPT_SET_3, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, "Set the function name within which the stop hook will be run." }, // clang-format on }; class CommandObjectTargetStopHookAdd : public CommandObjectParsed, public IOHandlerDelegateMultiline { public: class CommandOptions : public Options { public: CommandOptions() : Options(), m_line_start(0), m_line_end(UINT_MAX), m_func_name_type_mask(eFunctionNameTypeAuto), m_sym_ctx_specified(false), m_thread_specified(false), m_use_one_liner(false), m_one_liner() {} ~CommandOptions() override = default; llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_target_stop_hook_add_options); } Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'c': m_class_name = option_arg; m_sym_ctx_specified = true; break; case 'e': if (option_arg.getAsInteger(0, m_line_end)) { error.SetErrorStringWithFormat("invalid end line number: \"%s\"", option_arg.str().c_str()); break; } m_sym_ctx_specified = true; break; case 'l': if (option_arg.getAsInteger(0, m_line_start)) { error.SetErrorStringWithFormat("invalid start line number: \"%s\"", option_arg.str().c_str()); break; } m_sym_ctx_specified = true; break; case 'i': m_no_inlines = true; break; case 'n': m_function_name = option_arg; m_func_name_type_mask |= eFunctionNameTypeAuto; m_sym_ctx_specified = true; break; case 'f': m_file_name = option_arg; m_sym_ctx_specified = true; break; case 's': m_module_name = option_arg; m_sym_ctx_specified = true; break; case 't': if (option_arg.getAsInteger(0, m_thread_id)) error.SetErrorStringWithFormat("invalid thread id string '%s'", option_arg.str().c_str()); m_thread_specified = true; break; case 'T': m_thread_name = option_arg; m_thread_specified = true; break; case 'q': m_queue_name = option_arg; m_thread_specified = true; break; case 'x': if (option_arg.getAsInteger(0, m_thread_index)) error.SetErrorStringWithFormat("invalid thread index string '%s'", option_arg.str().c_str()); m_thread_specified = true; break; case 'o': m_use_one_liner = true; m_one_liner = option_arg; break; default: error.SetErrorStringWithFormat("unrecognized option %c.", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_class_name.clear(); m_function_name.clear(); m_line_start = 0; m_line_end = UINT_MAX; m_file_name.clear(); m_module_name.clear(); m_func_name_type_mask = eFunctionNameTypeAuto; m_thread_id = LLDB_INVALID_THREAD_ID; m_thread_index = UINT32_MAX; m_thread_name.clear(); m_queue_name.clear(); m_no_inlines = false; m_sym_ctx_specified = false; m_thread_specified = false; m_use_one_liner = false; m_one_liner.clear(); } std::string m_class_name; std::string m_function_name; uint32_t m_line_start; uint32_t m_line_end; std::string m_file_name; std::string m_module_name; uint32_t m_func_name_type_mask; // A pick from lldb::FunctionNameType. lldb::tid_t m_thread_id; uint32_t m_thread_index; std::string m_thread_name; std::string m_queue_name; bool m_sym_ctx_specified; bool m_no_inlines; bool m_thread_specified; // Instance variables to hold the values for one_liner options. bool m_use_one_liner; std::string m_one_liner; }; CommandObjectTargetStopHookAdd(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "target stop-hook add", "Add a hook to be executed when the target stops.", "target stop-hook add"), IOHandlerDelegateMultiline("DONE", IOHandlerDelegate::Completion::LLDBCommand), m_options() {} ~CommandObjectTargetStopHookAdd() override = default; Options *GetOptions() override { return &m_options; } protected: void IOHandlerActivated(IOHandler &io_handler) override { StreamFileSP output_sp(io_handler.GetOutputStreamFile()); if (output_sp) { output_sp->PutCString( "Enter your stop hook command(s). Type 'DONE' to end.\n"); output_sp->Flush(); } } void IOHandlerInputComplete(IOHandler &io_handler, std::string &line) override { if (m_stop_hook_sp) { if (line.empty()) { StreamFileSP error_sp(io_handler.GetErrorStreamFile()); if (error_sp) { error_sp->Printf("error: stop hook #%" PRIu64 " aborted, no commands.\n", m_stop_hook_sp->GetID()); error_sp->Flush(); } Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) target->RemoveStopHookByID(m_stop_hook_sp->GetID()); } else { m_stop_hook_sp->GetCommandPointer()->SplitIntoLines(line); StreamFileSP output_sp(io_handler.GetOutputStreamFile()); if (output_sp) { output_sp->Printf("Stop hook #%" PRIu64 " added.\n", m_stop_hook_sp->GetID()); output_sp->Flush(); } } m_stop_hook_sp.reset(); } io_handler.SetIsDone(true); } bool DoExecute(Args &command, CommandReturnObject &result) override { m_stop_hook_sp.reset(); Target *target = GetSelectedOrDummyTarget(); if (target) { Target::StopHookSP new_hook_sp = target->CreateStopHook(); // First step, make the specifier. std::unique_ptr specifier_ap; if (m_options.m_sym_ctx_specified) { specifier_ap.reset(new SymbolContextSpecifier( m_interpreter.GetDebugger().GetSelectedTarget())); if (!m_options.m_module_name.empty()) { specifier_ap->AddSpecification( m_options.m_module_name.c_str(), SymbolContextSpecifier::eModuleSpecified); } if (!m_options.m_class_name.empty()) { specifier_ap->AddSpecification( m_options.m_class_name.c_str(), SymbolContextSpecifier::eClassOrNamespaceSpecified); } if (!m_options.m_file_name.empty()) { specifier_ap->AddSpecification( m_options.m_file_name.c_str(), SymbolContextSpecifier::eFileSpecified); } if (m_options.m_line_start != 0) { specifier_ap->AddLineSpecification( m_options.m_line_start, SymbolContextSpecifier::eLineStartSpecified); } if (m_options.m_line_end != UINT_MAX) { specifier_ap->AddLineSpecification( m_options.m_line_end, SymbolContextSpecifier::eLineEndSpecified); } if (!m_options.m_function_name.empty()) { specifier_ap->AddSpecification( m_options.m_function_name.c_str(), SymbolContextSpecifier::eFunctionSpecified); } } if (specifier_ap) new_hook_sp->SetSpecifier(specifier_ap.release()); // Next see if any of the thread options have been entered: if (m_options.m_thread_specified) { ThreadSpec *thread_spec = new ThreadSpec(); if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID) { thread_spec->SetTID(m_options.m_thread_id); } if (m_options.m_thread_index != UINT32_MAX) thread_spec->SetIndex(m_options.m_thread_index); if (!m_options.m_thread_name.empty()) thread_spec->SetName(m_options.m_thread_name.c_str()); if (!m_options.m_queue_name.empty()) thread_spec->SetQueueName(m_options.m_queue_name.c_str()); new_hook_sp->SetThreadSpecifier(thread_spec); } if (m_options.m_use_one_liner) { // Use one-liner. new_hook_sp->GetCommandPointer()->AppendString( m_options.m_one_liner.c_str()); result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n", new_hook_sp->GetID()); } else { m_stop_hook_sp = new_hook_sp; m_interpreter.GetLLDBCommandsFromIOHandler( "> ", // Prompt *this, // IOHandlerDelegate true, // Run IOHandler in async mode nullptr); // Baton for the "io_handler" that will be passed back // into our IOHandlerDelegate functions } result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendError("invalid target\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } private: CommandOptions m_options; Target::StopHookSP m_stop_hook_sp; }; #pragma mark CommandObjectTargetStopHookDelete //------------------------------------------------------------------------- // CommandObjectTargetStopHookDelete //------------------------------------------------------------------------- class CommandObjectTargetStopHookDelete : public CommandObjectParsed { public: CommandObjectTargetStopHookDelete(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "target stop-hook delete", "Delete a stop-hook.", "target stop-hook delete []") {} ~CommandObjectTargetStopHookDelete() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = GetSelectedOrDummyTarget(); if (target) { // FIXME: see if we can use the breakpoint id style parser? size_t num_args = command.GetArgumentCount(); if (num_args == 0) { if (!m_interpreter.Confirm("Delete all stop hooks?", true)) { result.SetStatus(eReturnStatusFailed); return false; } else { target->RemoveAllStopHooks(); } } else { bool success; for (size_t i = 0; i < num_args; i++) { lldb::user_id_t user_id = StringConvert::ToUInt32( command.GetArgumentAtIndex(i), 0, 0, &success); if (!success) { result.AppendErrorWithFormat("invalid stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } success = target->RemoveStopHookByID(user_id); if (!success) { result.AppendErrorWithFormat("unknown stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } } } result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendError("invalid target\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; #pragma mark CommandObjectTargetStopHookEnableDisable //------------------------------------------------------------------------- // CommandObjectTargetStopHookEnableDisable //------------------------------------------------------------------------- class CommandObjectTargetStopHookEnableDisable : public CommandObjectParsed { public: CommandObjectTargetStopHookEnableDisable(CommandInterpreter &interpreter, bool enable, const char *name, const char *help, const char *syntax) : CommandObjectParsed(interpreter, name, help, syntax), m_enable(enable) { } ~CommandObjectTargetStopHookEnableDisable() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = GetSelectedOrDummyTarget(); if (target) { // FIXME: see if we can use the breakpoint id style parser? size_t num_args = command.GetArgumentCount(); bool success; if (num_args == 0) { target->SetAllStopHooksActiveState(m_enable); } else { for (size_t i = 0; i < num_args; i++) { lldb::user_id_t user_id = StringConvert::ToUInt32( command.GetArgumentAtIndex(i), 0, 0, &success); if (!success) { result.AppendErrorWithFormat("invalid stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } success = target->SetStopHookActiveStateByID(user_id, m_enable); if (!success) { result.AppendErrorWithFormat("unknown stop hook id: \"%s\".\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } } } result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendError("invalid target\n"); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } private: bool m_enable; }; #pragma mark CommandObjectTargetStopHookList //------------------------------------------------------------------------- // CommandObjectTargetStopHookList //------------------------------------------------------------------------- class CommandObjectTargetStopHookList : public CommandObjectParsed { public: CommandObjectTargetStopHookList(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "target stop-hook list", "List all stop-hooks.", "target stop-hook list []") {} ~CommandObjectTargetStopHookList() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target *target = GetSelectedOrDummyTarget(); if (!target) { result.AppendError("invalid target\n"); result.SetStatus(eReturnStatusFailed); return result.Succeeded(); } size_t num_hooks = target->GetNumStopHooks(); if (num_hooks == 0) { result.GetOutputStream().PutCString("No stop hooks.\n"); } else { for (size_t i = 0; i < num_hooks; i++) { Target::StopHookSP this_hook = target->GetStopHookAtIndex(i); if (i > 0) result.GetOutputStream().PutCString("\n"); this_hook->GetDescription(&(result.GetOutputStream()), eDescriptionLevelFull); } } result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } }; #pragma mark CommandObjectMultiwordTargetStopHooks //------------------------------------------------------------------------- // CommandObjectMultiwordTargetStopHooks //------------------------------------------------------------------------- class CommandObjectMultiwordTargetStopHooks : public CommandObjectMultiword { public: CommandObjectMultiwordTargetStopHooks(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "target stop-hook", "Commands for operating on debugger target stop-hooks.", "target stop-hook []") { LoadSubCommand("add", CommandObjectSP( new CommandObjectTargetStopHookAdd(interpreter))); LoadSubCommand( "delete", CommandObjectSP(new CommandObjectTargetStopHookDelete(interpreter))); LoadSubCommand("disable", CommandObjectSP(new CommandObjectTargetStopHookEnableDisable( interpreter, false, "target stop-hook disable []", "Disable a stop-hook.", "target stop-hook disable"))); LoadSubCommand("enable", CommandObjectSP(new CommandObjectTargetStopHookEnableDisable( interpreter, true, "target stop-hook enable []", "Enable a stop-hook.", "target stop-hook enable"))); LoadSubCommand("list", CommandObjectSP(new CommandObjectTargetStopHookList( interpreter))); } ~CommandObjectMultiwordTargetStopHooks() override = default; }; #pragma mark CommandObjectMultiwordTarget //------------------------------------------------------------------------- // CommandObjectMultiwordTarget //------------------------------------------------------------------------- CommandObjectMultiwordTarget::CommandObjectMultiwordTarget( CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "target", "Commands for operating on debugger targets.", "target []") { LoadSubCommand("create", CommandObjectSP(new CommandObjectTargetCreate(interpreter))); LoadSubCommand("delete", CommandObjectSP(new CommandObjectTargetDelete(interpreter))); LoadSubCommand("list", CommandObjectSP(new CommandObjectTargetList(interpreter))); LoadSubCommand("select", CommandObjectSP(new CommandObjectTargetSelect(interpreter))); LoadSubCommand( "stop-hook", CommandObjectSP(new CommandObjectMultiwordTargetStopHooks(interpreter))); LoadSubCommand("modules", CommandObjectSP(new CommandObjectTargetModules(interpreter))); LoadSubCommand("symbols", CommandObjectSP(new CommandObjectTargetSymbols(interpreter))); LoadSubCommand("variable", CommandObjectSP(new CommandObjectTargetVariable(interpreter))); } CommandObjectMultiwordTarget::~CommandObjectMultiwordTarget() = default; Index: vendor/lldb/dist/source/Core/FormatEntity.cpp =================================================================== --- vendor/lldb/dist/source/Core/FormatEntity.cpp (revision 337146) +++ vendor/lldb/dist/source/Core/FormatEntity.cpp (revision 337147) @@ -1,2413 +1,2418 @@ //===-- FormatEntity.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Core/FormatEntity.h" #include "lldb/Core/Address.h" #include "lldb/Core/AddressRange.h" // for AddressRange #include "lldb/Core/Debugger.h" #include "lldb/Core/DumpRegisterValue.h" #include "lldb/Core/Module.h" #include "lldb/Core/RegisterValue.h" // for RegisterValue #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/DataFormatters/FormatClasses.h" // for TypeNameSpecifier... #include "lldb/DataFormatters/FormatManager.h" #include "lldb/DataFormatters/TypeSummary.h" // for TypeSummaryImpl::... #include "lldb/Expression/ExpressionVariable.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/CompilerType.h" // for CompilerType #include "lldb/Symbol/Function.h" #include "lldb/Symbol/LineEntry.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" // for SymbolContext #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/ExecutionContextScope.h" // for ExecutionContextS... #include "lldb/Target/Language.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/AnsiTerminal.h" #include "lldb/Utility/ArchSpec.h" // for ArchSpec #include "lldb/Utility/ConstString.h" // for ConstString, oper... #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" // for Log #include "lldb/Utility/Logging.h" // for GetLogIfAllCatego... #include "lldb/Utility/SharingPtr.h" // for SharingPtr #include "lldb/Utility/Stream.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StringList.h" // for StringList #include "lldb/Utility/StructuredData.h" // for StructuredData::O... #include "lldb/lldb-defines.h" // for LLDB_INVALID_ADDRESS #include "lldb/lldb-forward.h" // for ValueObjectSP #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" // for Triple, Triple::O... #include "llvm/Support/Compiler.h" // for LLVM_FALLTHROUGH #include // for isxdigit #include // for PRIu64, PRIx64 #include // for shared_ptr, opera... #include // for sprintf #include // for strtoul #include // for size_t, strchr #include // for move #include // for pair namespace lldb_private { class ScriptInterpreter; } namespace lldb_private { struct RegisterInfo; } using namespace lldb; using namespace lldb_private; enum FileKind { FileError = 0, Basename, Dirname, Fullpath }; #define ENTRY(n, t, f) \ { \ n, nullptr, FormatEntity::Entry::Type::t, \ FormatEntity::Entry::FormatType::f, 0, 0, nullptr, false \ } #define ENTRY_VALUE(n, t, f, v) \ { \ n, nullptr, FormatEntity::Entry::Type::t, \ FormatEntity::Entry::FormatType::f, v, 0, nullptr, false \ } #define ENTRY_CHILDREN(n, t, f, c) \ { \ n, nullptr, FormatEntity::Entry::Type::t, \ FormatEntity::Entry::FormatType::f, 0, \ static_cast(llvm::array_lengthof(c)), c, false \ } #define ENTRY_CHILDREN_KEEP_SEP(n, t, f, c) \ { \ n, nullptr, FormatEntity::Entry::Type::t, \ FormatEntity::Entry::FormatType::f, 0, \ static_cast(llvm::array_lengthof(c)), c, true \ } #define ENTRY_STRING(n, s) \ { \ n, s, FormatEntity::Entry::Type::InsertString, \ FormatEntity::Entry::FormatType::None, 0, 0, nullptr, false \ } static FormatEntity::Entry::Definition g_string_entry[] = { ENTRY("*", ParentString, None)}; static FormatEntity::Entry::Definition g_addr_entries[] = { ENTRY("load", AddressLoad, UInt64), ENTRY("file", AddressFile, UInt64), ENTRY("load", AddressLoadOrFile, UInt64), }; static FormatEntity::Entry::Definition g_file_child_entries[] = { ENTRY_VALUE("basename", ParentNumber, CString, FileKind::Basename), ENTRY_VALUE("dirname", ParentNumber, CString, FileKind::Dirname), ENTRY_VALUE("fullpath", ParentNumber, CString, FileKind::Fullpath)}; static FormatEntity::Entry::Definition g_frame_child_entries[] = { ENTRY("index", FrameIndex, UInt32), ENTRY("pc", FrameRegisterPC, UInt64), ENTRY("fp", FrameRegisterFP, UInt64), ENTRY("sp", FrameRegisterSP, UInt64), ENTRY("flags", FrameRegisterFlags, UInt64), ENTRY("no-debug", FrameNoDebug, None), ENTRY_CHILDREN("reg", FrameRegisterByName, UInt64, g_string_entry), }; static FormatEntity::Entry::Definition g_function_child_entries[] = { ENTRY("id", FunctionID, UInt64), ENTRY("name", FunctionName, CString), ENTRY("name-without-args", FunctionNameNoArgs, CString), ENTRY("name-with-args", FunctionNameWithArgs, CString), ENTRY("addr-offset", FunctionAddrOffset, UInt64), ENTRY("concrete-only-addr-offset-no-padding", FunctionAddrOffsetConcrete, UInt64), ENTRY("line-offset", FunctionLineOffset, UInt64), ENTRY("pc-offset", FunctionPCOffset, UInt64), ENTRY("initial-function", FunctionInitial, None), ENTRY("changed", FunctionChanged, None), ENTRY("is-optimized", FunctionIsOptimized, None)}; static FormatEntity::Entry::Definition g_line_child_entries[] = { ENTRY_CHILDREN("file", LineEntryFile, None, g_file_child_entries), ENTRY("number", LineEntryLineNumber, UInt32), ENTRY("start-addr", LineEntryStartAddress, UInt64), ENTRY("end-addr", LineEntryEndAddress, UInt64), }; static FormatEntity::Entry::Definition g_module_child_entries[] = { ENTRY_CHILDREN("file", ModuleFile, None, g_file_child_entries), }; static FormatEntity::Entry::Definition g_process_child_entries[] = { ENTRY("id", ProcessID, UInt64), ENTRY_VALUE("name", ProcessFile, CString, FileKind::Basename), ENTRY_CHILDREN("file", ProcessFile, None, g_file_child_entries), }; static FormatEntity::Entry::Definition g_svar_child_entries[] = { ENTRY("*", ParentString, None)}; static FormatEntity::Entry::Definition g_var_child_entries[] = { ENTRY("*", ParentString, None)}; static FormatEntity::Entry::Definition g_thread_child_entries[] = { ENTRY("id", ThreadID, UInt64), ENTRY("protocol_id", ThreadProtocolID, UInt64), ENTRY("index", ThreadIndexID, UInt32), ENTRY_CHILDREN("info", ThreadInfo, None, g_string_entry), ENTRY("queue", ThreadQueue, CString), ENTRY("name", ThreadName, CString), ENTRY("stop-reason", ThreadStopReason, CString), ENTRY("return-value", ThreadReturnValue, CString), ENTRY("completed-expression", ThreadCompletedExpression, CString), }; static FormatEntity::Entry::Definition g_target_child_entries[] = { ENTRY("arch", TargetArch, CString), }; #define _TO_STR2(_val) #_val #define _TO_STR(_val) _TO_STR2(_val) static FormatEntity::Entry::Definition g_ansi_fg_entries[] = { ENTRY_STRING("black", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLACK) ANSI_ESC_END), ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_RED) ANSI_ESC_END), ENTRY_STRING("green", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_GREEN) ANSI_ESC_END), ENTRY_STRING("yellow", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_YELLOW) ANSI_ESC_END), ENTRY_STRING("blue", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLUE) ANSI_ESC_END), ENTRY_STRING("purple", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_PURPLE) ANSI_ESC_END), ENTRY_STRING("cyan", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_CYAN) ANSI_ESC_END), ENTRY_STRING("white", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_WHITE) ANSI_ESC_END), }; static FormatEntity::Entry::Definition g_ansi_bg_entries[] = { ENTRY_STRING("black", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLACK) ANSI_ESC_END), ENTRY_STRING("red", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_RED) ANSI_ESC_END), ENTRY_STRING("green", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_GREEN) ANSI_ESC_END), ENTRY_STRING("yellow", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_YELLOW) ANSI_ESC_END), ENTRY_STRING("blue", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLUE) ANSI_ESC_END), ENTRY_STRING("purple", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_PURPLE) ANSI_ESC_END), ENTRY_STRING("cyan", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_CYAN) ANSI_ESC_END), ENTRY_STRING("white", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_WHITE) ANSI_ESC_END), }; static FormatEntity::Entry::Definition g_ansi_entries[] = { ENTRY_CHILDREN("fg", Invalid, None, g_ansi_fg_entries), ENTRY_CHILDREN("bg", Invalid, None, g_ansi_bg_entries), ENTRY_STRING("normal", ANSI_ESC_START _TO_STR(ANSI_CTRL_NORMAL) ANSI_ESC_END), ENTRY_STRING("bold", ANSI_ESC_START _TO_STR(ANSI_CTRL_BOLD) ANSI_ESC_END), ENTRY_STRING("faint", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAINT) ANSI_ESC_END), ENTRY_STRING("italic", ANSI_ESC_START _TO_STR(ANSI_CTRL_ITALIC) ANSI_ESC_END), ENTRY_STRING("underline", ANSI_ESC_START _TO_STR(ANSI_CTRL_UNDERLINE) ANSI_ESC_END), ENTRY_STRING("slow-blink", ANSI_ESC_START _TO_STR(ANSI_CTRL_SLOW_BLINK) ANSI_ESC_END), ENTRY_STRING("fast-blink", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAST_BLINK) ANSI_ESC_END), ENTRY_STRING("negative", ANSI_ESC_START _TO_STR(ANSI_CTRL_IMAGE_NEGATIVE) ANSI_ESC_END), ENTRY_STRING("conceal", ANSI_ESC_START _TO_STR(ANSI_CTRL_CONCEAL) ANSI_ESC_END), ENTRY_STRING("crossed-out", ANSI_ESC_START _TO_STR(ANSI_CTRL_CROSSED_OUT) ANSI_ESC_END), }; static FormatEntity::Entry::Definition g_script_child_entries[] = { ENTRY("frame", ScriptFrame, None), ENTRY("process", ScriptProcess, None), ENTRY("target", ScriptTarget, None), ENTRY("thread", ScriptThread, None), ENTRY("var", ScriptVariable, None), ENTRY("svar", ScriptVariableSynthetic, None), ENTRY("thread", ScriptThread, None), }; static FormatEntity::Entry::Definition g_top_level_entries[] = { ENTRY_CHILDREN("addr", AddressLoadOrFile, UInt64, g_addr_entries), ENTRY("addr-file-or-load", AddressLoadOrFile, UInt64), ENTRY_CHILDREN("ansi", Invalid, None, g_ansi_entries), ENTRY("current-pc-arrow", CurrentPCArrow, CString), ENTRY_CHILDREN("file", File, CString, g_file_child_entries), ENTRY("language", Lang, CString), ENTRY_CHILDREN("frame", Invalid, None, g_frame_child_entries), ENTRY_CHILDREN("function", Invalid, None, g_function_child_entries), ENTRY_CHILDREN("line", Invalid, None, g_line_child_entries), ENTRY_CHILDREN("module", Invalid, None, g_module_child_entries), ENTRY_CHILDREN("process", Invalid, None, g_process_child_entries), ENTRY_CHILDREN("script", Invalid, None, g_script_child_entries), ENTRY_CHILDREN_KEEP_SEP("svar", VariableSynthetic, None, g_svar_child_entries), ENTRY_CHILDREN("thread", Invalid, None, g_thread_child_entries), ENTRY_CHILDREN("target", Invalid, None, g_target_child_entries), ENTRY_CHILDREN_KEEP_SEP("var", Variable, None, g_var_child_entries), }; static FormatEntity::Entry::Definition g_root = ENTRY_CHILDREN("", Root, None, g_top_level_entries); FormatEntity::Entry::Entry(llvm::StringRef s) : string(s.data(), s.size()), printf_format(), children(), definition(nullptr), type(Type::String), fmt(lldb::eFormatDefault), number(0), deref(false) {} FormatEntity::Entry::Entry(char ch) : string(1, ch), printf_format(), children(), definition(nullptr), type(Type::String), fmt(lldb::eFormatDefault), number(0), deref(false) {} void FormatEntity::Entry::AppendChar(char ch) { if (children.empty() || children.back().type != Entry::Type::String) children.push_back(Entry(ch)); else children.back().string.append(1, ch); } void FormatEntity::Entry::AppendText(const llvm::StringRef &s) { if (children.empty() || children.back().type != Entry::Type::String) children.push_back(Entry(s)); else children.back().string.append(s.data(), s.size()); } void FormatEntity::Entry::AppendText(const char *cstr) { return AppendText(llvm::StringRef(cstr)); } Status FormatEntity::Parse(const llvm::StringRef &format_str, Entry &entry) { entry.Clear(); entry.type = Entry::Type::Root; llvm::StringRef modifiable_format(format_str); return ParseInternal(modifiable_format, entry, 0); } #define ENUM_TO_CSTR(eee) \ case FormatEntity::Entry::Type::eee: \ return #eee const char *FormatEntity::Entry::TypeToCString(Type t) { switch (t) { ENUM_TO_CSTR(Invalid); ENUM_TO_CSTR(ParentNumber); ENUM_TO_CSTR(ParentString); ENUM_TO_CSTR(InsertString); ENUM_TO_CSTR(Root); ENUM_TO_CSTR(String); ENUM_TO_CSTR(Scope); ENUM_TO_CSTR(Variable); ENUM_TO_CSTR(VariableSynthetic); ENUM_TO_CSTR(ScriptVariable); ENUM_TO_CSTR(ScriptVariableSynthetic); ENUM_TO_CSTR(AddressLoad); ENUM_TO_CSTR(AddressFile); ENUM_TO_CSTR(AddressLoadOrFile); ENUM_TO_CSTR(ProcessID); ENUM_TO_CSTR(ProcessFile); ENUM_TO_CSTR(ScriptProcess); ENUM_TO_CSTR(ThreadID); ENUM_TO_CSTR(ThreadProtocolID); ENUM_TO_CSTR(ThreadIndexID); ENUM_TO_CSTR(ThreadName); ENUM_TO_CSTR(ThreadQueue); ENUM_TO_CSTR(ThreadStopReason); ENUM_TO_CSTR(ThreadReturnValue); ENUM_TO_CSTR(ThreadCompletedExpression); ENUM_TO_CSTR(ScriptThread); ENUM_TO_CSTR(ThreadInfo); ENUM_TO_CSTR(TargetArch); ENUM_TO_CSTR(ScriptTarget); ENUM_TO_CSTR(ModuleFile); ENUM_TO_CSTR(File); ENUM_TO_CSTR(Lang); ENUM_TO_CSTR(FrameIndex); ENUM_TO_CSTR(FrameNoDebug); ENUM_TO_CSTR(FrameRegisterPC); ENUM_TO_CSTR(FrameRegisterSP); ENUM_TO_CSTR(FrameRegisterFP); ENUM_TO_CSTR(FrameRegisterFlags); ENUM_TO_CSTR(FrameRegisterByName); ENUM_TO_CSTR(ScriptFrame); ENUM_TO_CSTR(FunctionID); ENUM_TO_CSTR(FunctionDidChange); ENUM_TO_CSTR(FunctionInitialFunction); ENUM_TO_CSTR(FunctionName); ENUM_TO_CSTR(FunctionNameWithArgs); ENUM_TO_CSTR(FunctionNameNoArgs); ENUM_TO_CSTR(FunctionAddrOffset); ENUM_TO_CSTR(FunctionAddrOffsetConcrete); ENUM_TO_CSTR(FunctionLineOffset); ENUM_TO_CSTR(FunctionPCOffset); ENUM_TO_CSTR(FunctionInitial); ENUM_TO_CSTR(FunctionChanged); ENUM_TO_CSTR(FunctionIsOptimized); ENUM_TO_CSTR(LineEntryFile); ENUM_TO_CSTR(LineEntryLineNumber); ENUM_TO_CSTR(LineEntryStartAddress); ENUM_TO_CSTR(LineEntryEndAddress); ENUM_TO_CSTR(CurrentPCArrow); } return "???"; } #undef ENUM_TO_CSTR void FormatEntity::Entry::Dump(Stream &s, int depth) const { s.Printf("%*.*s%-20s: ", depth * 2, depth * 2, "", TypeToCString(type)); if (fmt != eFormatDefault) s.Printf("lldb-format = %s, ", FormatManager::GetFormatAsCString(fmt)); if (!string.empty()) s.Printf("string = \"%s\"", string.c_str()); if (!printf_format.empty()) s.Printf("printf_format = \"%s\"", printf_format.c_str()); if (number != 0) s.Printf("number = %" PRIu64 " (0x%" PRIx64 "), ", number, number); if (deref) s.Printf("deref = true, "); s.EOL(); for (const auto &child : children) { child.Dump(s, depth + 1); } } template static bool RunScriptFormatKeyword(Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, T t, const char *script_function_name) { Target *target = Target::GetTargetFromContexts(exe_ctx, sc); if (target) { ScriptInterpreter *script_interpreter = target->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); if (script_interpreter) { Status error; std::string script_output; if (script_interpreter->RunScriptFormatKeyword(script_function_name, t, script_output, error) && error.Success()) { s.Printf("%s", script_output.c_str()); return true; } else { s.Printf("", error.AsCString()); } } } return false; } static bool DumpAddress(Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const Address &addr, bool print_file_addr_or_load_addr) { Target *target = Target::GetTargetFromContexts(exe_ctx, sc); addr_t vaddr = LLDB_INVALID_ADDRESS; if (exe_ctx && !target->GetSectionLoadList().IsEmpty()) vaddr = addr.GetLoadAddress(target); if (vaddr == LLDB_INVALID_ADDRESS) vaddr = addr.GetFileAddress(); if (vaddr != LLDB_INVALID_ADDRESS) { int addr_width = 0; if (exe_ctx && target) { addr_width = target->GetArchitecture().GetAddressByteSize() * 2; } if (addr_width == 0) addr_width = 16; if (print_file_addr_or_load_addr) { ExecutionContextScope *exe_scope = nullptr; if (exe_ctx) exe_scope = exe_ctx->GetBestExecutionContextScope(); addr.Dump(&s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress, 0); } else { s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr); } return true; } return false; } static bool DumpAddressOffsetFromFunction(Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const Address &format_addr, bool concrete_only, bool no_padding, bool print_zero_offsets) { if (format_addr.IsValid()) { Address func_addr; if (sc) { if (sc->function) { func_addr = sc->function->GetAddressRange().GetBaseAddress(); if (sc->block && !concrete_only) { // Check to make sure we aren't in an inline function. If we are, use // the inline block range that contains "format_addr" since blocks // can be discontiguous. Block *inline_block = sc->block->GetContainingInlinedBlock(); AddressRange inline_range; if (inline_block && inline_block->GetRangeContainingAddress(format_addr, inline_range)) func_addr = inline_range.GetBaseAddress(); } } else if (sc->symbol && sc->symbol->ValueIsAddress()) func_addr = sc->symbol->GetAddressRef(); } if (func_addr.IsValid()) { const char *addr_offset_padding = no_padding ? "" : " "; if (func_addr.GetSection() == format_addr.GetSection()) { addr_t func_file_addr = func_addr.GetFileAddress(); addr_t addr_file_addr = format_addr.GetFileAddress(); if (addr_file_addr > func_file_addr || (addr_file_addr == func_file_addr && print_zero_offsets)) { s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding, addr_file_addr - func_file_addr); } else if (addr_file_addr < func_file_addr) { s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding, func_file_addr - addr_file_addr); } return true; } else { Target *target = Target::GetTargetFromContexts(exe_ctx, sc); if (target) { addr_t func_load_addr = func_addr.GetLoadAddress(target); addr_t addr_load_addr = format_addr.GetLoadAddress(target); if (addr_load_addr > func_load_addr || (addr_load_addr == func_load_addr && print_zero_offsets)) { s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding, addr_load_addr - func_load_addr); } else if (addr_load_addr < func_load_addr) { s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding, func_load_addr - addr_load_addr); } return true; } } } } return false; } static bool ScanBracketedRange(llvm::StringRef subpath, size_t &close_bracket_index, const char *&var_name_final_if_array_range, int64_t &index_lower, int64_t &index_higher) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS)); close_bracket_index = llvm::StringRef::npos; const size_t open_bracket_index = subpath.find('['); if (open_bracket_index == llvm::StringRef::npos) { if (log) log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely"); return false; } close_bracket_index = subpath.find(']', open_bracket_index + 1); if (close_bracket_index == llvm::StringRef::npos) { if (log) log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely"); return false; } else { var_name_final_if_array_range = subpath.data() + open_bracket_index; if (close_bracket_index - open_bracket_index == 1) { if (log) log->Printf( "[ScanBracketedRange] '[]' detected.. going from 0 to end of data"); index_lower = 0; } else { const size_t separator_index = subpath.find('-', open_bracket_index + 1); if (separator_index == llvm::StringRef::npos) { const char *index_lower_cstr = subpath.data() + open_bracket_index + 1; index_lower = ::strtoul(index_lower_cstr, nullptr, 0); index_higher = index_lower; if (log) log->Printf("[ScanBracketedRange] [%" PRId64 "] detected, high index is same", index_lower); } else { const char *index_lower_cstr = subpath.data() + open_bracket_index + 1; const char *index_higher_cstr = subpath.data() + separator_index + 1; index_lower = ::strtoul(index_lower_cstr, nullptr, 0); index_higher = ::strtoul(index_higher_cstr, nullptr, 0); if (log) log->Printf("[ScanBracketedRange] [%" PRId64 "-%" PRId64 "] detected", index_lower, index_higher); } if (index_lower > index_higher && index_higher > 0) { if (log) log->Printf("[ScanBracketedRange] swapping indices"); const int64_t temp = index_lower; index_lower = index_higher; index_higher = temp; } } } return true; } static bool DumpFile(Stream &s, const FileSpec &file, FileKind file_kind) { switch (file_kind) { case FileKind::FileError: break; case FileKind::Basename: if (file.GetFilename()) { s << file.GetFilename(); return true; } break; case FileKind::Dirname: if (file.GetDirectory()) { s << file.GetDirectory(); return true; } break; case FileKind::Fullpath: if (file) { s << file; return true; } break; } return false; } static bool DumpRegister(Stream &s, StackFrame *frame, RegisterKind reg_kind, uint32_t reg_num, Format format) { if (frame) { RegisterContext *reg_ctx = frame->GetRegisterContext().get(); if (reg_ctx) { const uint32_t lldb_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); if (lldb_reg_num != LLDB_INVALID_REGNUM) { const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(lldb_reg_num); if (reg_info) { RegisterValue reg_value; if (reg_ctx->ReadRegister(reg_info, reg_value)) { DumpRegisterValue(reg_value, &s, reg_info, false, false, format); return true; } } } } } return false; } static ValueObjectSP ExpandIndexedExpression(ValueObject *valobj, size_t index, StackFrame *frame, bool deref_pointer) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS)); const char *ptr_deref_format = "[%d]"; std::string ptr_deref_buffer(10, 0); ::sprintf(&ptr_deref_buffer[0], ptr_deref_format, index); if (log) log->Printf("[ExpandIndexedExpression] name to deref: %s", ptr_deref_buffer.c_str()); ValueObject::GetValueForExpressionPathOptions options; ValueObject::ExpressionPathEndResultType final_value_type; ValueObject::ExpressionPathScanEndReason reason_to_stop; ValueObject::ExpressionPathAftermath what_next = (deref_pointer ? ValueObject::eExpressionPathAftermathDereference : ValueObject::eExpressionPathAftermathNothing); ValueObjectSP item = valobj->GetValueForExpressionPath( ptr_deref_buffer.c_str(), &reason_to_stop, &final_value_type, options, &what_next); if (!item) { if (log) log->Printf("[ExpandIndexedExpression] ERROR: why stopping = %d," " final_value_type %d", reason_to_stop, final_value_type); } else { if (log) log->Printf("[ExpandIndexedExpression] ALL RIGHT: why stopping = %d," " final_value_type %d", reason_to_stop, final_value_type); } return item; } static char ConvertValueObjectStyleToChar( ValueObject::ValueObjectRepresentationStyle style) { switch (style) { case ValueObject::eValueObjectRepresentationStyleLanguageSpecific: return '@'; case ValueObject::eValueObjectRepresentationStyleValue: return 'V'; case ValueObject::eValueObjectRepresentationStyleLocation: return 'L'; case ValueObject::eValueObjectRepresentationStyleSummary: return 'S'; case ValueObject::eValueObjectRepresentationStyleChildrenCount: return '#'; case ValueObject::eValueObjectRepresentationStyleType: return 'T'; case ValueObject::eValueObjectRepresentationStyleName: return 'N'; case ValueObject::eValueObjectRepresentationStyleExpressionPath: return '>'; } return '\0'; } static bool DumpValue(Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const FormatEntity::Entry &entry, ValueObject *valobj) { if (valobj == nullptr) return false; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS)); Format custom_format = eFormatInvalid; ValueObject::ValueObjectRepresentationStyle val_obj_display = entry.string.empty() ? ValueObject::eValueObjectRepresentationStyleValue : ValueObject::eValueObjectRepresentationStyleSummary; bool do_deref_pointer = entry.deref; bool is_script = false; switch (entry.type) { case FormatEntity::Entry::Type::ScriptVariable: is_script = true; break; case FormatEntity::Entry::Type::Variable: custom_format = entry.fmt; val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number; break; case FormatEntity::Entry::Type::ScriptVariableSynthetic: is_script = true; LLVM_FALLTHROUGH; case FormatEntity::Entry::Type::VariableSynthetic: custom_format = entry.fmt; val_obj_display = (ValueObject::ValueObjectRepresentationStyle)entry.number; if (!valobj->IsSynthetic()) { valobj = valobj->GetSyntheticValue().get(); if (valobj == nullptr) return false; } break; default: return false; } if (valobj == nullptr) return false; ValueObject::ExpressionPathAftermath what_next = (do_deref_pointer ? ValueObject::eExpressionPathAftermathDereference : ValueObject::eExpressionPathAftermathNothing); ValueObject::GetValueForExpressionPathOptions options; options.DontCheckDotVsArrowSyntax() .DoAllowBitfieldSyntax() .DoAllowFragileIVar() .SetSyntheticChildrenTraversal( ValueObject::GetValueForExpressionPathOptions:: SyntheticChildrenTraversal::Both); ValueObject *target = nullptr; const char *var_name_final_if_array_range = nullptr; size_t close_bracket_index = llvm::StringRef::npos; int64_t index_lower = -1; int64_t index_higher = -1; bool is_array_range = false; bool was_plain_var = false; bool was_var_format = false; bool was_var_indexed = false; ValueObject::ExpressionPathScanEndReason reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; ValueObject::ExpressionPathEndResultType final_value_type = ValueObject::eExpressionPathEndResultTypePlain; if (is_script) { return RunScriptFormatKeyword(s, sc, exe_ctx, valobj, entry.string.c_str()); } llvm::StringRef subpath(entry.string); // simplest case ${var}, just print valobj's value if (entry.string.empty()) { if (entry.printf_format.empty() && entry.fmt == eFormatDefault && entry.number == ValueObject::eValueObjectRepresentationStyleValue) was_plain_var = true; else was_var_format = true; target = valobj; } else // this is ${var.something} or multiple .something nested { if (entry.string[0] == '[') was_var_indexed = true; ScanBracketedRange(subpath, close_bracket_index, var_name_final_if_array_range, index_lower, index_higher); Status error; const std::string &expr_path = entry.string; if (log) log->Printf("[Debugger::FormatPrompt] symbol to expand: %s", expr_path.c_str()); target = valobj ->GetValueForExpressionPath(expr_path.c_str(), &reason_to_stop, &final_value_type, options, &what_next) .get(); if (!target) { if (log) log->Printf("[Debugger::FormatPrompt] ERROR: why stopping = %d," " final_value_type %d", reason_to_stop, final_value_type); return false; } else { if (log) log->Printf("[Debugger::FormatPrompt] ALL RIGHT: why stopping = %d," " final_value_type %d", reason_to_stop, final_value_type); target = target ->GetQualifiedRepresentationIfAvailable( target->GetDynamicValueType(), true) .get(); } } is_array_range = (final_value_type == ValueObject::eExpressionPathEndResultTypeBoundedRange || final_value_type == ValueObject::eExpressionPathEndResultTypeUnboundedRange); do_deref_pointer = (what_next == ValueObject::eExpressionPathAftermathDereference); if (do_deref_pointer && !is_array_range) { // I have not deref-ed yet, let's do it // this happens when we are not going through // GetValueForVariableExpressionPath to get to the target ValueObject Status error; target = target->Dereference(error).get(); if (error.Fail()) { if (log) log->Printf("[Debugger::FormatPrompt] ERROR: %s\n", error.AsCString("unknown")); return false; } do_deref_pointer = false; } if (!target) { if (log) log->Printf("[Debugger::FormatPrompt] could not calculate target for " "prompt expression"); return false; } // we do not want to use the summary for a bitfield of type T:n if we were // originally dealing with just a T - that would get us into an endless // recursion if (target->IsBitfield() && was_var_indexed) { // TODO: check for a (T:n)-specific summary - we should still obey that StreamString bitfield_name; bitfield_name.Printf("%s:%d", target->GetTypeName().AsCString(), target->GetBitfieldBitSize()); auto type_sp = std::make_shared( bitfield_name.GetString(), false); if (val_obj_display == ValueObject::eValueObjectRepresentationStyleSummary && !DataVisualization::GetSummaryForType(type_sp)) val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; } // TODO use flags for these const uint32_t type_info_flags = target->GetCompilerType().GetTypeInfo(nullptr); bool is_array = (type_info_flags & eTypeIsArray) != 0; bool is_pointer = (type_info_flags & eTypeIsPointer) != 0; bool is_aggregate = target->GetCompilerType().IsAggregateType(); if ((is_array || is_pointer) && (!is_array_range) && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) // this should be // wrong, but there // are some // exceptions { StreamString str_temp; if (log) log->Printf( "[Debugger::FormatPrompt] I am into array || pointer && !range"); if (target->HasSpecialPrintableRepresentation(val_obj_display, custom_format)) { // try to use the special cases bool success = target->DumpPrintableRepresentation( str_temp, val_obj_display, custom_format); if (log) log->Printf("[Debugger::FormatPrompt] special cases did%s match", success ? "" : "n't"); // should not happen if (success) s << str_temp.GetString(); return true; } else { if (was_plain_var) // if ${var} { s << target->GetTypeName() << " @ " << target->GetLocationAsCString(); } else if (is_pointer) // if pointer, value is the address stored { target->DumpPrintableRepresentation( s, val_obj_display, custom_format, ValueObject::PrintableRepresentationSpecialCases::eDisable); } return true; } } // if directly trying to print ${var}, and this is an aggregate, display a // nice type @ location message if (is_aggregate && was_plain_var) { s << target->GetTypeName() << " @ " << target->GetLocationAsCString(); return true; } // if directly trying to print ${var%V}, and this is an aggregate, do not let // the user do it if (is_aggregate && ((was_var_format && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue))) { s << ""; return true; } if (!is_array_range) { if (log) log->Printf("[Debugger::FormatPrompt] dumping ordinary printable output"); return target->DumpPrintableRepresentation(s, val_obj_display, custom_format); } else { if (log) log->Printf("[Debugger::FormatPrompt] checking if I can handle as array"); if (!is_array && !is_pointer) return false; if (log) log->Printf("[Debugger::FormatPrompt] handle as array"); StreamString special_directions_stream; llvm::StringRef special_directions; if (close_bracket_index != llvm::StringRef::npos && subpath.size() > close_bracket_index) { ConstString additional_data(subpath.drop_front(close_bracket_index + 1)); special_directions_stream.Printf("${%svar%s", do_deref_pointer ? "*" : "", additional_data.GetCString()); if (entry.fmt != eFormatDefault) { const char format_char = FormatManager::GetFormatAsFormatChar(entry.fmt); if (format_char != '\0') special_directions_stream.Printf("%%%c", format_char); else { const char *format_cstr = FormatManager::GetFormatAsCString(entry.fmt); special_directions_stream.Printf("%%%s", format_cstr); } } else if (entry.number != 0) { const char style_char = ConvertValueObjectStyleToChar( (ValueObject::ValueObjectRepresentationStyle)entry.number); if (style_char) special_directions_stream.Printf("%%%c", style_char); } special_directions_stream.PutChar('}'); special_directions = llvm::StringRef(special_directions_stream.GetString()); } // let us display items index_lower thru index_higher of this array s.PutChar('['); if (index_higher < 0) index_higher = valobj->GetNumChildren() - 1; uint32_t max_num_children = target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); bool success = true; for (int64_t index = index_lower; index <= index_higher; ++index) { ValueObject *item = ExpandIndexedExpression(target, index, exe_ctx->GetFramePtr(), false) .get(); if (!item) { if (log) log->Printf("[Debugger::FormatPrompt] ERROR in getting child item at " "index %" PRId64, index); } else { if (log) log->Printf( "[Debugger::FormatPrompt] special_directions for child item: %s", special_directions.data() ? special_directions.data() : ""); } if (special_directions.empty()) { success &= item->DumpPrintableRepresentation(s, val_obj_display, custom_format); } else { success &= FormatEntity::FormatStringRef( special_directions, s, sc, exe_ctx, nullptr, item, false, false); } if (--max_num_children == 0) { s.PutCString(", ..."); break; } if (index < index_higher) s.PutChar(','); } s.PutChar(']'); return success; } } static bool DumpRegister(Stream &s, StackFrame *frame, const char *reg_name, Format format) { if (frame) { RegisterContext *reg_ctx = frame->GetRegisterContext().get(); if (reg_ctx) { const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); if (reg_info) { RegisterValue reg_value; if (reg_ctx->ReadRegister(reg_info, reg_value)) { DumpRegisterValue(reg_value, &s, reg_info, false, false, format); return true; } } } } return false; } static bool FormatThreadExtendedInfoRecurse( const FormatEntity::Entry &entry, const StructuredData::ObjectSP &thread_info_dictionary, const SymbolContext *sc, const ExecutionContext *exe_ctx, Stream &s) { llvm::StringRef path(entry.string); StructuredData::ObjectSP value = thread_info_dictionary->GetObjectForDotSeparatedPath(path); if (value) { if (value->GetType() == eStructuredDataTypeInteger) { const char *token_format = "0x%4.4" PRIx64; if (!entry.printf_format.empty()) token_format = entry.printf_format.c_str(); s.Printf(token_format, value->GetAsInteger()->GetValue()); return true; } else if (value->GetType() == eStructuredDataTypeFloat) { s.Printf("%f", value->GetAsFloat()->GetValue()); return true; } else if (value->GetType() == eStructuredDataTypeString) { s.Format("{0}", value->GetAsString()->GetValue()); return true; } else if (value->GetType() == eStructuredDataTypeArray) { if (value->GetAsArray()->GetSize() > 0) { s.Printf("%zu", value->GetAsArray()->GetSize()); return true; } } else if (value->GetType() == eStructuredDataTypeDictionary) { s.Printf("%zu", value->GetAsDictionary()->GetKeys()->GetAsArray()->GetSize()); return true; } } return false; } static inline bool IsToken(const char *var_name_begin, const char *var) { return (::strncmp(var_name_begin, var, strlen(var)) == 0); } bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const Address *addr, ValueObject *valobj, bool function_changed, bool initial_function) { if (!format_str.empty()) { FormatEntity::Entry root; Status error = FormatEntity::Parse(format_str, root); if (error.Success()) { return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj, function_changed, initial_function); } } return false; } bool FormatEntity::FormatCString(const char *format, Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const Address *addr, ValueObject *valobj, bool function_changed, bool initial_function) { if (format && format[0]) { FormatEntity::Entry root; llvm::StringRef format_str(format); Status error = FormatEntity::Parse(format_str, root); if (error.Success()) { return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj, function_changed, initial_function); } } return false; } bool FormatEntity::Format(const Entry &entry, Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const Address *addr, ValueObject *valobj, bool function_changed, bool initial_function) { switch (entry.type) { case Entry::Type::Invalid: case Entry::Type::ParentNumber: // Only used for // FormatEntity::Entry::Definition encoding case Entry::Type::ParentString: // Only used for // FormatEntity::Entry::Definition encoding case Entry::Type::InsertString: // Only used for // FormatEntity::Entry::Definition encoding return false; case Entry::Type::Root: for (const auto &child : entry.children) { if (!Format(child, s, sc, exe_ctx, addr, valobj, function_changed, initial_function)) { return false; // If any item of root fails, then the formatting fails } } return true; // Only return true if all items succeeded case Entry::Type::String: s.PutCString(entry.string); return true; case Entry::Type::Scope: { StreamString scope_stream; bool success = false; for (const auto &child : entry.children) { success = Format(child, scope_stream, sc, exe_ctx, addr, valobj, function_changed, initial_function); if (!success) break; } // Only if all items in a scope succeed, then do we print the output into // the main stream if (success) s.Write(scope_stream.GetString().data(), scope_stream.GetString().size()); } return true; // Scopes always successfully print themselves case Entry::Type::Variable: case Entry::Type::VariableSynthetic: case Entry::Type::ScriptVariable: case Entry::Type::ScriptVariableSynthetic: return DumpValue(s, sc, exe_ctx, entry, valobj); case Entry::Type::AddressFile: case Entry::Type::AddressLoad: case Entry::Type::AddressLoadOrFile: return (addr != nullptr && addr->IsValid() && DumpAddress(s, sc, exe_ctx, *addr, entry.type == Entry::Type::AddressLoadOrFile)); case Entry::Type::ProcessID: if (exe_ctx) { Process *process = exe_ctx->GetProcessPtr(); if (process) { const char *format = "%" PRIu64; if (!entry.printf_format.empty()) format = entry.printf_format.c_str(); s.Printf(format, process->GetID()); return true; } } return false; case Entry::Type::ProcessFile: if (exe_ctx) { Process *process = exe_ctx->GetProcessPtr(); if (process) { Module *exe_module = process->GetTarget().GetExecutableModulePointer(); if (exe_module) { if (DumpFile(s, exe_module->GetFileSpec(), (FileKind)entry.number)) return true; } } } return false; case Entry::Type::ScriptProcess: if (exe_ctx) { Process *process = exe_ctx->GetProcessPtr(); if (process) return RunScriptFormatKeyword(s, sc, exe_ctx, process, entry.string.c_str()); } return false; case Entry::Type::ThreadID: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { const char *format = "0x%4.4" PRIx64; if (!entry.printf_format.empty()) { // Watch for the special "tid" format... if (entry.printf_format == "tid") { // TODO(zturner): Rather than hardcoding this to be platform // specific, it should be controlled by a setting and the default // value of the setting can be different depending on the platform. Target &target = thread->GetProcess()->GetTarget(); ArchSpec arch(target.GetArchitecture()); llvm::Triple::OSType ostype = arch.IsValid() ? arch.GetTriple().getOS() : llvm::Triple::UnknownOS; if ((ostype == llvm::Triple::FreeBSD) || (ostype == llvm::Triple::Linux) || (ostype == llvm::Triple::NetBSD)) { format = "%" PRIu64; } } else { format = entry.printf_format.c_str(); } } s.Printf(format, thread->GetID()); return true; } } return false; case Entry::Type::ThreadProtocolID: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { const char *format = "0x%4.4" PRIx64; if (!entry.printf_format.empty()) format = entry.printf_format.c_str(); s.Printf(format, thread->GetProtocolID()); return true; } } return false; case Entry::Type::ThreadIndexID: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { const char *format = "%" PRIu32; if (!entry.printf_format.empty()) format = entry.printf_format.c_str(); s.Printf(format, thread->GetIndexID()); return true; } } return false; case Entry::Type::ThreadName: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { const char *cstr = thread->GetName(); if (cstr && cstr[0]) { s.PutCString(cstr); return true; } } } return false; case Entry::Type::ThreadQueue: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { const char *cstr = thread->GetQueueName(); if (cstr && cstr[0]) { s.PutCString(cstr); return true; } } } return false; case Entry::Type::ThreadStopReason: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { StopInfoSP stop_info_sp = thread->GetStopInfo(); if (stop_info_sp && stop_info_sp->IsValid()) { const char *cstr = stop_info_sp->GetDescription(); if (cstr && cstr[0]) { s.PutCString(cstr); return true; } } } } return false; case Entry::Type::ThreadReturnValue: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { StopInfoSP stop_info_sp = thread->GetStopInfo(); if (stop_info_sp && stop_info_sp->IsValid()) { ValueObjectSP return_valobj_sp = StopInfo::GetReturnValueObject(stop_info_sp); if (return_valobj_sp) { return_valobj_sp->Dump(s); return true; } } } } return false; case Entry::Type::ThreadCompletedExpression: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { StopInfoSP stop_info_sp = thread->GetStopInfo(); if (stop_info_sp && stop_info_sp->IsValid()) { ExpressionVariableSP expression_var_sp = StopInfo::GetExpressionVariable(stop_info_sp); if (expression_var_sp && expression_var_sp->GetValueObject()) { expression_var_sp->GetValueObject()->Dump(s); return true; } } } } return false; case Entry::Type::ScriptThread: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) return RunScriptFormatKeyword(s, sc, exe_ctx, thread, entry.string.c_str()); } return false; case Entry::Type::ThreadInfo: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { StructuredData::ObjectSP object_sp = thread->GetExtendedInfo(); if (object_sp && object_sp->GetType() == eStructuredDataTypeDictionary) { if (FormatThreadExtendedInfoRecurse(entry, object_sp, sc, exe_ctx, s)) return true; } } } return false; case Entry::Type::TargetArch: if (exe_ctx) { Target *target = exe_ctx->GetTargetPtr(); if (target) { const ArchSpec &arch = target->GetArchitecture(); if (arch.IsValid()) { s.PutCString(arch.GetArchitectureName()); return true; } } } return false; case Entry::Type::ScriptTarget: if (exe_ctx) { Target *target = exe_ctx->GetTargetPtr(); if (target) return RunScriptFormatKeyword(s, sc, exe_ctx, target, entry.string.c_str()); } return false; case Entry::Type::ModuleFile: if (sc) { Module *module = sc->module_sp.get(); if (module) { if (DumpFile(s, module->GetFileSpec(), (FileKind)entry.number)) return true; } } return false; case Entry::Type::File: if (sc) { CompileUnit *cu = sc->comp_unit; if (cu) { // CompileUnit is a FileSpec if (DumpFile(s, *cu, (FileKind)entry.number)) return true; } } return false; case Entry::Type::Lang: if (sc) { CompileUnit *cu = sc->comp_unit; if (cu) { const char *lang_name = Language::GetNameForLanguageType(cu->GetLanguage()); if (lang_name) { s.PutCString(lang_name); return true; } } } return false; case Entry::Type::FrameIndex: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { const char *format = "%" PRIu32; if (!entry.printf_format.empty()) format = entry.printf_format.c_str(); s.Printf(format, frame->GetFrameIndex()); return true; } } return false; case Entry::Type::FrameRegisterPC: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { const Address &pc_addr = frame->GetFrameCodeAddress(); if (pc_addr.IsValid()) { if (DumpAddress(s, sc, exe_ctx, pc_addr, false)) return true; } } } return false; case Entry::Type::FrameRegisterSP: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, (lldb::Format)entry.number)) return true; } } return false; case Entry::Type::FrameRegisterFP: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, (lldb::Format)entry.number)) return true; } } return false; case Entry::Type::FrameRegisterFlags: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { if (DumpRegister(s, frame, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, (lldb::Format)entry.number)) return true; } } return false; case Entry::Type::FrameNoDebug: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { return !frame->HasDebugInformation(); } } return true; case Entry::Type::FrameRegisterByName: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { if (DumpRegister(s, frame, entry.string.c_str(), (lldb::Format)entry.number)) return true; } } return false; case Entry::Type::ScriptFrame: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) return RunScriptFormatKeyword(s, sc, exe_ctx, frame, entry.string.c_str()); } return false; case Entry::Type::FunctionID: if (sc) { if (sc->function) { s.Printf("function{0x%8.8" PRIx64 "}", sc->function->GetID()); return true; } else if (sc->symbol) { s.Printf("symbol[%u]", sc->symbol->GetID()); return true; } } return false; case Entry::Type::FunctionDidChange: return function_changed; case Entry::Type::FunctionInitialFunction: return initial_function; case Entry::Type::FunctionName: { Language *language_plugin = nullptr; bool language_plugin_handled = false; StreamString ss; if (sc->function) language_plugin = Language::FindPlugin(sc->function->GetLanguage()); else if (sc->symbol) language_plugin = Language::FindPlugin(sc->symbol->GetLanguage()); if (language_plugin) { language_plugin_handled = language_plugin->GetFunctionDisplayName( sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss); } if (language_plugin_handled) { s << ss.GetString(); return true; } else { const char *name = nullptr; if (sc->function) name = sc->function->GetName().AsCString(nullptr); else if (sc->symbol) name = sc->symbol->GetName().AsCString(nullptr); if (name) { s.PutCString(name); if (sc->block) { Block *inline_block = sc->block->GetContainingInlinedBlock(); if (inline_block) { const InlineFunctionInfo *inline_info = sc->block->GetInlinedFunctionInfo(); if (inline_info) { s.PutCString(" [inlined] "); inline_info->GetName(sc->function->GetLanguage()).Dump(&s); } } } return true; } } } return false; case Entry::Type::FunctionNameNoArgs: { Language *language_plugin = nullptr; bool language_plugin_handled = false; StreamString ss; if (sc->function) language_plugin = Language::FindPlugin(sc->function->GetLanguage()); else if (sc->symbol) language_plugin = Language::FindPlugin(sc->symbol->GetLanguage()); if (language_plugin) { language_plugin_handled = language_plugin->GetFunctionDisplayName( sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs, ss); } if (language_plugin_handled) { s << ss.GetString(); return true; } else { ConstString name; if (sc->function) name = sc->function->GetNameNoArguments(); else if (sc->symbol) name = sc->symbol->GetNameNoArguments(); if (name) { s.PutCString(name.GetCString()); return true; } } } return false; case Entry::Type::FunctionNameWithArgs: { Language *language_plugin = nullptr; bool language_plugin_handled = false; StreamString ss; if (sc->function) language_plugin = Language::FindPlugin(sc->function->GetLanguage()); else if (sc->symbol) language_plugin = Language::FindPlugin(sc->symbol->GetLanguage()); if (language_plugin) { language_plugin_handled = language_plugin->GetFunctionDisplayName( sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss); } if (language_plugin_handled) { s << ss.GetString(); return true; } else { // Print the function name with arguments in it if (sc->function) { ExecutionContextScope *exe_scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; const char *cstr = sc->function->GetName().AsCString(nullptr); if (cstr) { const InlineFunctionInfo *inline_info = nullptr; VariableListSP variable_list_sp; bool get_function_vars = true; if (sc->block) { Block *inline_block = sc->block->GetContainingInlinedBlock(); if (inline_block) { get_function_vars = false; inline_info = sc->block->GetInlinedFunctionInfo(); if (inline_info) variable_list_sp = inline_block->GetBlockVariableList(true); } } if (get_function_vars) { variable_list_sp = sc->function->GetBlock(true).GetBlockVariableList(true); } if (inline_info) { s.PutCString(cstr); s.PutCString(" [inlined] "); cstr = inline_info->GetName(sc->function->GetLanguage()).GetCString(); } VariableList args; if (variable_list_sp) variable_list_sp->AppendVariablesWithScope( eValueTypeVariableArgument, args); if (args.GetSize() > 0) { const char *open_paren = strchr(cstr, '('); const char *close_paren = nullptr; const char *generic = strchr(cstr, '<'); // if before the arguments list begins there is a template sign // then scan to the end of the generic args before you try to find // the arguments list if (generic && open_paren && generic < open_paren) { int generic_depth = 1; ++generic; for (; *generic && generic_depth > 0; generic++) { if (*generic == '<') generic_depth++; if (*generic == '>') generic_depth--; } if (*generic) open_paren = strchr(generic, '('); else open_paren = nullptr; } if (open_paren) { if (IsToken(open_paren, "(anonymous namespace)")) { open_paren = strchr(open_paren + strlen("(anonymous namespace)"), '('); if (open_paren) close_paren = strchr(open_paren, ')'); } else close_paren = strchr(open_paren, ')'); } if (open_paren) s.Write(cstr, open_paren - cstr + 1); else { s.PutCString(cstr); s.PutChar('('); } const size_t num_args = args.GetSize(); for (size_t arg_idx = 0; arg_idx < num_args; ++arg_idx) { std::string buffer; VariableSP var_sp(args.GetVariableAtIndex(arg_idx)); ValueObjectSP var_value_sp( ValueObjectVariable::Create(exe_scope, var_sp)); StreamString ss; llvm::StringRef var_representation; const char *var_name = var_value_sp->GetName().GetCString(); if (var_value_sp->GetCompilerType().IsValid()) { if (var_value_sp && exe_scope->CalculateTarget()) var_value_sp = var_value_sp->GetQualifiedRepresentationIfAvailable( exe_scope->CalculateTarget() ->TargetProperties::GetPreferDynamicValue(), exe_scope->CalculateTarget() ->TargetProperties::GetEnableSyntheticValue()); if (var_value_sp->GetCompilerType().IsAggregateType() && DataVisualization::ShouldPrintAsOneLiner(*var_value_sp)) { static StringSummaryFormat format( TypeSummaryImpl::Flags() .SetHideItemNames(false) .SetShowMembersOneLiner(true), ""); format.FormatObject(var_value_sp.get(), buffer, TypeSummaryOptions()); var_representation = buffer; } else var_value_sp->DumpPrintableRepresentation( ss, ValueObject::ValueObjectRepresentationStyle:: eValueObjectRepresentationStyleSummary, eFormatDefault, ValueObject::PrintableRepresentationSpecialCases::eAllow, false); } if (!ss.GetString().empty()) var_representation = ss.GetString(); if (arg_idx > 0) s.PutCString(", "); if (var_value_sp->GetError().Success()) { if (!var_representation.empty()) s.Printf("%s=%s", var_name, var_representation.str().c_str()); else s.Printf("%s=%s at %s", var_name, var_value_sp->GetTypeName().GetCString(), var_value_sp->GetLocationAsCString()); } else s.Printf("%s=", var_name); } if (close_paren) s.PutCString(close_paren); else s.PutChar(')'); } else { s.PutCString(cstr); } return true; } } else if (sc->symbol) { const char *cstr = sc->symbol->GetName().AsCString(nullptr); if (cstr) { s.PutCString(cstr); return true; } } } } return false; case Entry::Type::FunctionAddrOffset: if (addr) { if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, false, false, false)) return true; } return false; case Entry::Type::FunctionAddrOffsetConcrete: if (addr) { if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, *addr, true, true, true)) return true; } return false; case Entry::Type::FunctionLineOffset: return (DumpAddressOffsetFromFunction(s, sc, exe_ctx, sc->line_entry.range.GetBaseAddress(), false, false, false)); case Entry::Type::FunctionPCOffset: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, frame->GetFrameCodeAddress(), false, false, false)) return true; } } return false; case Entry::Type::FunctionChanged: return function_changed; case Entry::Type::FunctionIsOptimized: { bool is_optimized = false; if (sc->function && sc->function->GetIsOptimized()) { is_optimized = true; } return is_optimized; } case Entry::Type::FunctionInitial: return initial_function; case Entry::Type::LineEntryFile: if (sc && sc->line_entry.IsValid()) { Module *module = sc->module_sp.get(); if (module) { if (DumpFile(s, sc->line_entry.file, (FileKind)entry.number)) return true; } } return false; case Entry::Type::LineEntryLineNumber: if (sc && sc->line_entry.IsValid()) { const char *format = "%" PRIu32; if (!entry.printf_format.empty()) format = entry.printf_format.c_str(); s.Printf(format, sc->line_entry.line); return true; } return false; case Entry::Type::LineEntryStartAddress: case Entry::Type::LineEntryEndAddress: if (sc && sc->line_entry.range.GetBaseAddress().IsValid()) { Address addr = sc->line_entry.range.GetBaseAddress(); if (entry.type == Entry::Type::LineEntryEndAddress) addr.Slide(sc->line_entry.range.GetByteSize()); if (DumpAddress(s, sc, exe_ctx, addr, false)) return true; } return false; case Entry::Type::CurrentPCArrow: if (addr && exe_ctx && exe_ctx->GetFramePtr()) { RegisterContextSP reg_ctx = exe_ctx->GetFramePtr()->GetRegisterContextSP(); if (reg_ctx) { addr_t pc_loadaddr = reg_ctx->GetPC(); if (pc_loadaddr != LLDB_INVALID_ADDRESS) { Address pc; pc.SetLoadAddress(pc_loadaddr, exe_ctx->GetTargetPtr()); if (pc == *addr) { s.Printf("-> "); return true; } } } s.Printf(" "); return true; } return false; } return false; } static bool DumpCommaSeparatedChildEntryNames( Stream &s, const FormatEntity::Entry::Definition *parent) { if (parent->children) { const size_t n = parent->num_children; for (size_t i = 0; i < n; ++i) { if (i > 0) s.PutCString(", "); s.Printf("\"%s\"", parent->children[i].name); } return true; } return false; } static Status ParseEntry(const llvm::StringRef &format_str, const FormatEntity::Entry::Definition *parent, FormatEntity::Entry &entry) { Status error; const size_t sep_pos = format_str.find_first_of(".[:"); const char sep_char = (sep_pos == llvm::StringRef::npos) ? '\0' : format_str[sep_pos]; llvm::StringRef key = format_str.substr(0, sep_pos); const size_t n = parent->num_children; for (size_t i = 0; i < n; ++i) { const FormatEntity::Entry::Definition *entry_def = parent->children + i; if (key.equals(entry_def->name) || entry_def->name[0] == '*') { llvm::StringRef value; if (sep_char) value = format_str.substr(sep_pos + (entry_def->keep_separator ? 0 : 1)); switch (entry_def->type) { case FormatEntity::Entry::Type::ParentString: entry.string = format_str.str(); return error; // Success case FormatEntity::Entry::Type::ParentNumber: entry.number = entry_def->data; return error; // Success case FormatEntity::Entry::Type::InsertString: entry.type = entry_def->type; entry.string = entry_def->string; return error; // Success default: entry.type = entry_def->type; break; } if (value.empty()) { if (entry_def->type == FormatEntity::Entry::Type::Invalid) { if (entry_def->children) { StreamString error_strm; error_strm.Printf("'%s' can't be specified on its own, you must " "access one of its children: ", entry_def->name); DumpCommaSeparatedChildEntryNames(error_strm, entry_def); error.SetErrorStringWithFormat("%s", error_strm.GetData()); } else if (sep_char == ':') { // Any value whose separator is a with a ':' means this value has a // string argument that needs to be stored in the entry (like // "${script.var:}"). In this case the string value is the empty // string which is ok. } else { error.SetErrorStringWithFormat("%s", "invalid entry definitions"); } } } else { if (entry_def->children) { error = ParseEntry(value, entry_def, entry); } else if (sep_char == ':') { // Any value whose separator is a with a ':' means this value has a // string argument that needs to be stored in the entry (like // "${script.var:modulename.function}") entry.string = value.str(); } else { error.SetErrorStringWithFormat( "'%s' followed by '%s' but it has no children", key.str().c_str(), value.str().c_str()); } } return error; } } StreamString error_strm; if (parent->type == FormatEntity::Entry::Type::Root) error_strm.Printf( "invalid top level item '%s'. Valid top level items are: ", key.str().c_str()); else error_strm.Printf("invalid member '%s' in '%s'. Valid members are: ", key.str().c_str(), parent->name); DumpCommaSeparatedChildEntryNames(error_strm, parent); error.SetErrorStringWithFormat("%s", error_strm.GetData()); return error; } static const FormatEntity::Entry::Definition * FindEntry(const llvm::StringRef &format_str, const FormatEntity::Entry::Definition *parent, llvm::StringRef &remainder) { Status error; std::pair p = format_str.split('.'); const size_t n = parent->num_children; for (size_t i = 0; i < n; ++i) { const FormatEntity::Entry::Definition *entry_def = parent->children + i; if (p.first.equals(entry_def->name) || entry_def->name[0] == '*') { if (p.second.empty()) { if (format_str.back() == '.') remainder = format_str.drop_front(format_str.size() - 1); else remainder = llvm::StringRef(); // Exact match return entry_def; } else { if (entry_def->children) { return FindEntry(p.second, entry_def, remainder); } else { remainder = p.second; return entry_def; } } } } remainder = format_str; return parent; } Status FormatEntity::ParseInternal(llvm::StringRef &format, Entry &parent_entry, uint32_t depth) { Status error; while (!format.empty() && error.Success()) { const size_t non_special_chars = format.find_first_of("${}\\"); if (non_special_chars == llvm::StringRef::npos) { // No special characters, just string bytes so add them and we are done parent_entry.AppendText(format); return error; } if (non_special_chars > 0) { // We have a special character, so add all characters before these as a // plain string parent_entry.AppendText(format.substr(0, non_special_chars)); format = format.drop_front(non_special_chars); } switch (format[0]) { case '\0': return error; case '{': { format = format.drop_front(); // Skip the '{' Entry scope_entry(Entry::Type::Scope); error = FormatEntity::ParseInternal(format, scope_entry, depth + 1); if (error.Fail()) return error; parent_entry.AppendEntry(std::move(scope_entry)); } break; case '}': if (depth == 0) error.SetErrorString("unmatched '}' character"); else format = format .drop_front(); // Skip the '}' as we are at the end of the scope return error; case '\\': { format = format.drop_front(); // Skip the '\' character if (format.empty()) { error.SetErrorString( "'\\' character was not followed by another character"); return error; } const char desens_char = format[0]; format = format.drop_front(); // Skip the desensitized char character switch (desens_char) { case 'a': parent_entry.AppendChar('\a'); break; case 'b': parent_entry.AppendChar('\b'); break; case 'f': parent_entry.AppendChar('\f'); break; case 'n': parent_entry.AppendChar('\n'); break; case 'r': parent_entry.AppendChar('\r'); break; case 't': parent_entry.AppendChar('\t'); break; case 'v': parent_entry.AppendChar('\v'); break; case '\'': parent_entry.AppendChar('\''); break; case '\\': parent_entry.AppendChar('\\'); break; case '0': // 1 to 3 octal chars { // Make a string that can hold onto the initial zero char, up to 3 // octal digits, and a terminating NULL. char oct_str[5] = {0, 0, 0, 0, 0}; int i; for (i = 0; (format[i] >= '0' && format[i] <= '7') && i < 4; ++i) oct_str[i] = format[i]; // We don't want to consume the last octal character since the main // for loop will do this for us, so we advance p by one less than i // (even if i is zero) format = format.drop_front(i); unsigned long octal_value = ::strtoul(oct_str, nullptr, 8); if (octal_value <= UINT8_MAX) { parent_entry.AppendChar((char)octal_value); } else { error.SetErrorString("octal number is larger than a single byte"); return error; } } break; case 'x': // hex number in the format if (isxdigit(format[0])) { // Make a string that can hold onto two hex chars plus a // NULL terminator char hex_str[3] = {0, 0, 0}; hex_str[0] = format[0]; format = format.drop_front(); if (isxdigit(format[0])) { hex_str[1] = format[0]; format = format.drop_front(); } unsigned long hex_value = strtoul(hex_str, nullptr, 16); if (hex_value <= UINT8_MAX) { parent_entry.AppendChar((char)hex_value); } else { error.SetErrorString("hex number is larger than a single byte"); return error; } } else { parent_entry.AppendChar(desens_char); } break; default: // Just desensitize any other character by just printing what came // after the '\' parent_entry.AppendChar(desens_char); break; } } break; case '$': if (format.size() == 1) { // '$' at the end of a format string, just print the '$' parent_entry.AppendText("$"); } else { format = format.drop_front(); // Skip the '$' if (format[0] == '{') { format = format.drop_front(); // Skip the '{' llvm::StringRef variable, variable_format; error = FormatEntity::ExtractVariableInfo(format, variable, variable_format); if (error.Fail()) return error; bool verify_is_thread_id = false; Entry entry; if (!variable_format.empty()) { entry.printf_format = variable_format.str(); // If the format contains a '%' we are going to assume this is a // printf style format. So if you want to format your thread ID // using "0x%llx" you can use: ${thread.id%0x%llx} // // If there is no '%' in the format, then it is assumed to be a // LLDB format name, or one of the extended formats specified in // the switch statement below. if (entry.printf_format.find('%') == std::string::npos) { bool clear_printf = false; if (FormatManager::GetFormatFromCString( entry.printf_format.c_str(), false, entry.fmt)) { // We have an LLDB format, so clear the printf format clear_printf = true; } else if (entry.printf_format.size() == 1) { switch (entry.printf_format[0]) { case '@': // if this is an @ sign, print ObjC description entry.number = ValueObject:: eValueObjectRepresentationStyleLanguageSpecific; clear_printf = true; break; case 'V': // if this is a V, print the value using the default // format entry.number = ValueObject::eValueObjectRepresentationStyleValue; clear_printf = true; break; case 'L': // if this is an L, print the location of the value entry.number = ValueObject::eValueObjectRepresentationStyleLocation; clear_printf = true; break; case 'S': // if this is an S, print the summary after all entry.number = ValueObject::eValueObjectRepresentationStyleSummary; clear_printf = true; break; case '#': // if this is a '#', print the number of children entry.number = ValueObject::eValueObjectRepresentationStyleChildrenCount; clear_printf = true; break; case 'T': // if this is a 'T', print the type entry.number = ValueObject::eValueObjectRepresentationStyleType; clear_printf = true; break; case 'N': // if this is a 'N', print the name entry.number = ValueObject::eValueObjectRepresentationStyleName; clear_printf = true; break; case '>': // if this is a '>', print the expression path entry.number = ValueObject:: eValueObjectRepresentationStyleExpressionPath; clear_printf = true; break; default: error.SetErrorStringWithFormat("invalid format: '%s'", entry.printf_format.c_str()); return error; } } else if (FormatManager::GetFormatFromCString( entry.printf_format.c_str(), true, entry.fmt)) { clear_printf = true; } else if (entry.printf_format == "tid") { verify_is_thread_id = true; } else { error.SetErrorStringWithFormat("invalid format: '%s'", entry.printf_format.c_str()); return error; } // Our format string turned out to not be a printf style format // so lets clear the string if (clear_printf) entry.printf_format.clear(); } } // Check for dereferences if (variable[0] == '*') { entry.deref = true; variable = variable.drop_front(); } error = ParseEntry(variable, &g_root, entry); if (error.Fail()) return error; if (verify_is_thread_id) { if (entry.type != Entry::Type::ThreadID && entry.type != Entry::Type::ThreadProtocolID) { error.SetErrorString("the 'tid' format can only be used on " "${thread.id} and ${thread.protocol_id}"); } } switch (entry.type) { case Entry::Type::Variable: case Entry::Type::VariableSynthetic: if (entry.number == 0) { if (entry.string.empty()) entry.number = ValueObject::eValueObjectRepresentationStyleValue; else entry.number = ValueObject::eValueObjectRepresentationStyleSummary; } break; default: // Make sure someone didn't try to dereference anything but ${var} // or ${svar} if (entry.deref) { error.SetErrorStringWithFormat( "${%s} can't be dereferenced, only ${var} and ${svar} can.", variable.str().c_str()); return error; } } // Check if this entry just wants to insert a constant string value // into the parent_entry, if so, insert the string with AppendText, // else append the entry to the parent_entry. if (entry.type == Entry::Type::InsertString) parent_entry.AppendText(entry.string.c_str()); else parent_entry.AppendEntry(std::move(entry)); } } break; } } return error; } Status FormatEntity::ExtractVariableInfo(llvm::StringRef &format_str, llvm::StringRef &variable_name, llvm::StringRef &variable_format) { Status error; variable_name = llvm::StringRef(); variable_format = llvm::StringRef(); const size_t paren_pos = format_str.find('}'); if (paren_pos != llvm::StringRef::npos) { const size_t percent_pos = format_str.find('%'); if (percent_pos < paren_pos) { if (percent_pos > 0) { if (percent_pos > 1) variable_name = format_str.substr(0, percent_pos); variable_format = format_str.substr(percent_pos + 1, paren_pos - (percent_pos + 1)); } } else { variable_name = format_str.substr(0, paren_pos); } // Strip off elements and the formatting and the trailing '}' format_str = format_str.substr(paren_pos + 1); } else { error.SetErrorStringWithFormat( "missing terminating '}' character for '${%s'", format_str.str().c_str()); } return error; } bool FormatEntity::FormatFileSpec(const FileSpec &file_spec, Stream &s, llvm::StringRef variable_name, llvm::StringRef variable_format) { if (variable_name.empty() || variable_name.equals(".fullpath")) { file_spec.Dump(&s); return true; } else if (variable_name.equals(".basename")) { s.PutCString(file_spec.GetFilename().AsCString("")); return true; } else if (variable_name.equals(".dirname")) { s.PutCString(file_spec.GetFilename().AsCString("")); return true; } return false; } static std::string MakeMatch(const llvm::StringRef &prefix, const char *suffix) { std::string match(prefix.str()); match.append(suffix); return match; } static void AddMatches(const FormatEntity::Entry::Definition *def, const llvm::StringRef &prefix, const llvm::StringRef &match_prefix, StringList &matches) { const size_t n = def->num_children; if (n > 0) { for (size_t i = 0; i < n; ++i) { std::string match = prefix.str(); if (match_prefix.empty()) matches.AppendString(MakeMatch(prefix, def->children[i].name)); else if (strncmp(def->children[i].name, match_prefix.data(), match_prefix.size()) == 0) matches.AppendString( MakeMatch(prefix, def->children[i].name + match_prefix.size())); } } } size_t FormatEntity::AutoComplete(CompletionRequest &request) { llvm::StringRef str = request.GetCursorArgumentPrefix().str(); request.SetWordComplete(false); str = str.drop_front(request.GetMatchStartPoint()); - request.GetMatches().Clear(); const size_t dollar_pos = str.rfind('$'); if (dollar_pos == llvm::StringRef::npos) return 0; // Hitting TAB after $ at the end of the string add a "{" if (dollar_pos == str.size() - 1) { std::string match = str.str(); match.append("{"); - request.GetMatches().AppendString(match); + request.AddCompletion(match); return 1; } if (str[dollar_pos + 1] != '{') return 0; const size_t close_pos = str.find('}', dollar_pos + 2); if (close_pos != llvm::StringRef::npos) return 0; const size_t format_pos = str.find('%', dollar_pos + 2); if (format_pos != llvm::StringRef::npos) return 0; llvm::StringRef partial_variable(str.substr(dollar_pos + 2)); if (partial_variable.empty()) { // Suggest all top level entites as we are just past "${" - AddMatches(&g_root, str, llvm::StringRef(), request.GetMatches()); - return request.GetMatches().GetSize(); + StringList new_matches; + AddMatches(&g_root, str, llvm::StringRef(), new_matches); + request.AddCompletions(new_matches); + return request.GetNumberOfMatches(); } // We have a partially specified variable, find it llvm::StringRef remainder; const FormatEntity::Entry::Definition *entry_def = FindEntry(partial_variable, &g_root, remainder); if (!entry_def) return 0; const size_t n = entry_def->num_children; if (remainder.empty()) { // Exact match if (n > 0) { // "${thread.info" - request.GetMatches().AppendString(MakeMatch(str, ".")); + request.AddCompletion(MakeMatch(str, ".")); } else { // "${thread.id" - request.GetMatches().AppendString(MakeMatch(str, "}")); + request.AddCompletion(MakeMatch(str, "}")); request.SetWordComplete(true); } } else if (remainder.equals(".")) { // "${thread." - AddMatches(entry_def, str, llvm::StringRef(), request.GetMatches()); + StringList new_matches; + AddMatches(entry_def, str, llvm::StringRef(), new_matches); + request.AddCompletions(new_matches); } else { // We have a partial match // "${thre" - AddMatches(entry_def, str, remainder, request.GetMatches()); + StringList new_matches; + AddMatches(entry_def, str, remainder, new_matches); + request.AddCompletions(new_matches); } - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } Index: vendor/lldb/dist/source/Core/IOHandler.cpp =================================================================== --- vendor/lldb/dist/source/Core/IOHandler.cpp (revision 337146) +++ vendor/lldb/dist/source/Core/IOHandler.cpp (revision 337147) @@ -1,4671 +1,4671 @@ //===-- IOHandler.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Core/IOHandler.h" // C Includes #ifndef LLDB_DISABLE_CURSES #include #include #endif // C++ Includes #if defined(__APPLE__) #include #endif #include // Other libraries and framework includes // Project includes #include "lldb/Core/Debugger.h" #include "lldb/Core/StreamFile.h" #include "lldb/Host/File.h" // for File #include "lldb/Host/Predicate.h" // for Predicate, ::eBroad... #include "lldb/Utility/Status.h" // for Status #include "lldb/Utility/StreamString.h" // for StreamString #include "lldb/Utility/StringList.h" // for StringList #include "lldb/lldb-forward.h" // for StreamFileSP #ifndef LLDB_DISABLE_LIBEDIT #include "lldb/Host/Editline.h" #endif #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" #ifndef LLDB_DISABLE_CURSES #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Module.h" #include "lldb/Core/State.h" #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectRegister.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #endif #include "llvm/ADT/StringRef.h" // for StringRef #ifdef _MSC_VER #include "lldb/Host/windows/windows.h" #endif #include // for shared_ptr #include // for recursive_mutex #include // for assert #include // for isspace #include // for EINTR, errno #include // for setlocale #include // for uint32_t, UINT32_MAX #include // for size_t, fprintf, feof #include // for strlen #include // for move using namespace lldb; using namespace lldb_private; IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type) : IOHandler(debugger, type, StreamFileSP(), // Adopt STDIN from top input reader StreamFileSP(), // Adopt STDOUT from top input reader StreamFileSP(), // Adopt STDERR from top input reader 0) // Flags {} IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type, const lldb::StreamFileSP &input_sp, const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp, uint32_t flags) : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp), m_error_sp(error_sp), m_popped(false), m_flags(flags), m_type(type), m_user_data(nullptr), m_done(false), m_active(false) { // If any files are not specified, then adopt them from the top input reader. if (!m_input_sp || !m_output_sp || !m_error_sp) debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_sp, m_output_sp, m_error_sp); } IOHandler::~IOHandler() = default; int IOHandler::GetInputFD() { return (m_input_sp ? m_input_sp->GetFile().GetDescriptor() : -1); } int IOHandler::GetOutputFD() { return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1); } int IOHandler::GetErrorFD() { return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1); } FILE *IOHandler::GetInputFILE() { return (m_input_sp ? m_input_sp->GetFile().GetStream() : nullptr); } FILE *IOHandler::GetOutputFILE() { return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr); } FILE *IOHandler::GetErrorFILE() { return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr); } StreamFileSP &IOHandler::GetInputStreamFile() { return m_input_sp; } StreamFileSP &IOHandler::GetOutputStreamFile() { return m_output_sp; } StreamFileSP &IOHandler::GetErrorStreamFile() { return m_error_sp; } bool IOHandler::GetIsInteractive() { return GetInputStreamFile()->GetFile().GetIsInteractive(); } bool IOHandler::GetIsRealTerminal() { return GetInputStreamFile()->GetFile().GetIsRealTerminal(); } void IOHandler::SetPopped(bool b) { m_popped.SetValue(b, eBroadcastOnChange); } void IOHandler::WaitForPop() { m_popped.WaitForValueEqualTo(true); } void IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len) { if (stream) { std::lock_guard guard(m_mutex); if (m_top) m_top->PrintAsync(stream, s, len); } } IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt, bool default_response) : IOHandlerEditline( debugger, IOHandler::Type::Confirm, nullptr, // nullptr editline_name means no history loaded/saved llvm::StringRef(), // No prompt llvm::StringRef(), // No continuation prompt false, // Multi-line false, // Don't colorize the prompt (i.e. the confirm message.) 0, *this), m_default_response(default_response), m_user_response(default_response) { StreamString prompt_stream; prompt_stream.PutCString(prompt); if (m_default_response) prompt_stream.Printf(": [Y/n] "); else prompt_stream.Printf(": [y/N] "); SetPrompt(prompt_stream.GetString()); } IOHandlerConfirm::~IOHandlerConfirm() = default; int IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler, const char *current_line, const char *cursor, const char *last_char, int skip_first_n_matches, int max_matches, StringList &matches) { if (current_line == cursor) { if (m_default_response) { matches.AppendString("y"); } else { matches.AppendString("n"); } } return matches.GetSize(); } void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler, std::string &line) { if (line.empty()) { // User just hit enter, set the response to the default m_user_response = m_default_response; io_handler.SetIsDone(true); return; } if (line.size() == 1) { switch (line[0]) { case 'y': case 'Y': m_user_response = true; io_handler.SetIsDone(true); return; case 'n': case 'N': m_user_response = false; io_handler.SetIsDone(true); return; default: break; } } if (line == "yes" || line == "YES" || line == "Yes") { m_user_response = true; io_handler.SetIsDone(true); } else if (line == "no" || line == "NO" || line == "No") { m_user_response = false; io_handler.SetIsDone(true); } } int IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler, const char *current_line, const char *cursor, const char *last_char, int skip_first_n_matches, int max_matches, StringList &matches) { switch (m_completion) { case Completion::None: break; case Completion::LLDBCommand: return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion( current_line, cursor, last_char, skip_first_n_matches, max_matches, matches); case Completion::Expression: { CompletionRequest request(current_line, current_line - cursor, skip_first_n_matches, max_matches, matches); CommandCompletions::InvokeCommonCompletionCallbacks( io_handler.GetDebugger().GetCommandInterpreter(), CommandCompletions::eVariablePathCompletion, request, nullptr); - size_t num_matches = request.GetMatches().GetSize(); + size_t num_matches = request.GetNumberOfMatches(); if (num_matches > 0) { std::string common_prefix; - request.GetMatches().LongestCommonPrefix(common_prefix); + matches.LongestCommonPrefix(common_prefix); const size_t partial_name_len = request.GetCursorArgumentPrefix().size(); // If we matched a unique single command, add a space... Only do this if // the completer told us this was a complete word, however... if (num_matches == 1 && request.GetWordComplete()) { common_prefix.push_back(' '); } common_prefix.erase(0, partial_name_len); matches.InsertStringAtIndex(0, std::move(common_prefix)); } return num_matches; } break; } return 0; } IOHandlerEditline::IOHandlerEditline( Debugger &debugger, IOHandler::Type type, const char *editline_name, // Used for saving history files llvm::StringRef prompt, llvm::StringRef continuation_prompt, bool multi_line, bool color_prompts, uint32_t line_number_start, IOHandlerDelegate &delegate) : IOHandlerEditline(debugger, type, StreamFileSP(), // Inherit input from top input reader StreamFileSP(), // Inherit output from top input reader StreamFileSP(), // Inherit error from top input reader 0, // Flags editline_name, // Used for saving history files prompt, continuation_prompt, multi_line, color_prompts, line_number_start, delegate) {} IOHandlerEditline::IOHandlerEditline( Debugger &debugger, IOHandler::Type type, const lldb::StreamFileSP &input_sp, const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp, uint32_t flags, const char *editline_name, // Used for saving history files llvm::StringRef prompt, llvm::StringRef continuation_prompt, bool multi_line, bool color_prompts, uint32_t line_number_start, IOHandlerDelegate &delegate) : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags), #ifndef LLDB_DISABLE_LIBEDIT m_editline_ap(), #endif m_delegate(delegate), m_prompt(), m_continuation_prompt(), m_current_lines_ptr(nullptr), m_base_line_number(line_number_start), m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line), m_color_prompts(color_prompts), m_interrupt_exits(true), m_editing(false) { SetPrompt(prompt); #ifndef LLDB_DISABLE_LIBEDIT bool use_editline = false; use_editline = m_input_sp->GetFile().GetIsRealTerminal(); if (use_editline) { m_editline_ap.reset(new Editline(editline_name, GetInputFILE(), GetOutputFILE(), GetErrorFILE(), m_color_prompts)); m_editline_ap->SetIsInputCompleteCallback(IsInputCompleteCallback, this); m_editline_ap->SetAutoCompleteCallback(AutoCompleteCallback, this); // See if the delegate supports fixing indentation const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters(); if (indent_chars) { // The delegate does support indentation, hook it up so when any // indentation character is typed, the delegate gets a chance to fix it m_editline_ap->SetFixIndentationCallback(FixIndentationCallback, this, indent_chars); } } #endif SetBaseLineNumber(m_base_line_number); SetPrompt(prompt); SetContinuationPrompt(continuation_prompt); } IOHandlerEditline::~IOHandlerEditline() { #ifndef LLDB_DISABLE_LIBEDIT m_editline_ap.reset(); #endif } void IOHandlerEditline::Activate() { IOHandler::Activate(); m_delegate.IOHandlerActivated(*this); } void IOHandlerEditline::Deactivate() { IOHandler::Deactivate(); m_delegate.IOHandlerDeactivated(*this); } bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) { #ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) { return m_editline_ap->GetLine(line, interrupted); } else { #endif line.clear(); FILE *in = GetInputFILE(); if (in) { if (GetIsInteractive()) { const char *prompt = nullptr; if (m_multi_line && m_curr_line_idx > 0) prompt = GetContinuationPrompt(); if (prompt == nullptr) prompt = GetPrompt(); if (prompt && prompt[0]) { FILE *out = GetOutputFILE(); if (out) { ::fprintf(out, "%s", prompt); ::fflush(out); } } } char buffer[256]; bool done = false; bool got_line = false; m_editing = true; while (!done) { if (fgets(buffer, sizeof(buffer), in) == nullptr) { const int saved_errno = errno; if (feof(in)) done = true; else if (ferror(in)) { if (saved_errno != EINTR) done = true; } } else { got_line = true; size_t buffer_len = strlen(buffer); assert(buffer[buffer_len] == '\0'); char last_char = buffer[buffer_len - 1]; if (last_char == '\r' || last_char == '\n') { done = true; // Strip trailing newlines while (last_char == '\r' || last_char == '\n') { --buffer_len; if (buffer_len == 0) break; last_char = buffer[buffer_len - 1]; } } line.append(buffer, buffer_len); } } m_editing = false; // We might have gotten a newline on a line by itself make sure to return // true in this case. return got_line; } else { // No more input file, we are done... SetIsDone(true); } return false; #ifndef LLDB_DISABLE_LIBEDIT } #endif } #ifndef LLDB_DISABLE_LIBEDIT bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline, StringList &lines, void *baton) { IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, lines); } int IOHandlerEditline::FixIndentationCallback(Editline *editline, const StringList &lines, int cursor_position, void *baton) { IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; return editline_reader->m_delegate.IOHandlerFixIndentation( *editline_reader, lines, cursor_position); } int IOHandlerEditline::AutoCompleteCallback(const char *current_line, const char *cursor, const char *last_char, int skip_first_n_matches, int max_matches, StringList &matches, void *baton) { IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; if (editline_reader) return editline_reader->m_delegate.IOHandlerComplete( *editline_reader, current_line, cursor, last_char, skip_first_n_matches, max_matches, matches); return 0; } #endif const char *IOHandlerEditline::GetPrompt() { #ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) { return m_editline_ap->GetPrompt(); } else { #endif if (m_prompt.empty()) return nullptr; #ifndef LLDB_DISABLE_LIBEDIT } #endif return m_prompt.c_str(); } bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) { m_prompt = prompt; #ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) m_editline_ap->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str()); #endif return true; } const char *IOHandlerEditline::GetContinuationPrompt() { return (m_continuation_prompt.empty() ? nullptr : m_continuation_prompt.c_str()); } void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) { m_continuation_prompt = prompt; #ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) m_editline_ap->SetContinuationPrompt(m_continuation_prompt.empty() ? nullptr : m_continuation_prompt.c_str()); #endif } void IOHandlerEditline::SetBaseLineNumber(uint32_t line) { m_base_line_number = line; } uint32_t IOHandlerEditline::GetCurrentLineIndex() const { #ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) return m_editline_ap->GetCurrentLine(); #endif return m_curr_line_idx; } bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) { m_current_lines_ptr = &lines; bool success = false; #ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) { return m_editline_ap->GetLines(m_base_line_number, lines, interrupted); } else { #endif bool done = false; Status error; while (!done) { // Show line numbers if we are asked to std::string line; if (m_base_line_number > 0 && GetIsInteractive()) { FILE *out = GetOutputFILE(); if (out) ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(), GetPrompt() == nullptr ? " " : ""); } m_curr_line_idx = lines.GetSize(); bool interrupted = false; if (GetLine(line, interrupted) && !interrupted) { lines.AppendString(line); done = m_delegate.IOHandlerIsInputComplete(*this, lines); } else { done = true; } } success = lines.GetSize() > 0; #ifndef LLDB_DISABLE_LIBEDIT } #endif return success; } // Each IOHandler gets to run until it is done. It should read data from the // "in" and place output into "out" and "err and return when done. void IOHandlerEditline::Run() { std::string line; while (IsActive()) { bool interrupted = false; if (m_multi_line) { StringList lines; if (GetLines(lines, interrupted)) { if (interrupted) { m_done = m_interrupt_exits; m_delegate.IOHandlerInputInterrupted(*this, line); } else { line = lines.CopyList(); m_delegate.IOHandlerInputComplete(*this, line); } } else { m_done = true; } } else { if (GetLine(line, interrupted)) { if (interrupted) m_delegate.IOHandlerInputInterrupted(*this, line); else m_delegate.IOHandlerInputComplete(*this, line); } else { m_done = true; } } } } void IOHandlerEditline::Cancel() { #ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) m_editline_ap->Cancel(); #endif } bool IOHandlerEditline::Interrupt() { // Let the delgate handle it first if (m_delegate.IOHandlerInterrupt(*this)) return true; #ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) return m_editline_ap->Interrupt(); #endif return false; } void IOHandlerEditline::GotEOF() { #ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) m_editline_ap->Interrupt(); #endif } void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) { #ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) m_editline_ap->PrintAsync(stream, s, len); else #endif { #ifdef _MSC_VER const char *prompt = GetPrompt(); if (prompt) { // Back up over previous prompt using Windows API CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info); COORD coord = screen_buffer_info.dwCursorPosition; coord.X -= strlen(prompt); if (coord.X < 0) coord.X = 0; SetConsoleCursorPosition(console_handle, coord); } #endif IOHandler::PrintAsync(stream, s, len); #ifdef _MSC_VER if (prompt) IOHandler::PrintAsync(GetOutputStreamFile().get(), prompt, strlen(prompt)); #endif } } // we may want curses to be disabled for some builds for instance, windows #ifndef LLDB_DISABLE_CURSES #define KEY_RETURN 10 #define KEY_ESCAPE 27 namespace curses { class Menu; class MenuDelegate; class Window; class WindowDelegate; typedef std::shared_ptr MenuSP; typedef std::shared_ptr MenuDelegateSP; typedef std::shared_ptr WindowSP; typedef std::shared_ptr WindowDelegateSP; typedef std::vector Menus; typedef std::vector Windows; typedef std::vector WindowDelegates; #if 0 type summary add -s "x=${var.x}, y=${var.y}" curses::Point type summary add -s "w=${var.width}, h=${var.height}" curses::Size type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect #endif struct Point { int x; int y; Point(int _x = 0, int _y = 0) : x(_x), y(_y) {} void Clear() { x = 0; y = 0; } Point &operator+=(const Point &rhs) { x += rhs.x; y += rhs.y; return *this; } void Dump() { printf("(x=%i, y=%i)\n", x, y); } }; bool operator==(const Point &lhs, const Point &rhs) { return lhs.x == rhs.x && lhs.y == rhs.y; } bool operator!=(const Point &lhs, const Point &rhs) { return lhs.x != rhs.x || lhs.y != rhs.y; } struct Size { int width; int height; Size(int w = 0, int h = 0) : width(w), height(h) {} void Clear() { width = 0; height = 0; } void Dump() { printf("(w=%i, h=%i)\n", width, height); } }; bool operator==(const Size &lhs, const Size &rhs) { return lhs.width == rhs.width && lhs.height == rhs.height; } bool operator!=(const Size &lhs, const Size &rhs) { return lhs.width != rhs.width || lhs.height != rhs.height; } struct Rect { Point origin; Size size; Rect() : origin(), size() {} Rect(const Point &p, const Size &s) : origin(p), size(s) {} void Clear() { origin.Clear(); size.Clear(); } void Dump() { printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, size.height); } void Inset(int w, int h) { if (size.width > w * 2) size.width -= w * 2; origin.x += w; if (size.height > h * 2) size.height -= h * 2; origin.y += h; } // Return a status bar rectangle which is the last line of this rectangle. // This rectangle will be modified to not include the status bar area. Rect MakeStatusBar() { Rect status_bar; if (size.height > 1) { status_bar.origin.x = origin.x; status_bar.origin.y = size.height; status_bar.size.width = size.width; status_bar.size.height = 1; --size.height; } return status_bar; } // Return a menubar rectangle which is the first line of this rectangle. This // rectangle will be modified to not include the menubar area. Rect MakeMenuBar() { Rect menubar; if (size.height > 1) { menubar.origin.x = origin.x; menubar.origin.y = origin.y; menubar.size.width = size.width; menubar.size.height = 1; ++origin.y; --size.height; } return menubar; } void HorizontalSplitPercentage(float top_percentage, Rect &top, Rect &bottom) const { float top_height = top_percentage * size.height; HorizontalSplit(top_height, top, bottom); } void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const { top = *this; if (top_height < size.height) { top.size.height = top_height; bottom.origin.x = origin.x; bottom.origin.y = origin.y + top.size.height; bottom.size.width = size.width; bottom.size.height = size.height - top.size.height; } else { bottom.Clear(); } } void VerticalSplitPercentage(float left_percentage, Rect &left, Rect &right) const { float left_width = left_percentage * size.width; VerticalSplit(left_width, left, right); } void VerticalSplit(int left_width, Rect &left, Rect &right) const { left = *this; if (left_width < size.width) { left.size.width = left_width; right.origin.x = origin.x + left.size.width; right.origin.y = origin.y; right.size.width = size.width - left.size.width; right.size.height = size.height; } else { right.Clear(); } } }; bool operator==(const Rect &lhs, const Rect &rhs) { return lhs.origin == rhs.origin && lhs.size == rhs.size; } bool operator!=(const Rect &lhs, const Rect &rhs) { return lhs.origin != rhs.origin || lhs.size != rhs.size; } enum HandleCharResult { eKeyNotHandled = 0, eKeyHandled = 1, eQuitApplication = 2 }; enum class MenuActionResult { Handled, NotHandled, Quit // Exit all menus and quit }; struct KeyHelp { int ch; const char *description; }; class WindowDelegate { public: virtual ~WindowDelegate() = default; virtual bool WindowDelegateDraw(Window &window, bool force) { return false; // Drawing not handled } virtual HandleCharResult WindowDelegateHandleChar(Window &window, int key) { return eKeyNotHandled; } virtual const char *WindowDelegateGetHelpText() { return nullptr; } virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; } }; class HelpDialogDelegate : public WindowDelegate { public: HelpDialogDelegate(const char *text, KeyHelp *key_help_array); ~HelpDialogDelegate() override; bool WindowDelegateDraw(Window &window, bool force) override; HandleCharResult WindowDelegateHandleChar(Window &window, int key) override; size_t GetNumLines() const { return m_text.GetSize(); } size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); } protected: StringList m_text; int m_first_visible_line; }; class Window { public: Window(const char *name) : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr), m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX), m_prev_active_window_idx(UINT32_MAX), m_delete(false), m_needs_update(true), m_can_activate(true), m_is_subwin(false) {} Window(const char *name, WINDOW *w, bool del = true) : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr), m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX), m_prev_active_window_idx(UINT32_MAX), m_delete(del), m_needs_update(true), m_can_activate(true), m_is_subwin(false) { if (w) Reset(w); } Window(const char *name, const Rect &bounds) : m_name(name), m_window(nullptr), m_parent(nullptr), m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX), m_prev_active_window_idx(UINT32_MAX), m_delete(true), m_needs_update(true), m_can_activate(true), m_is_subwin(false) { Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.y)); } virtual ~Window() { RemoveSubWindows(); Reset(); } void Reset(WINDOW *w = nullptr, bool del = true) { if (m_window == w) return; if (m_panel) { ::del_panel(m_panel); m_panel = nullptr; } if (m_window && m_delete) { ::delwin(m_window); m_window = nullptr; m_delete = false; } if (w) { m_window = w; m_panel = ::new_panel(m_window); m_delete = del; } } void AttributeOn(attr_t attr) { ::wattron(m_window, attr); } void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); } void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { ::box(m_window, v_char, h_char); } void Clear() { ::wclear(m_window); } void Erase() { ::werase(m_window); } Rect GetBounds() { return Rect(GetParentOrigin(), GetSize()); } // Get the rectangle in our parent window int GetChar() { return ::wgetch(m_window); } int GetCursorX() { return getcurx(m_window); } int GetCursorY() { return getcury(m_window); } Rect GetFrame() { return Rect(Point(), GetSize()); } // Get our rectangle in our own coordinate system Point GetParentOrigin() { return Point(GetParentX(), GetParentY()); } Size GetSize() { return Size(GetWidth(), GetHeight()); } int GetParentX() { return getparx(m_window); } int GetParentY() { return getpary(m_window); } int GetMaxX() { return getmaxx(m_window); } int GetMaxY() { return getmaxy(m_window); } int GetWidth() { return GetMaxX(); } int GetHeight() { return GetMaxY(); } void MoveCursor(int x, int y) { ::wmove(m_window, y, x); } void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); } void Resize(int w, int h) { ::wresize(m_window, h, w); } void Resize(const Size &size) { ::wresize(m_window, size.height, size.width); } void PutChar(int ch) { ::waddch(m_window, ch); } void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); } void Refresh() { ::wrefresh(m_window); } void DeferredRefresh() { // We are using panels, so we don't need to call this... //::wnoutrefresh(m_window); } void SetBackground(int color_pair_idx) { ::wbkgd(m_window, COLOR_PAIR(color_pair_idx)); } void UnderlineOn() { AttributeOn(A_UNDERLINE); } void UnderlineOff() { AttributeOff(A_UNDERLINE); } void PutCStringTruncated(const char *s, int right_pad) { int bytes_left = GetWidth() - GetCursorX(); if (bytes_left > right_pad) { bytes_left -= right_pad; ::waddnstr(m_window, s, bytes_left); } } void MoveWindow(const Point &origin) { const bool moving_window = origin != GetParentOrigin(); if (m_is_subwin && moving_window) { // Can't move subwindows, must delete and re-create Size size = GetSize(); Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y, origin.x), true); } else { ::mvwin(m_window, origin.y, origin.x); } } void SetBounds(const Rect &bounds) { const bool moving_window = bounds.origin != GetParentOrigin(); if (m_is_subwin && moving_window) { // Can't move subwindows, must delete and re-create Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.x), true); } else { if (moving_window) MoveWindow(bounds.origin); Resize(bounds.size); } } void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) { va_list args; va_start(args, format); vwprintw(m_window, format, args); va_end(args); } void Touch() { ::touchwin(m_window); if (m_parent) m_parent->Touch(); } WindowSP CreateSubWindow(const char *name, const Rect &bounds, bool make_active) { WindowSP subwindow_sp; if (m_window) { subwindow_sp.reset(new Window( name, ::subwin(m_window, bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.x), true)); subwindow_sp->m_is_subwin = true; } else { subwindow_sp.reset( new Window(name, ::newwin(bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.x), true)); subwindow_sp->m_is_subwin = false; } subwindow_sp->m_parent = this; if (make_active) { m_prev_active_window_idx = m_curr_active_window_idx; m_curr_active_window_idx = m_subwindows.size(); } m_subwindows.push_back(subwindow_sp); ::top_panel(subwindow_sp->m_panel); m_needs_update = true; return subwindow_sp; } bool RemoveSubWindow(Window *window) { Windows::iterator pos, end = m_subwindows.end(); size_t i = 0; for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) { if ((*pos).get() == window) { if (m_prev_active_window_idx == i) m_prev_active_window_idx = UINT32_MAX; else if (m_prev_active_window_idx != UINT32_MAX && m_prev_active_window_idx > i) --m_prev_active_window_idx; if (m_curr_active_window_idx == i) m_curr_active_window_idx = UINT32_MAX; else if (m_curr_active_window_idx != UINT32_MAX && m_curr_active_window_idx > i) --m_curr_active_window_idx; window->Erase(); m_subwindows.erase(pos); m_needs_update = true; if (m_parent) m_parent->Touch(); else ::touchwin(stdscr); return true; } } return false; } WindowSP FindSubWindow(const char *name) { Windows::iterator pos, end = m_subwindows.end(); size_t i = 0; for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) { if ((*pos)->m_name.compare(name) == 0) return *pos; } return WindowSP(); } void RemoveSubWindows() { m_curr_active_window_idx = UINT32_MAX; m_prev_active_window_idx = UINT32_MAX; for (Windows::iterator pos = m_subwindows.begin(); pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) { (*pos)->Erase(); } if (m_parent) m_parent->Touch(); else ::touchwin(stdscr); } WINDOW *get() { return m_window; } operator WINDOW *() { return m_window; } //---------------------------------------------------------------------- // Window drawing utilities //---------------------------------------------------------------------- void DrawTitleBox(const char *title, const char *bottom_message = nullptr) { attr_t attr = 0; if (IsActive()) attr = A_BOLD | COLOR_PAIR(2); else attr = 0; if (attr) AttributeOn(attr); Box(); MoveCursor(3, 0); if (title && title[0]) { PutChar('<'); PutCString(title); PutChar('>'); } if (bottom_message && bottom_message[0]) { int bottom_message_length = strlen(bottom_message); int x = GetWidth() - 3 - (bottom_message_length + 2); if (x > 0) { MoveCursor(x, GetHeight() - 1); PutChar('['); PutCString(bottom_message); PutChar(']'); } else { MoveCursor(1, GetHeight() - 1); PutChar('['); PutCStringTruncated(bottom_message, 1); } } if (attr) AttributeOff(attr); } virtual void Draw(bool force) { if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force)) return; for (auto &subwindow_sp : m_subwindows) subwindow_sp->Draw(force); } bool CreateHelpSubwindow() { if (m_delegate_sp) { const char *text = m_delegate_sp->WindowDelegateGetHelpText(); KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp(); if ((text && text[0]) || key_help) { std::unique_ptr help_delegate_ap( new HelpDialogDelegate(text, key_help)); const size_t num_lines = help_delegate_ap->GetNumLines(); const size_t max_length = help_delegate_ap->GetMaxLineLength(); Rect bounds = GetBounds(); bounds.Inset(1, 1); if (max_length + 4 < static_cast(bounds.size.width)) { bounds.origin.x += (bounds.size.width - max_length + 4) / 2; bounds.size.width = max_length + 4; } else { if (bounds.size.width > 100) { const int inset_w = bounds.size.width / 4; bounds.origin.x += inset_w; bounds.size.width -= 2 * inset_w; } } if (num_lines + 2 < static_cast(bounds.size.height)) { bounds.origin.y += (bounds.size.height - num_lines + 2) / 2; bounds.size.height = num_lines + 2; } else { if (bounds.size.height > 100) { const int inset_h = bounds.size.height / 4; bounds.origin.y += inset_h; bounds.size.height -= 2 * inset_h; } } WindowSP help_window_sp; Window *parent_window = GetParent(); if (parent_window) help_window_sp = parent_window->CreateSubWindow("Help", bounds, true); else help_window_sp = CreateSubWindow("Help", bounds, true); help_window_sp->SetDelegate( WindowDelegateSP(help_delegate_ap.release())); return true; } } return false; } virtual HandleCharResult HandleChar(int key) { // Always check the active window first HandleCharResult result = eKeyNotHandled; WindowSP active_window_sp = GetActiveWindow(); if (active_window_sp) { result = active_window_sp->HandleChar(key); if (result != eKeyNotHandled) return result; } if (m_delegate_sp) { result = m_delegate_sp->WindowDelegateHandleChar(*this, key); if (result != eKeyNotHandled) return result; } // Then check for any windows that want any keys that weren't handled. This // is typically only for a menubar. Make a copy of the subwindows in case // any HandleChar() functions muck with the subwindows. If we don't do // this, we can crash when iterating over the subwindows. Windows subwindows(m_subwindows); for (auto subwindow_sp : subwindows) { if (!subwindow_sp->m_can_activate) { HandleCharResult result = subwindow_sp->HandleChar(key); if (result != eKeyNotHandled) return result; } } return eKeyNotHandled; } bool SetActiveWindow(Window *window) { const size_t num_subwindows = m_subwindows.size(); for (size_t i = 0; i < num_subwindows; ++i) { if (m_subwindows[i].get() == window) { m_prev_active_window_idx = m_curr_active_window_idx; ::top_panel(window->m_panel); m_curr_active_window_idx = i; return true; } } return false; } WindowSP GetActiveWindow() { if (!m_subwindows.empty()) { if (m_curr_active_window_idx >= m_subwindows.size()) { if (m_prev_active_window_idx < m_subwindows.size()) { m_curr_active_window_idx = m_prev_active_window_idx; m_prev_active_window_idx = UINT32_MAX; } else if (IsActive()) { m_prev_active_window_idx = UINT32_MAX; m_curr_active_window_idx = UINT32_MAX; // Find first window that wants to be active if this window is active const size_t num_subwindows = m_subwindows.size(); for (size_t i = 0; i < num_subwindows; ++i) { if (m_subwindows[i]->GetCanBeActive()) { m_curr_active_window_idx = i; break; } } } } if (m_curr_active_window_idx < m_subwindows.size()) return m_subwindows[m_curr_active_window_idx]; } return WindowSP(); } bool GetCanBeActive() const { return m_can_activate; } void SetCanBeActive(bool b) { m_can_activate = b; } const WindowDelegateSP &GetDelegate() const { return m_delegate_sp; } void SetDelegate(const WindowDelegateSP &delegate_sp) { m_delegate_sp = delegate_sp; } Window *GetParent() const { return m_parent; } bool IsActive() const { if (m_parent) return m_parent->GetActiveWindow().get() == this; else return true; // Top level window is always active } void SelectNextWindowAsActive() { // Move active focus to next window const size_t num_subwindows = m_subwindows.size(); if (m_curr_active_window_idx == UINT32_MAX) { uint32_t idx = 0; for (auto subwindow_sp : m_subwindows) { if (subwindow_sp->GetCanBeActive()) { m_curr_active_window_idx = idx; break; } ++idx; } } else if (m_curr_active_window_idx + 1 < num_subwindows) { bool handled = false; m_prev_active_window_idx = m_curr_active_window_idx; for (size_t idx = m_curr_active_window_idx + 1; idx < num_subwindows; ++idx) { if (m_subwindows[idx]->GetCanBeActive()) { m_curr_active_window_idx = idx; handled = true; break; } } if (!handled) { for (size_t idx = 0; idx <= m_prev_active_window_idx; ++idx) { if (m_subwindows[idx]->GetCanBeActive()) { m_curr_active_window_idx = idx; break; } } } } else { m_prev_active_window_idx = m_curr_active_window_idx; for (size_t idx = 0; idx < num_subwindows; ++idx) { if (m_subwindows[idx]->GetCanBeActive()) { m_curr_active_window_idx = idx; break; } } } } const char *GetName() const { return m_name.c_str(); } protected: std::string m_name; WINDOW *m_window; PANEL *m_panel; Window *m_parent; Windows m_subwindows; WindowDelegateSP m_delegate_sp; uint32_t m_curr_active_window_idx; uint32_t m_prev_active_window_idx; bool m_delete; bool m_needs_update; bool m_can_activate; bool m_is_subwin; private: DISALLOW_COPY_AND_ASSIGN(Window); }; class MenuDelegate { public: virtual ~MenuDelegate() = default; virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0; }; class Menu : public WindowDelegate { public: enum class Type { Invalid, Bar, Item, Separator }; // Menubar or separator constructor Menu(Type type); // Menuitem constructor Menu(const char *name, const char *key_name, int key_value, uint64_t identifier); ~Menu() override = default; const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; } void SetDelegate(const MenuDelegateSP &delegate_sp) { m_delegate_sp = delegate_sp; } void RecalculateNameLengths(); void AddSubmenu(const MenuSP &menu_sp); int DrawAndRunMenu(Window &window); void DrawMenuTitle(Window &window, bool highlight); bool WindowDelegateDraw(Window &window, bool force) override; HandleCharResult WindowDelegateHandleChar(Window &window, int key) override; MenuActionResult ActionPrivate(Menu &menu) { MenuActionResult result = MenuActionResult::NotHandled; if (m_delegate_sp) { result = m_delegate_sp->MenuDelegateAction(menu); if (result != MenuActionResult::NotHandled) return result; } else if (m_parent) { result = m_parent->ActionPrivate(menu); if (result != MenuActionResult::NotHandled) return result; } return m_canned_result; } MenuActionResult Action() { // Call the recursive action so it can try to handle it with the menu // delegate, and if not, try our parent menu return ActionPrivate(*this); } void SetCannedResult(MenuActionResult result) { m_canned_result = result; } Menus &GetSubmenus() { return m_submenus; } const Menus &GetSubmenus() const { return m_submenus; } int GetSelectedSubmenuIndex() const { return m_selected; } void SetSelectedSubmenuIndex(int idx) { m_selected = idx; } Type GetType() const { return m_type; } int GetStartingColumn() const { return m_start_col; } void SetStartingColumn(int col) { m_start_col = col; } int GetKeyValue() const { return m_key_value; } void SetKeyValue(int key_value) { m_key_value = key_value; } std::string &GetName() { return m_name; } std::string &GetKeyName() { return m_key_name; } int GetDrawWidth() const { return m_max_submenu_name_length + m_max_submenu_key_name_length + 8; } uint64_t GetIdentifier() const { return m_identifier; } void SetIdentifier(uint64_t identifier) { m_identifier = identifier; } protected: std::string m_name; std::string m_key_name; uint64_t m_identifier; Type m_type; int m_key_value; int m_start_col; int m_max_submenu_name_length; int m_max_submenu_key_name_length; int m_selected; Menu *m_parent; Menus m_submenus; WindowSP m_menu_window_sp; MenuActionResult m_canned_result; MenuDelegateSP m_delegate_sp; }; // Menubar or separator constructor Menu::Menu(Type type) : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0), m_start_col(0), m_max_submenu_name_length(0), m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr), m_submenus(), m_canned_result(MenuActionResult::NotHandled), m_delegate_sp() {} // Menuitem constructor Menu::Menu(const char *name, const char *key_name, int key_value, uint64_t identifier) : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid), m_key_value(key_value), m_start_col(0), m_max_submenu_name_length(0), m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr), m_submenus(), m_canned_result(MenuActionResult::NotHandled), m_delegate_sp() { if (name && name[0]) { m_name = name; m_type = Type::Item; if (key_name && key_name[0]) m_key_name = key_name; } else { m_type = Type::Separator; } } void Menu::RecalculateNameLengths() { m_max_submenu_name_length = 0; m_max_submenu_key_name_length = 0; Menus &submenus = GetSubmenus(); const size_t num_submenus = submenus.size(); for (size_t i = 0; i < num_submenus; ++i) { Menu *submenu = submenus[i].get(); if (static_cast(m_max_submenu_name_length) < submenu->m_name.size()) m_max_submenu_name_length = submenu->m_name.size(); if (static_cast(m_max_submenu_key_name_length) < submenu->m_key_name.size()) m_max_submenu_key_name_length = submenu->m_key_name.size(); } } void Menu::AddSubmenu(const MenuSP &menu_sp) { menu_sp->m_parent = this; if (static_cast(m_max_submenu_name_length) < menu_sp->m_name.size()) m_max_submenu_name_length = menu_sp->m_name.size(); if (static_cast(m_max_submenu_key_name_length) < menu_sp->m_key_name.size()) m_max_submenu_key_name_length = menu_sp->m_key_name.size(); m_submenus.push_back(menu_sp); } void Menu::DrawMenuTitle(Window &window, bool highlight) { if (m_type == Type::Separator) { window.MoveCursor(0, window.GetCursorY()); window.PutChar(ACS_LTEE); int width = window.GetWidth(); if (width > 2) { width -= 2; for (int i = 0; i < width; ++i) window.PutChar(ACS_HLINE); } window.PutChar(ACS_RTEE); } else { const int shortcut_key = m_key_value; bool underlined_shortcut = false; const attr_t hilgight_attr = A_REVERSE; if (highlight) window.AttributeOn(hilgight_attr); if (isprint(shortcut_key)) { size_t lower_pos = m_name.find(tolower(shortcut_key)); size_t upper_pos = m_name.find(toupper(shortcut_key)); const char *name = m_name.c_str(); size_t pos = std::min(lower_pos, upper_pos); if (pos != std::string::npos) { underlined_shortcut = true; if (pos > 0) { window.PutCString(name, pos); name += pos; } const attr_t shortcut_attr = A_UNDERLINE | A_BOLD; window.AttributeOn(shortcut_attr); window.PutChar(name[0]); window.AttributeOff(shortcut_attr); name++; if (name[0]) window.PutCString(name); } } if (!underlined_shortcut) { window.PutCString(m_name.c_str()); } if (highlight) window.AttributeOff(hilgight_attr); if (m_key_name.empty()) { if (!underlined_shortcut && isprint(m_key_value)) { window.AttributeOn(COLOR_PAIR(3)); window.Printf(" (%c)", m_key_value); window.AttributeOff(COLOR_PAIR(3)); } } else { window.AttributeOn(COLOR_PAIR(3)); window.Printf(" (%s)", m_key_name.c_str()); window.AttributeOff(COLOR_PAIR(3)); } } } bool Menu::WindowDelegateDraw(Window &window, bool force) { Menus &submenus = GetSubmenus(); const size_t num_submenus = submenus.size(); const int selected_idx = GetSelectedSubmenuIndex(); Menu::Type menu_type = GetType(); switch (menu_type) { case Menu::Type::Bar: { window.SetBackground(2); window.MoveCursor(0, 0); for (size_t i = 0; i < num_submenus; ++i) { Menu *menu = submenus[i].get(); if (i > 0) window.PutChar(' '); menu->SetStartingColumn(window.GetCursorX()); window.PutCString("| "); menu->DrawMenuTitle(window, false); } window.PutCString(" |"); window.DeferredRefresh(); } break; case Menu::Type::Item: { int y = 1; int x = 3; // Draw the menu int cursor_x = 0; int cursor_y = 0; window.Erase(); window.SetBackground(2); window.Box(); for (size_t i = 0; i < num_submenus; ++i) { const bool is_selected = (i == static_cast(selected_idx)); window.MoveCursor(x, y + i); if (is_selected) { // Remember where we want the cursor to be cursor_x = x - 1; cursor_y = y + i; } submenus[i]->DrawMenuTitle(window, is_selected); } window.MoveCursor(cursor_x, cursor_y); window.DeferredRefresh(); } break; default: case Menu::Type::Separator: break; } return true; // Drawing handled... } HandleCharResult Menu::WindowDelegateHandleChar(Window &window, int key) { HandleCharResult result = eKeyNotHandled; Menus &submenus = GetSubmenus(); const size_t num_submenus = submenus.size(); const int selected_idx = GetSelectedSubmenuIndex(); Menu::Type menu_type = GetType(); if (menu_type == Menu::Type::Bar) { MenuSP run_menu_sp; switch (key) { case KEY_DOWN: case KEY_UP: // Show last menu or first menu if (selected_idx < static_cast(num_submenus)) run_menu_sp = submenus[selected_idx]; else if (!submenus.empty()) run_menu_sp = submenus.front(); result = eKeyHandled; break; case KEY_RIGHT: ++m_selected; if (m_selected >= static_cast(num_submenus)) m_selected = 0; if (m_selected < static_cast(num_submenus)) run_menu_sp = submenus[m_selected]; else if (!submenus.empty()) run_menu_sp = submenus.front(); result = eKeyHandled; break; case KEY_LEFT: --m_selected; if (m_selected < 0) m_selected = num_submenus - 1; if (m_selected < static_cast(num_submenus)) run_menu_sp = submenus[m_selected]; else if (!submenus.empty()) run_menu_sp = submenus.front(); result = eKeyHandled; break; default: for (size_t i = 0; i < num_submenus; ++i) { if (submenus[i]->GetKeyValue() == key) { SetSelectedSubmenuIndex(i); run_menu_sp = submenus[i]; result = eKeyHandled; break; } } break; } if (run_menu_sp) { // Run the action on this menu in case we need to populate the menu with // dynamic content and also in case check marks, and any other menu // decorations need to be calculated if (run_menu_sp->Action() == MenuActionResult::Quit) return eQuitApplication; Rect menu_bounds; menu_bounds.origin.x = run_menu_sp->GetStartingColumn(); menu_bounds.origin.y = 1; menu_bounds.size.width = run_menu_sp->GetDrawWidth(); menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2; if (m_menu_window_sp) window.GetParent()->RemoveSubWindow(m_menu_window_sp.get()); m_menu_window_sp = window.GetParent()->CreateSubWindow( run_menu_sp->GetName().c_str(), menu_bounds, true); m_menu_window_sp->SetDelegate(run_menu_sp); } } else if (menu_type == Menu::Type::Item) { switch (key) { case KEY_DOWN: if (m_submenus.size() > 1) { const int start_select = m_selected; while (++m_selected != start_select) { if (static_cast(m_selected) >= num_submenus) m_selected = 0; if (m_submenus[m_selected]->GetType() == Type::Separator) continue; else break; } return eKeyHandled; } break; case KEY_UP: if (m_submenus.size() > 1) { const int start_select = m_selected; while (--m_selected != start_select) { if (m_selected < static_cast(0)) m_selected = num_submenus - 1; if (m_submenus[m_selected]->GetType() == Type::Separator) continue; else break; } return eKeyHandled; } break; case KEY_RETURN: if (static_cast(selected_idx) < num_submenus) { if (submenus[selected_idx]->Action() == MenuActionResult::Quit) return eQuitApplication; window.GetParent()->RemoveSubWindow(&window); return eKeyHandled; } break; case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in // case other chars are entered for escaped sequences window.GetParent()->RemoveSubWindow(&window); return eKeyHandled; default: for (size_t i = 0; i < num_submenus; ++i) { Menu *menu = submenus[i].get(); if (menu->GetKeyValue() == key) { SetSelectedSubmenuIndex(i); window.GetParent()->RemoveSubWindow(&window); if (menu->Action() == MenuActionResult::Quit) return eQuitApplication; return eKeyHandled; } } break; } } else if (menu_type == Menu::Type::Separator) { } return result; } class Application { public: Application(FILE *in, FILE *out) : m_window_sp(), m_screen(nullptr), m_in(in), m_out(out) {} ~Application() { m_window_delegates.clear(); m_window_sp.reset(); if (m_screen) { ::delscreen(m_screen); m_screen = nullptr; } } void Initialize() { ::setlocale(LC_ALL, ""); ::setlocale(LC_CTYPE, ""); #if 0 ::initscr(); #else m_screen = ::newterm(nullptr, m_out, m_in); #endif ::start_color(); ::curs_set(0); ::noecho(); ::keypad(stdscr, TRUE); } void Terminate() { ::endwin(); } void Run(Debugger &debugger) { bool done = false; int delay_in_tenths_of_a_second = 1; // Alas the threading model in curses is a bit lame so we need to resort to // polling every 0.5 seconds. We could poll for stdin ourselves and then // pass the keys down but then we need to translate all of the escape // sequences ourselves. So we resort to polling for input because we need // to receive async process events while in this loop. halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths // of seconds seconds when calling // Window::GetChar() ListenerSP listener_sp( Listener::MakeListener("lldb.IOHandler.curses.Application")); ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass()); ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass()); ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass()); debugger.EnableForwardEvents(listener_sp); bool update = true; #if defined(__APPLE__) std::deque escape_chars; #endif while (!done) { if (update) { m_window_sp->Draw(false); // All windows should be calling Window::DeferredRefresh() instead of // Window::Refresh() so we can do a single update and avoid any screen // blinking update_panels(); // Cursor hiding isn't working on MacOSX, so hide it in the top left // corner m_window_sp->MoveCursor(0, 0); doupdate(); update = false; } #if defined(__APPLE__) // Terminal.app doesn't map its function keys correctly, F1-F4 default // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if // possible int ch; if (escape_chars.empty()) ch = m_window_sp->GetChar(); else { ch = escape_chars.front(); escape_chars.pop_front(); } if (ch == KEY_ESCAPE) { int ch2 = m_window_sp->GetChar(); if (ch2 == 'O') { int ch3 = m_window_sp->GetChar(); switch (ch3) { case 'P': ch = KEY_F(1); break; case 'Q': ch = KEY_F(2); break; case 'R': ch = KEY_F(3); break; case 'S': ch = KEY_F(4); break; default: escape_chars.push_back(ch2); if (ch3 != -1) escape_chars.push_back(ch3); break; } } else if (ch2 != -1) escape_chars.push_back(ch2); } #else int ch = m_window_sp->GetChar(); #endif if (ch == -1) { if (feof(m_in) || ferror(m_in)) { done = true; } else { // Just a timeout from using halfdelay(), check for events EventSP event_sp; while (listener_sp->PeekAtNextEvent()) { listener_sp->GetEvent(event_sp, std::chrono::seconds(0)); if (event_sp) { Broadcaster *broadcaster = event_sp->GetBroadcaster(); if (broadcaster) { // uint32_t event_type = event_sp->GetType(); ConstString broadcaster_class( broadcaster->GetBroadcasterClass()); if (broadcaster_class == broadcaster_class_process) { debugger.GetCommandInterpreter().UpdateExecutionContext( nullptr); update = true; continue; // Don't get any key, just update our view } } } } } } else { HandleCharResult key_result = m_window_sp->HandleChar(ch); switch (key_result) { case eKeyHandled: debugger.GetCommandInterpreter().UpdateExecutionContext(nullptr); update = true; break; case eKeyNotHandled: break; case eQuitApplication: done = true; break; } } } debugger.CancelForwardEvents(listener_sp); } WindowSP &GetMainWindow() { if (!m_window_sp) m_window_sp.reset(new Window("main", stdscr, false)); return m_window_sp; } WindowDelegates &GetWindowDelegates() { return m_window_delegates; } protected: WindowSP m_window_sp; WindowDelegates m_window_delegates; SCREEN *m_screen; FILE *m_in; FILE *m_out; }; } // namespace curses using namespace curses; struct Row { ValueObjectManager value; Row *parent; // The process stop ID when the children were calculated. uint32_t children_stop_id; int row_idx; int x; int y; bool might_have_children; bool expanded; bool calculated_children; std::vector children; Row(const ValueObjectSP &v, Row *p) : value(v, lldb::eDynamicDontRunTarget, true), parent(p), row_idx(0), x(1), y(1), might_have_children(v ? v->MightHaveChildren() : false), expanded(false), calculated_children(false), children() {} size_t GetDepth() const { if (parent) return 1 + parent->GetDepth(); return 0; } void Expand() { expanded = true; } std::vector &GetChildren() { ProcessSP process_sp = value.GetProcessSP(); auto stop_id = process_sp->GetStopID(); if (process_sp && stop_id != children_stop_id) { children_stop_id = stop_id; calculated_children = false; } if (!calculated_children) { children.clear(); calculated_children = true; ValueObjectSP valobj = value.GetSP(); if (valobj) { const size_t num_children = valobj->GetNumChildren(); for (size_t i = 0; i < num_children; ++i) { children.push_back(Row(valobj->GetChildAtIndex(i, true), this)); } } } return children; } void Unexpand() { expanded = false; calculated_children = false; children.clear(); } void DrawTree(Window &window) { if (parent) parent->DrawTreeForChild(window, this, 0); if (might_have_children) { // It we can get UTF8 characters to work we should try to use the // "symbol" UTF8 string below // const char *symbol = ""; // if (row.expanded) // symbol = "\xe2\x96\xbd "; // else // symbol = "\xe2\x96\xb7 "; // window.PutCString (symbol); // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v' // or '>' character... // if (expanded) // window.PutChar (ACS_DARROW); // else // window.PutChar (ACS_RARROW); // Since we can't find any good looking right arrow/down arrow symbols, // just use a diamond... window.PutChar(ACS_DIAMOND); window.PutChar(ACS_HLINE); } } void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) { if (parent) parent->DrawTreeForChild(window, this, reverse_depth + 1); if (&GetChildren().back() == child) { // Last child if (reverse_depth == 0) { window.PutChar(ACS_LLCORNER); window.PutChar(ACS_HLINE); } else { window.PutChar(' '); window.PutChar(' '); } } else { if (reverse_depth == 0) { window.PutChar(ACS_LTEE); window.PutChar(ACS_HLINE); } else { window.PutChar(ACS_VLINE); window.PutChar(' '); } } } }; struct DisplayOptions { bool show_types; }; class TreeItem; class TreeDelegate { public: TreeDelegate() = default; virtual ~TreeDelegate() = default; virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0; virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0; virtual bool TreeDelegateItemSelected( TreeItem &item) = 0; // Return true if we need to update views }; typedef std::shared_ptr TreeDelegateSP; class TreeItem { public: TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children) : m_parent(parent), m_delegate(delegate), m_user_data(nullptr), m_identifier(0), m_row_idx(-1), m_children(), m_might_have_children(might_have_children), m_is_expanded(false) {} TreeItem &operator=(const TreeItem &rhs) { if (this != &rhs) { m_parent = rhs.m_parent; m_delegate = rhs.m_delegate; m_user_data = rhs.m_user_data; m_identifier = rhs.m_identifier; m_row_idx = rhs.m_row_idx; m_children = rhs.m_children; m_might_have_children = rhs.m_might_have_children; m_is_expanded = rhs.m_is_expanded; } return *this; } size_t GetDepth() const { if (m_parent) return 1 + m_parent->GetDepth(); return 0; } int GetRowIndex() const { return m_row_idx; } void ClearChildren() { m_children.clear(); } void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); } TreeItem &operator[](size_t i) { return m_children[i]; } void SetRowIndex(int row_idx) { m_row_idx = row_idx; } size_t GetNumChildren() { m_delegate.TreeDelegateGenerateChildren(*this); return m_children.size(); } void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); } void CalculateRowIndexes(int &row_idx) { SetRowIndex(row_idx); ++row_idx; const bool expanded = IsExpanded(); // The root item must calculate its children, or we must calculate the // number of children if the item is expanded if (m_parent == nullptr || expanded) GetNumChildren(); for (auto &item : m_children) { if (expanded) item.CalculateRowIndexes(row_idx); else item.SetRowIndex(-1); } } TreeItem *GetParent() { return m_parent; } bool IsExpanded() const { return m_is_expanded; } void Expand() { m_is_expanded = true; } void Unexpand() { m_is_expanded = false; } bool Draw(Window &window, const int first_visible_row, const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) { if (num_rows_left <= 0) return false; if (m_row_idx >= first_visible_row) { window.MoveCursor(2, row_idx + 1); if (m_parent) m_parent->DrawTreeForChild(window, this, 0); if (m_might_have_children) { // It we can get UTF8 characters to work we should try to use the // "symbol" UTF8 string below // const char *symbol = ""; // if (row.expanded) // symbol = "\xe2\x96\xbd "; // else // symbol = "\xe2\x96\xb7 "; // window.PutCString (symbol); // The ACS_DARROW and ACS_RARROW don't look very nice they are just a // 'v' or '>' character... // if (expanded) // window.PutChar (ACS_DARROW); // else // window.PutChar (ACS_RARROW); // Since we can't find any good looking right arrow/down arrow symbols, // just use a diamond... window.PutChar(ACS_DIAMOND); window.PutChar(ACS_HLINE); } bool highlight = (selected_row_idx == static_cast(m_row_idx)) && window.IsActive(); if (highlight) window.AttributeOn(A_REVERSE); m_delegate.TreeDelegateDrawTreeItem(*this, window); if (highlight) window.AttributeOff(A_REVERSE); ++row_idx; --num_rows_left; } if (num_rows_left <= 0) return false; // We are done drawing... if (IsExpanded()) { for (auto &item : m_children) { // If we displayed all the rows and item.Draw() returns false we are // done drawing and can exit this for loop if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx, num_rows_left)) break; } } return num_rows_left >= 0; // Return true if not done drawing yet } void DrawTreeForChild(Window &window, TreeItem *child, uint32_t reverse_depth) { if (m_parent) m_parent->DrawTreeForChild(window, this, reverse_depth + 1); if (&m_children.back() == child) { // Last child if (reverse_depth == 0) { window.PutChar(ACS_LLCORNER); window.PutChar(ACS_HLINE); } else { window.PutChar(' '); window.PutChar(' '); } } else { if (reverse_depth == 0) { window.PutChar(ACS_LTEE); window.PutChar(ACS_HLINE); } else { window.PutChar(ACS_VLINE); window.PutChar(' '); } } } TreeItem *GetItemForRowIndex(uint32_t row_idx) { if (static_cast(m_row_idx) == row_idx) return this; if (m_children.empty()) return nullptr; if (IsExpanded()) { for (auto &item : m_children) { TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx); if (selected_item_ptr) return selected_item_ptr; } } return nullptr; } void *GetUserData() const { return m_user_data; } void SetUserData(void *user_data) { m_user_data = user_data; } uint64_t GetIdentifier() const { return m_identifier; } void SetIdentifier(uint64_t identifier) { m_identifier = identifier; } void SetMightHaveChildren(bool b) { m_might_have_children = b; } protected: TreeItem *m_parent; TreeDelegate &m_delegate; void *m_user_data; uint64_t m_identifier; int m_row_idx; // Zero based visible row index, -1 if not visible or for the // root item std::vector m_children; bool m_might_have_children; bool m_is_expanded; }; class TreeWindowDelegate : public WindowDelegate { public: TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp) : m_debugger(debugger), m_delegate_sp(delegate_sp), m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr), m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0), m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {} int NumVisibleRows() const { return m_max_y - m_min_y; } bool WindowDelegateDraw(Window &window, bool force) override { ExecutionContext exe_ctx( m_debugger.GetCommandInterpreter().GetExecutionContext()); Process *process = exe_ctx.GetProcessPtr(); bool display_content = false; if (process) { StateType state = process->GetState(); if (StateIsStoppedState(state, true)) { // We are stopped, so it is ok to display_content = true; } else if (StateIsRunningState(state)) { return true; // Don't do any updating when we are running } } m_min_x = 2; m_min_y = 1; m_max_x = window.GetWidth() - 1; m_max_y = window.GetHeight() - 1; window.Erase(); window.DrawTitleBox(window.GetName()); if (display_content) { const int num_visible_rows = NumVisibleRows(); m_num_rows = 0; m_root.CalculateRowIndexes(m_num_rows); // If we unexpanded while having something selected our total number of // rows is less than the num visible rows, then make sure we show all the // rows by setting the first visible row accordingly. if (m_first_visible_row > 0 && m_num_rows < num_visible_rows) m_first_visible_row = 0; // Make sure the selected row is always visible if (m_selected_row_idx < m_first_visible_row) m_first_visible_row = m_selected_row_idx; else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx) m_first_visible_row = m_selected_row_idx - num_visible_rows + 1; int row_idx = 0; int num_rows_left = num_visible_rows; m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx, num_rows_left); // Get the selected row m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); } else { m_selected_item = nullptr; } window.DeferredRefresh(); return true; // Drawing handled } const char *WindowDelegateGetHelpText() override { return "Thread window keyboard shortcuts:"; } KeyHelp *WindowDelegateGetKeyHelp() override { static curses::KeyHelp g_source_view_key_help[] = { {KEY_UP, "Select previous item"}, {KEY_DOWN, "Select next item"}, {KEY_RIGHT, "Expand the selected item"}, {KEY_LEFT, "Unexpand the selected item or select parent if not expanded"}, {KEY_PPAGE, "Page up"}, {KEY_NPAGE, "Page down"}, {'h', "Show help dialog"}, {' ', "Toggle item expansion"}, {',', "Page up"}, {'.', "Page down"}, {'\0', nullptr}}; return g_source_view_key_help; } HandleCharResult WindowDelegateHandleChar(Window &window, int c) override { switch (c) { case ',': case KEY_PPAGE: // Page up key if (m_first_visible_row > 0) { if (m_first_visible_row > m_max_y) m_first_visible_row -= m_max_y; else m_first_visible_row = 0; m_selected_row_idx = m_first_visible_row; m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); if (m_selected_item) m_selected_item->ItemWasSelected(); } return eKeyHandled; case '.': case KEY_NPAGE: // Page down key if (m_num_rows > m_max_y) { if (m_first_visible_row + m_max_y < m_num_rows) { m_first_visible_row += m_max_y; m_selected_row_idx = m_first_visible_row; m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); if (m_selected_item) m_selected_item->ItemWasSelected(); } } return eKeyHandled; case KEY_UP: if (m_selected_row_idx > 0) { --m_selected_row_idx; m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); if (m_selected_item) m_selected_item->ItemWasSelected(); } return eKeyHandled; case KEY_DOWN: if (m_selected_row_idx + 1 < m_num_rows) { ++m_selected_row_idx; m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); if (m_selected_item) m_selected_item->ItemWasSelected(); } return eKeyHandled; case KEY_RIGHT: if (m_selected_item) { if (!m_selected_item->IsExpanded()) m_selected_item->Expand(); } return eKeyHandled; case KEY_LEFT: if (m_selected_item) { if (m_selected_item->IsExpanded()) m_selected_item->Unexpand(); else if (m_selected_item->GetParent()) { m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex(); m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx); if (m_selected_item) m_selected_item->ItemWasSelected(); } } return eKeyHandled; case ' ': // Toggle expansion state when SPACE is pressed if (m_selected_item) { if (m_selected_item->IsExpanded()) m_selected_item->Unexpand(); else m_selected_item->Expand(); } return eKeyHandled; case 'h': window.CreateHelpSubwindow(); return eKeyHandled; default: break; } return eKeyNotHandled; } protected: Debugger &m_debugger; TreeDelegateSP m_delegate_sp; TreeItem m_root; TreeItem *m_selected_item; int m_num_rows; int m_selected_row_idx; int m_first_visible_row; int m_min_x; int m_min_y; int m_max_x; int m_max_y; }; class FrameTreeDelegate : public TreeDelegate { public: FrameTreeDelegate() : TreeDelegate() { FormatEntity::Parse( "frame #${frame.index}: {${function.name}${function.pc-offset}}}", m_format); } ~FrameTreeDelegate() override = default; void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { Thread *thread = (Thread *)item.GetUserData(); if (thread) { const uint64_t frame_idx = item.GetIdentifier(); StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx); if (frame_sp) { StreamString strm; const SymbolContext &sc = frame_sp->GetSymbolContext(eSymbolContextEverything); ExecutionContext exe_ctx(frame_sp); if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr, nullptr, false, false)) { int right_pad = 1; window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad); } } } } void TreeDelegateGenerateChildren(TreeItem &item) override { // No children for frames yet... } bool TreeDelegateItemSelected(TreeItem &item) override { Thread *thread = (Thread *)item.GetUserData(); if (thread) { thread->GetProcess()->GetThreadList().SetSelectedThreadByID( thread->GetID()); const uint64_t frame_idx = item.GetIdentifier(); thread->SetSelectedFrameByIndex(frame_idx); return true; } return false; } protected: FormatEntity::Entry m_format; }; class ThreadTreeDelegate : public TreeDelegate { public: ThreadTreeDelegate(Debugger &debugger) : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID), m_stop_id(UINT32_MAX) { FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop " "reason = ${thread.stop-reason}}", m_format); } ~ThreadTreeDelegate() override = default; ProcessSP GetProcess() { return m_debugger.GetCommandInterpreter() .GetExecutionContext() .GetProcessSP(); } ThreadSP GetThread(const TreeItem &item) { ProcessSP process_sp = GetProcess(); if (process_sp) return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier()); return ThreadSP(); } void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { ThreadSP thread_sp = GetThread(item); if (thread_sp) { StreamString strm; ExecutionContext exe_ctx(thread_sp); if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr, nullptr, false, false)) { int right_pad = 1; window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad); } } } void TreeDelegateGenerateChildren(TreeItem &item) override { ProcessSP process_sp = GetProcess(); if (process_sp && process_sp->IsAlive()) { StateType state = process_sp->GetState(); if (StateIsStoppedState(state, true)) { ThreadSP thread_sp = GetThread(item); if (thread_sp) { if (m_stop_id == process_sp->GetStopID() && thread_sp->GetID() == m_tid) return; // Children are already up to date if (!m_frame_delegate_sp) { // Always expand the thread item the first time we show it m_frame_delegate_sp.reset(new FrameTreeDelegate()); } m_stop_id = process_sp->GetStopID(); m_tid = thread_sp->GetID(); TreeItem t(&item, *m_frame_delegate_sp, false); size_t num_frames = thread_sp->GetStackFrameCount(); item.Resize(num_frames, t); for (size_t i = 0; i < num_frames; ++i) { item[i].SetUserData(thread_sp.get()); item[i].SetIdentifier(i); } } return; } } item.ClearChildren(); } bool TreeDelegateItemSelected(TreeItem &item) override { ProcessSP process_sp = GetProcess(); if (process_sp && process_sp->IsAlive()) { StateType state = process_sp->GetState(); if (StateIsStoppedState(state, true)) { ThreadSP thread_sp = GetThread(item); if (thread_sp) { ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList(); std::lock_guard guard(thread_list.GetMutex()); ThreadSP selected_thread_sp = thread_list.GetSelectedThread(); if (selected_thread_sp->GetID() != thread_sp->GetID()) { thread_list.SetSelectedThreadByID(thread_sp->GetID()); return true; } } } } return false; } protected: Debugger &m_debugger; std::shared_ptr m_frame_delegate_sp; lldb::user_id_t m_tid; uint32_t m_stop_id; FormatEntity::Entry m_format; }; class ThreadsTreeDelegate : public TreeDelegate { public: ThreadsTreeDelegate(Debugger &debugger) : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger), m_stop_id(UINT32_MAX) { FormatEntity::Parse("process ${process.id}{, name = ${process.name}}", m_format); } ~ThreadsTreeDelegate() override = default; ProcessSP GetProcess() { return m_debugger.GetCommandInterpreter() .GetExecutionContext() .GetProcessSP(); } void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override { ProcessSP process_sp = GetProcess(); if (process_sp && process_sp->IsAlive()) { StreamString strm; ExecutionContext exe_ctx(process_sp); if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr, nullptr, false, false)) { int right_pad = 1; window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad); } } } void TreeDelegateGenerateChildren(TreeItem &item) override { ProcessSP process_sp = GetProcess(); if (process_sp && process_sp->IsAlive()) { StateType state = process_sp->GetState(); if (StateIsStoppedState(state, true)) { const uint32_t stop_id = process_sp->GetStopID(); if (m_stop_id == stop_id) return; // Children are already up to date m_stop_id = stop_id; if (!m_thread_delegate_sp) { // Always expand the thread item the first time we show it // item.Expand(); m_thread_delegate_sp.reset(new ThreadTreeDelegate(m_debugger)); } TreeItem t(&item, *m_thread_delegate_sp, false); ThreadList &threads = process_sp->GetThreadList(); std::lock_guard guard(threads.GetMutex()); size_t num_threads = threads.GetSize(); item.Resize(num_threads, t); for (size_t i = 0; i < num_threads; ++i) { item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID()); item[i].SetMightHaveChildren(true); } return; } } item.ClearChildren(); } bool TreeDelegateItemSelected(TreeItem &item) override { return false; } protected: std::shared_ptr m_thread_delegate_sp; Debugger &m_debugger; uint32_t m_stop_id; FormatEntity::Entry m_format; }; class ValueObjectListDelegate : public WindowDelegate { public: ValueObjectListDelegate() : m_rows(), m_selected_row(nullptr), m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0), m_max_x(0), m_max_y(0) {} ValueObjectListDelegate(ValueObjectList &valobj_list) : m_rows(), m_selected_row(nullptr), m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0), m_max_x(0), m_max_y(0) { SetValues(valobj_list); } ~ValueObjectListDelegate() override = default; void SetValues(ValueObjectList &valobj_list) { m_selected_row = nullptr; m_selected_row_idx = 0; m_first_visible_row = 0; m_num_rows = 0; m_rows.clear(); for (auto &valobj_sp : valobj_list.GetObjects()) m_rows.push_back(Row(valobj_sp, nullptr)); } bool WindowDelegateDraw(Window &window, bool force) override { m_num_rows = 0; m_min_x = 2; m_min_y = 1; m_max_x = window.GetWidth() - 1; m_max_y = window.GetHeight() - 1; window.Erase(); window.DrawTitleBox(window.GetName()); const int num_visible_rows = NumVisibleRows(); const int num_rows = CalculateTotalNumberRows(m_rows); // If we unexpanded while having something selected our total number of // rows is less than the num visible rows, then make sure we show all the // rows by setting the first visible row accordingly. if (m_first_visible_row > 0 && num_rows < num_visible_rows) m_first_visible_row = 0; // Make sure the selected row is always visible if (m_selected_row_idx < m_first_visible_row) m_first_visible_row = m_selected_row_idx; else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx) m_first_visible_row = m_selected_row_idx - num_visible_rows + 1; DisplayRows(window, m_rows, g_options); window.DeferredRefresh(); // Get the selected row m_selected_row = GetRowForRowIndex(m_selected_row_idx); // Keep the cursor on the selected row so the highlight and the cursor are // always on the same line if (m_selected_row) window.MoveCursor(m_selected_row->x, m_selected_row->y); return true; // Drawing handled } KeyHelp *WindowDelegateGetKeyHelp() override { static curses::KeyHelp g_source_view_key_help[] = { {KEY_UP, "Select previous item"}, {KEY_DOWN, "Select next item"}, {KEY_RIGHT, "Expand selected item"}, {KEY_LEFT, "Unexpand selected item or select parent if not expanded"}, {KEY_PPAGE, "Page up"}, {KEY_NPAGE, "Page down"}, {'A', "Format as annotated address"}, {'b', "Format as binary"}, {'B', "Format as hex bytes with ASCII"}, {'c', "Format as character"}, {'d', "Format as a signed integer"}, {'D', "Format selected value using the default format for the type"}, {'f', "Format as float"}, {'h', "Show help dialog"}, {'i', "Format as instructions"}, {'o', "Format as octal"}, {'p', "Format as pointer"}, {'s', "Format as C string"}, {'t', "Toggle showing/hiding type names"}, {'u', "Format as an unsigned integer"}, {'x', "Format as hex"}, {'X', "Format as uppercase hex"}, {' ', "Toggle item expansion"}, {',', "Page up"}, {'.', "Page down"}, {'\0', nullptr}}; return g_source_view_key_help; } HandleCharResult WindowDelegateHandleChar(Window &window, int c) override { switch (c) { case 'x': case 'X': case 'o': case 's': case 'u': case 'd': case 'D': case 'i': case 'A': case 'p': case 'c': case 'b': case 'B': case 'f': // Change the format for the currently selected item if (m_selected_row) { auto valobj_sp = m_selected_row->value.GetSP(); if (valobj_sp) valobj_sp->SetFormat(FormatForChar(c)); } return eKeyHandled; case 't': // Toggle showing type names g_options.show_types = !g_options.show_types; return eKeyHandled; case ',': case KEY_PPAGE: // Page up key if (m_first_visible_row > 0) { if (static_cast(m_first_visible_row) > m_max_y) m_first_visible_row -= m_max_y; else m_first_visible_row = 0; m_selected_row_idx = m_first_visible_row; } return eKeyHandled; case '.': case KEY_NPAGE: // Page down key if (m_num_rows > static_cast(m_max_y)) { if (m_first_visible_row + m_max_y < m_num_rows) { m_first_visible_row += m_max_y; m_selected_row_idx = m_first_visible_row; } } return eKeyHandled; case KEY_UP: if (m_selected_row_idx > 0) --m_selected_row_idx; return eKeyHandled; case KEY_DOWN: if (m_selected_row_idx + 1 < m_num_rows) ++m_selected_row_idx; return eKeyHandled; case KEY_RIGHT: if (m_selected_row) { if (!m_selected_row->expanded) m_selected_row->Expand(); } return eKeyHandled; case KEY_LEFT: if (m_selected_row) { if (m_selected_row->expanded) m_selected_row->Unexpand(); else if (m_selected_row->parent) m_selected_row_idx = m_selected_row->parent->row_idx; } return eKeyHandled; case ' ': // Toggle expansion state when SPACE is pressed if (m_selected_row) { if (m_selected_row->expanded) m_selected_row->Unexpand(); else m_selected_row->Expand(); } return eKeyHandled; case 'h': window.CreateHelpSubwindow(); return eKeyHandled; default: break; } return eKeyNotHandled; } protected: std::vector m_rows; Row *m_selected_row; uint32_t m_selected_row_idx; uint32_t m_first_visible_row; uint32_t m_num_rows; int m_min_x; int m_min_y; int m_max_x; int m_max_y; static Format FormatForChar(int c) { switch (c) { case 'x': return eFormatHex; case 'X': return eFormatHexUppercase; case 'o': return eFormatOctal; case 's': return eFormatCString; case 'u': return eFormatUnsigned; case 'd': return eFormatDecimal; case 'D': return eFormatDefault; case 'i': return eFormatInstruction; case 'A': return eFormatAddressInfo; case 'p': return eFormatPointer; case 'c': return eFormatChar; case 'b': return eFormatBinary; case 'B': return eFormatBytesWithASCII; case 'f': return eFormatFloat; } return eFormatDefault; } bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options, bool highlight, bool last_child) { ValueObject *valobj = row.value.GetSP().get(); if (valobj == nullptr) return false; const char *type_name = options.show_types ? valobj->GetTypeName().GetCString() : nullptr; const char *name = valobj->GetName().GetCString(); const char *value = valobj->GetValueAsCString(); const char *summary = valobj->GetSummaryAsCString(); window.MoveCursor(row.x, row.y); row.DrawTree(window); if (highlight) window.AttributeOn(A_REVERSE); if (type_name && type_name[0]) window.Printf("(%s) ", type_name); if (name && name[0]) window.PutCString(name); attr_t changd_attr = 0; if (valobj->GetValueDidChange()) changd_attr = COLOR_PAIR(5) | A_BOLD; if (value && value[0]) { window.PutCString(" = "); if (changd_attr) window.AttributeOn(changd_attr); window.PutCString(value); if (changd_attr) window.AttributeOff(changd_attr); } if (summary && summary[0]) { window.PutChar(' '); if (changd_attr) window.AttributeOn(changd_attr); window.PutCString(summary); if (changd_attr) window.AttributeOff(changd_attr); } if (highlight) window.AttributeOff(A_REVERSE); return true; } void DisplayRows(Window &window, std::vector &rows, DisplayOptions &options) { // > 0x25B7 // \/ 0x25BD bool window_is_active = window.IsActive(); for (auto &row : rows) { const bool last_child = row.parent && &rows[rows.size() - 1] == &row; // Save the row index in each Row structure row.row_idx = m_num_rows; if ((m_num_rows >= m_first_visible_row) && ((m_num_rows - m_first_visible_row) < static_cast(NumVisibleRows()))) { row.x = m_min_x; row.y = m_num_rows - m_first_visible_row + 1; if (DisplayRowObject(window, row, options, window_is_active && m_num_rows == m_selected_row_idx, last_child)) { ++m_num_rows; } else { row.x = 0; row.y = 0; } } else { row.x = 0; row.y = 0; ++m_num_rows; } auto &children = row.GetChildren(); if (row.expanded && !children.empty()) { DisplayRows(window, children, options); } } } int CalculateTotalNumberRows(std::vector &rows) { int row_count = 0; for (auto &row : rows) { ++row_count; if (row.expanded) row_count += CalculateTotalNumberRows(row.GetChildren()); } return row_count; } static Row *GetRowForRowIndexImpl(std::vector &rows, size_t &row_index) { for (auto &row : rows) { if (row_index == 0) return &row; else { --row_index; auto &children = row.GetChildren(); if (row.expanded && !children.empty()) { Row *result = GetRowForRowIndexImpl(children, row_index); if (result) return result; } } } return nullptr; } Row *GetRowForRowIndex(size_t row_index) { return GetRowForRowIndexImpl(m_rows, row_index); } int NumVisibleRows() const { return m_max_y - m_min_y; } static DisplayOptions g_options; }; class FrameVariablesWindowDelegate : public ValueObjectListDelegate { public: FrameVariablesWindowDelegate(Debugger &debugger) : ValueObjectListDelegate(), m_debugger(debugger), m_frame_block(nullptr) {} ~FrameVariablesWindowDelegate() override = default; const char *WindowDelegateGetHelpText() override { return "Frame variable window keyboard shortcuts:"; } bool WindowDelegateDraw(Window &window, bool force) override { ExecutionContext exe_ctx( m_debugger.GetCommandInterpreter().GetExecutionContext()); Process *process = exe_ctx.GetProcessPtr(); Block *frame_block = nullptr; StackFrame *frame = nullptr; if (process) { StateType state = process->GetState(); if (StateIsStoppedState(state, true)) { frame = exe_ctx.GetFramePtr(); if (frame) frame_block = frame->GetFrameBlock(); } else if (StateIsRunningState(state)) { return true; // Don't do any updating when we are running } } ValueObjectList local_values; if (frame_block) { // Only update the variables if they have changed if (m_frame_block != frame_block) { m_frame_block = frame_block; VariableList *locals = frame->GetVariableList(true); if (locals) { const DynamicValueType use_dynamic = eDynamicDontRunTarget; const size_t num_locals = locals->GetSize(); for (size_t i = 0; i < num_locals; ++i) { ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable( locals->GetVariableAtIndex(i), use_dynamic); if (value_sp) { ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue(); if (synthetic_value_sp) local_values.Append(synthetic_value_sp); else local_values.Append(value_sp); } } // Update the values SetValues(local_values); } } } else { m_frame_block = nullptr; // Update the values with an empty list if there is no frame SetValues(local_values); } return ValueObjectListDelegate::WindowDelegateDraw(window, force); } protected: Debugger &m_debugger; Block *m_frame_block; }; class RegistersWindowDelegate : public ValueObjectListDelegate { public: RegistersWindowDelegate(Debugger &debugger) : ValueObjectListDelegate(), m_debugger(debugger) {} ~RegistersWindowDelegate() override = default; const char *WindowDelegateGetHelpText() override { return "Register window keyboard shortcuts:"; } bool WindowDelegateDraw(Window &window, bool force) override { ExecutionContext exe_ctx( m_debugger.GetCommandInterpreter().GetExecutionContext()); StackFrame *frame = exe_ctx.GetFramePtr(); ValueObjectList value_list; if (frame) { if (frame->GetStackID() != m_stack_id) { m_stack_id = frame->GetStackID(); RegisterContextSP reg_ctx(frame->GetRegisterContext()); if (reg_ctx) { const uint32_t num_sets = reg_ctx->GetRegisterSetCount(); for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) { value_list.Append( ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx)); } } SetValues(value_list); } } else { Process *process = exe_ctx.GetProcessPtr(); if (process && process->IsAlive()) return true; // Don't do any updating if we are running else { // Update the values with an empty list if there is no process or the // process isn't alive anymore SetValues(value_list); } } return ValueObjectListDelegate::WindowDelegateDraw(window, force); } protected: Debugger &m_debugger; StackID m_stack_id; }; static const char *CursesKeyToCString(int ch) { static char g_desc[32]; if (ch >= KEY_F0 && ch < KEY_F0 + 64) { snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0); return g_desc; } switch (ch) { case KEY_DOWN: return "down"; case KEY_UP: return "up"; case KEY_LEFT: return "left"; case KEY_RIGHT: return "right"; case KEY_HOME: return "home"; case KEY_BACKSPACE: return "backspace"; case KEY_DL: return "delete-line"; case KEY_IL: return "insert-line"; case KEY_DC: return "delete-char"; case KEY_IC: return "insert-char"; case KEY_CLEAR: return "clear"; case KEY_EOS: return "clear-to-eos"; case KEY_EOL: return "clear-to-eol"; case KEY_SF: return "scroll-forward"; case KEY_SR: return "scroll-backward"; case KEY_NPAGE: return "page-down"; case KEY_PPAGE: return "page-up"; case KEY_STAB: return "set-tab"; case KEY_CTAB: return "clear-tab"; case KEY_CATAB: return "clear-all-tabs"; case KEY_ENTER: return "enter"; case KEY_PRINT: return "print"; case KEY_LL: return "lower-left key"; case KEY_A1: return "upper left of keypad"; case KEY_A3: return "upper right of keypad"; case KEY_B2: return "center of keypad"; case KEY_C1: return "lower left of keypad"; case KEY_C3: return "lower right of keypad"; case KEY_BTAB: return "back-tab key"; case KEY_BEG: return "begin key"; case KEY_CANCEL: return "cancel key"; case KEY_CLOSE: return "close key"; case KEY_COMMAND: return "command key"; case KEY_COPY: return "copy key"; case KEY_CREATE: return "create key"; case KEY_END: return "end key"; case KEY_EXIT: return "exit key"; case KEY_FIND: return "find key"; case KEY_HELP: return "help key"; case KEY_MARK: return "mark key"; case KEY_MESSAGE: return "message key"; case KEY_MOVE: return "move key"; case KEY_NEXT: return "next key"; case KEY_OPEN: return "open key"; case KEY_OPTIONS: return "options key"; case KEY_PREVIOUS: return "previous key"; case KEY_REDO: return "redo key"; case KEY_REFERENCE: return "reference key"; case KEY_REFRESH: return "refresh key"; case KEY_REPLACE: return "replace key"; case KEY_RESTART: return "restart key"; case KEY_RESUME: return "resume key"; case KEY_SAVE: return "save key"; case KEY_SBEG: return "shifted begin key"; case KEY_SCANCEL: return "shifted cancel key"; case KEY_SCOMMAND: return "shifted command key"; case KEY_SCOPY: return "shifted copy key"; case KEY_SCREATE: return "shifted create key"; case KEY_SDC: return "shifted delete-character key"; case KEY_SDL: return "shifted delete-line key"; case KEY_SELECT: return "select key"; case KEY_SEND: return "shifted end key"; case KEY_SEOL: return "shifted clear-to-end-of-line key"; case KEY_SEXIT: return "shifted exit key"; case KEY_SFIND: return "shifted find key"; case KEY_SHELP: return "shifted help key"; case KEY_SHOME: return "shifted home key"; case KEY_SIC: return "shifted insert-character key"; case KEY_SLEFT: return "shifted left-arrow key"; case KEY_SMESSAGE: return "shifted message key"; case KEY_SMOVE: return "shifted move key"; case KEY_SNEXT: return "shifted next key"; case KEY_SOPTIONS: return "shifted options key"; case KEY_SPREVIOUS: return "shifted previous key"; case KEY_SPRINT: return "shifted print key"; case KEY_SREDO: return "shifted redo key"; case KEY_SREPLACE: return "shifted replace key"; case KEY_SRIGHT: return "shifted right-arrow key"; case KEY_SRSUME: return "shifted resume key"; case KEY_SSAVE: return "shifted save key"; case KEY_SSUSPEND: return "shifted suspend key"; case KEY_SUNDO: return "shifted undo key"; case KEY_SUSPEND: return "suspend key"; case KEY_UNDO: return "undo key"; case KEY_MOUSE: return "Mouse event has occurred"; case KEY_RESIZE: return "Terminal resize event"; #ifdef KEY_EVENT case KEY_EVENT: return "We were interrupted by an event"; #endif case KEY_RETURN: return "return"; case ' ': return "space"; case '\t': return "tab"; case KEY_ESCAPE: return "escape"; default: if (isprint(ch)) snprintf(g_desc, sizeof(g_desc), "%c", ch); else snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch); return g_desc; } return nullptr; } HelpDialogDelegate::HelpDialogDelegate(const char *text, KeyHelp *key_help_array) : m_text(), m_first_visible_line(0) { if (text && text[0]) { m_text.SplitIntoLines(text); m_text.AppendString(""); } if (key_help_array) { for (KeyHelp *key = key_help_array; key->ch; ++key) { StreamString key_description; key_description.Printf("%10s - %s", CursesKeyToCString(key->ch), key->description); m_text.AppendString(key_description.GetString()); } } } HelpDialogDelegate::~HelpDialogDelegate() = default; bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) { window.Erase(); const int window_height = window.GetHeight(); int x = 2; int y = 1; const int min_y = y; const int max_y = window_height - 1 - y; const size_t num_visible_lines = max_y - min_y + 1; const size_t num_lines = m_text.GetSize(); const char *bottom_message; if (num_lines <= num_visible_lines) bottom_message = "Press any key to exit"; else bottom_message = "Use arrows to scroll, any other key to exit"; window.DrawTitleBox(window.GetName(), bottom_message); while (y <= max_y) { window.MoveCursor(x, y); window.PutCStringTruncated( m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1); ++y; } return true; } HandleCharResult HelpDialogDelegate::WindowDelegateHandleChar(Window &window, int key) { bool done = false; const size_t num_lines = m_text.GetSize(); const size_t num_visible_lines = window.GetHeight() - 2; if (num_lines <= num_visible_lines) { done = true; // If we have all lines visible and don't need scrolling, then any key // press will cause us to exit } else { switch (key) { case KEY_UP: if (m_first_visible_line > 0) --m_first_visible_line; break; case KEY_DOWN: if (m_first_visible_line + num_visible_lines < num_lines) ++m_first_visible_line; break; case KEY_PPAGE: case ',': if (m_first_visible_line > 0) { if (static_cast(m_first_visible_line) >= num_visible_lines) m_first_visible_line -= num_visible_lines; else m_first_visible_line = 0; } break; case KEY_NPAGE: case '.': if (m_first_visible_line + num_visible_lines < num_lines) { m_first_visible_line += num_visible_lines; if (static_cast(m_first_visible_line) > num_lines) m_first_visible_line = num_lines - num_visible_lines; } break; default: done = true; break; } } if (done) window.GetParent()->RemoveSubWindow(&window); return eKeyHandled; } class ApplicationDelegate : public WindowDelegate, public MenuDelegate { public: enum { eMenuID_LLDB = 1, eMenuID_LLDBAbout, eMenuID_LLDBExit, eMenuID_Target, eMenuID_TargetCreate, eMenuID_TargetDelete, eMenuID_Process, eMenuID_ProcessAttach, eMenuID_ProcessDetach, eMenuID_ProcessLaunch, eMenuID_ProcessContinue, eMenuID_ProcessHalt, eMenuID_ProcessKill, eMenuID_Thread, eMenuID_ThreadStepIn, eMenuID_ThreadStepOver, eMenuID_ThreadStepOut, eMenuID_View, eMenuID_ViewBacktrace, eMenuID_ViewRegisters, eMenuID_ViewSource, eMenuID_ViewVariables, eMenuID_Help, eMenuID_HelpGUIHelp }; ApplicationDelegate(Application &app, Debugger &debugger) : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {} ~ApplicationDelegate() override = default; bool WindowDelegateDraw(Window &window, bool force) override { return false; // Drawing not handled, let standard window drawing happen } HandleCharResult WindowDelegateHandleChar(Window &window, int key) override { switch (key) { case '\t': window.SelectNextWindowAsActive(); return eKeyHandled; case 'h': window.CreateHelpSubwindow(); return eKeyHandled; case KEY_ESCAPE: return eQuitApplication; default: break; } return eKeyNotHandled; } const char *WindowDelegateGetHelpText() override { return "Welcome to the LLDB curses GUI.\n\n" "Press the TAB key to change the selected view.\n" "Each view has its own keyboard shortcuts, press 'h' to open a " "dialog to display them.\n\n" "Common key bindings for all views:"; } KeyHelp *WindowDelegateGetKeyHelp() override { static curses::KeyHelp g_source_view_key_help[] = { {'\t', "Select next view"}, {'h', "Show help dialog with view specific key bindings"}, {',', "Page up"}, {'.', "Page down"}, {KEY_UP, "Select previous"}, {KEY_DOWN, "Select next"}, {KEY_LEFT, "Unexpand or select parent"}, {KEY_RIGHT, "Expand"}, {KEY_PPAGE, "Page up"}, {KEY_NPAGE, "Page down"}, {'\0', nullptr}}; return g_source_view_key_help; } MenuActionResult MenuDelegateAction(Menu &menu) override { switch (menu.GetIdentifier()) { case eMenuID_ThreadStepIn: { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasThreadScope()) { Process *process = exe_ctx.GetProcessPtr(); if (process && process->IsAlive() && StateIsStoppedState(process->GetState(), true)) exe_ctx.GetThreadRef().StepIn(true); } } return MenuActionResult::Handled; case eMenuID_ThreadStepOut: { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasThreadScope()) { Process *process = exe_ctx.GetProcessPtr(); if (process && process->IsAlive() && StateIsStoppedState(process->GetState(), true)) exe_ctx.GetThreadRef().StepOut(); } } return MenuActionResult::Handled; case eMenuID_ThreadStepOver: { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasThreadScope()) { Process *process = exe_ctx.GetProcessPtr(); if (process && process->IsAlive() && StateIsStoppedState(process->GetState(), true)) exe_ctx.GetThreadRef().StepOver(true); } } return MenuActionResult::Handled; case eMenuID_ProcessContinue: { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasProcessScope()) { Process *process = exe_ctx.GetProcessPtr(); if (process && process->IsAlive() && StateIsStoppedState(process->GetState(), true)) process->Resume(); } } return MenuActionResult::Handled; case eMenuID_ProcessKill: { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasProcessScope()) { Process *process = exe_ctx.GetProcessPtr(); if (process && process->IsAlive()) process->Destroy(false); } } return MenuActionResult::Handled; case eMenuID_ProcessHalt: { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasProcessScope()) { Process *process = exe_ctx.GetProcessPtr(); if (process && process->IsAlive()) process->Halt(); } } return MenuActionResult::Handled; case eMenuID_ProcessDetach: { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasProcessScope()) { Process *process = exe_ctx.GetProcessPtr(); if (process && process->IsAlive()) process->Detach(false); } } return MenuActionResult::Handled; case eMenuID_Process: { // Populate the menu with all of the threads if the process is stopped // when the Process menu gets selected and is about to display its // submenu. Menus &submenus = menu.GetSubmenus(); ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); Process *process = exe_ctx.GetProcessPtr(); if (process && process->IsAlive() && StateIsStoppedState(process->GetState(), true)) { if (submenus.size() == 7) menu.AddSubmenu(MenuSP(new Menu(Menu::Type::Separator))); else if (submenus.size() > 8) submenus.erase(submenus.begin() + 8, submenus.end()); ThreadList &threads = process->GetThreadList(); std::lock_guard guard(threads.GetMutex()); size_t num_threads = threads.GetSize(); for (size_t i = 0; i < num_threads; ++i) { ThreadSP thread_sp = threads.GetThreadAtIndex(i); char menu_char = '\0'; if (i < 9) menu_char = '1' + i; StreamString thread_menu_title; thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID()); const char *thread_name = thread_sp->GetName(); if (thread_name && thread_name[0]) thread_menu_title.Printf(" %s", thread_name); else { const char *queue_name = thread_sp->GetQueueName(); if (queue_name && queue_name[0]) thread_menu_title.Printf(" %s", queue_name); } menu.AddSubmenu( MenuSP(new Menu(thread_menu_title.GetString().str().c_str(), nullptr, menu_char, thread_sp->GetID()))); } } else if (submenus.size() > 7) { // Remove the separator and any other thread submenu items that were // previously added submenus.erase(submenus.begin() + 7, submenus.end()); } // Since we are adding and removing items we need to recalculate the name // lengths menu.RecalculateNameLengths(); } return MenuActionResult::Handled; case eMenuID_ViewVariables: { WindowSP main_window_sp = m_app.GetMainWindow(); WindowSP source_window_sp = main_window_sp->FindSubWindow("Source"); WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables"); WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers"); const Rect source_bounds = source_window_sp->GetBounds(); if (variables_window_sp) { const Rect variables_bounds = variables_window_sp->GetBounds(); main_window_sp->RemoveSubWindow(variables_window_sp.get()); if (registers_window_sp) { // We have a registers window, so give all the area back to the // registers window Rect registers_bounds = variables_bounds; registers_bounds.size.width = source_bounds.size.width; registers_window_sp->SetBounds(registers_bounds); } else { // We have no registers window showing so give the bottom area back // to the source view source_window_sp->Resize(source_bounds.size.width, source_bounds.size.height + variables_bounds.size.height); } } else { Rect new_variables_rect; if (registers_window_sp) { // We have a registers window so split the area of the registers // window into two columns where the left hand side will be the // variables and the right hand side will be the registers const Rect variables_bounds = registers_window_sp->GetBounds(); Rect new_registers_rect; variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect, new_registers_rect); registers_window_sp->SetBounds(new_registers_rect); } else { // No variables window, grab the bottom part of the source window Rect new_source_rect; source_bounds.HorizontalSplitPercentage(0.70, new_source_rect, new_variables_rect); source_window_sp->SetBounds(new_source_rect); } WindowSP new_window_sp = main_window_sp->CreateSubWindow( "Variables", new_variables_rect, false); new_window_sp->SetDelegate( WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger))); } touchwin(stdscr); } return MenuActionResult::Handled; case eMenuID_ViewRegisters: { WindowSP main_window_sp = m_app.GetMainWindow(); WindowSP source_window_sp = main_window_sp->FindSubWindow("Source"); WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables"); WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers"); const Rect source_bounds = source_window_sp->GetBounds(); if (registers_window_sp) { if (variables_window_sp) { const Rect variables_bounds = variables_window_sp->GetBounds(); // We have a variables window, so give all the area back to the // variables window variables_window_sp->Resize(variables_bounds.size.width + registers_window_sp->GetWidth(), variables_bounds.size.height); } else { // We have no variables window showing so give the bottom area back // to the source view source_window_sp->Resize(source_bounds.size.width, source_bounds.size.height + registers_window_sp->GetHeight()); } main_window_sp->RemoveSubWindow(registers_window_sp.get()); } else { Rect new_regs_rect; if (variables_window_sp) { // We have a variables window, split it into two columns where the // left hand side will be the variables and the right hand side will // be the registers const Rect variables_bounds = variables_window_sp->GetBounds(); Rect new_vars_rect; variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect, new_regs_rect); variables_window_sp->SetBounds(new_vars_rect); } else { // No registers window, grab the bottom part of the source window Rect new_source_rect; source_bounds.HorizontalSplitPercentage(0.70, new_source_rect, new_regs_rect); source_window_sp->SetBounds(new_source_rect); } WindowSP new_window_sp = main_window_sp->CreateSubWindow("Registers", new_regs_rect, false); new_window_sp->SetDelegate( WindowDelegateSP(new RegistersWindowDelegate(m_debugger))); } touchwin(stdscr); } return MenuActionResult::Handled; case eMenuID_HelpGUIHelp: m_app.GetMainWindow()->CreateHelpSubwindow(); return MenuActionResult::Handled; default: break; } return MenuActionResult::NotHandled; } protected: Application &m_app; Debugger &m_debugger; }; class StatusBarWindowDelegate : public WindowDelegate { public: StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) { FormatEntity::Parse("Thread: ${thread.id%tid}", m_format); } ~StatusBarWindowDelegate() override = default; bool WindowDelegateDraw(Window &window, bool force) override { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); Process *process = exe_ctx.GetProcessPtr(); Thread *thread = exe_ctx.GetThreadPtr(); StackFrame *frame = exe_ctx.GetFramePtr(); window.Erase(); window.SetBackground(2); window.MoveCursor(0, 0); if (process) { const StateType state = process->GetState(); window.Printf("Process: %5" PRIu64 " %10s", process->GetID(), StateAsCString(state)); if (StateIsStoppedState(state, true)) { StreamString strm; if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr, nullptr, false, false)) { window.MoveCursor(40, 0); window.PutCStringTruncated(strm.GetString().str().c_str(), 1); } window.MoveCursor(60, 0); if (frame) window.Printf("Frame: %3u PC = 0x%16.16" PRIx64, frame->GetFrameIndex(), frame->GetFrameCodeAddress().GetOpcodeLoadAddress( exe_ctx.GetTargetPtr())); } else if (state == eStateExited) { const char *exit_desc = process->GetExitDescription(); const int exit_status = process->GetExitStatus(); if (exit_desc && exit_desc[0]) window.Printf(" with status = %i (%s)", exit_status, exit_desc); else window.Printf(" with status = %i", exit_status); } } window.DeferredRefresh(); return true; } protected: Debugger &m_debugger; FormatEntity::Entry m_format; }; class SourceFileWindowDelegate : public WindowDelegate { public: SourceFileWindowDelegate(Debugger &debugger) : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(), m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(), m_title(), m_line_width(4), m_selected_line(0), m_pc_line(0), m_stop_id(0), m_frame_idx(UINT32_MAX), m_first_visible_line(0), m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {} ~SourceFileWindowDelegate() override = default; void Update(const SymbolContext &sc) { m_sc = sc; } uint32_t NumVisibleLines() const { return m_max_y - m_min_y; } const char *WindowDelegateGetHelpText() override { return "Source/Disassembly window keyboard shortcuts:"; } KeyHelp *WindowDelegateGetKeyHelp() override { static curses::KeyHelp g_source_view_key_help[] = { {KEY_RETURN, "Run to selected line with one shot breakpoint"}, {KEY_UP, "Select previous source line"}, {KEY_DOWN, "Select next source line"}, {KEY_PPAGE, "Page up"}, {KEY_NPAGE, "Page down"}, {'b', "Set breakpoint on selected source/disassembly line"}, {'c', "Continue process"}, {'d', "Detach and resume process"}, {'D', "Detach with process suspended"}, {'h', "Show help dialog"}, {'k', "Kill process"}, {'n', "Step over (source line)"}, {'N', "Step over (single instruction)"}, {'o', "Step out"}, {'s', "Step in (source line)"}, {'S', "Step in (single instruction)"}, {',', "Page up"}, {'.', "Page down"}, {'\0', nullptr}}; return g_source_view_key_help; } bool WindowDelegateDraw(Window &window, bool force) override { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); Process *process = exe_ctx.GetProcessPtr(); Thread *thread = nullptr; bool update_location = false; if (process) { StateType state = process->GetState(); if (StateIsStoppedState(state, true)) { // We are stopped, so it is ok to update_location = true; } } m_min_x = 1; m_min_y = 2; m_max_x = window.GetMaxX() - 1; m_max_y = window.GetMaxY() - 1; const uint32_t num_visible_lines = NumVisibleLines(); StackFrameSP frame_sp; bool set_selected_line_to_pc = false; if (update_location) { const bool process_alive = process ? process->IsAlive() : false; bool thread_changed = false; if (process_alive) { thread = exe_ctx.GetThreadPtr(); if (thread) { frame_sp = thread->GetSelectedFrame(); auto tid = thread->GetID(); thread_changed = tid != m_tid; m_tid = tid; } else { if (m_tid != LLDB_INVALID_THREAD_ID) { thread_changed = true; m_tid = LLDB_INVALID_THREAD_ID; } } } const uint32_t stop_id = process ? process->GetStopID() : 0; const bool stop_id_changed = stop_id != m_stop_id; bool frame_changed = false; m_stop_id = stop_id; m_title.Clear(); if (frame_sp) { m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything); if (m_sc.module_sp) { m_title.Printf( "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString()); ConstString func_name = m_sc.GetFunctionName(); if (func_name) m_title.Printf("`%s", func_name.GetCString()); } const uint32_t frame_idx = frame_sp->GetFrameIndex(); frame_changed = frame_idx != m_frame_idx; m_frame_idx = frame_idx; } else { m_sc.Clear(true); frame_changed = m_frame_idx != UINT32_MAX; m_frame_idx = UINT32_MAX; } const bool context_changed = thread_changed || frame_changed || stop_id_changed; if (process_alive) { if (m_sc.line_entry.IsValid()) { m_pc_line = m_sc.line_entry.line; if (m_pc_line != UINT32_MAX) --m_pc_line; // Convert to zero based line number... // Update the selected line if the stop ID changed... if (context_changed) m_selected_line = m_pc_line; if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file)) { // Same file, nothing to do, we should either have the lines or not // (source file missing) if (m_selected_line >= static_cast(m_first_visible_line)) { if (m_selected_line >= m_first_visible_line + num_visible_lines) m_first_visible_line = m_selected_line - 10; } else { if (m_selected_line > 10) m_first_visible_line = m_selected_line - 10; else m_first_visible_line = 0; } } else { // File changed, set selected line to the line with the PC m_selected_line = m_pc_line; m_file_sp = m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file); if (m_file_sp) { const size_t num_lines = m_file_sp->GetNumLines(); int m_line_width = 1; for (size_t n = num_lines; n >= 10; n = n / 10) ++m_line_width; snprintf(m_line_format, sizeof(m_line_format), " %%%iu ", m_line_width); if (num_lines < num_visible_lines || m_selected_line < num_visible_lines) m_first_visible_line = 0; else m_first_visible_line = m_selected_line - 10; } } } else { m_file_sp.reset(); } if (!m_file_sp || m_file_sp->GetNumLines() == 0) { // Show disassembly bool prefer_file_cache = false; if (m_sc.function) { if (m_disassembly_scope != m_sc.function) { m_disassembly_scope = m_sc.function; m_disassembly_sp = m_sc.function->GetInstructions( exe_ctx, nullptr, prefer_file_cache); if (m_disassembly_sp) { set_selected_line_to_pc = true; m_disassembly_range = m_sc.function->GetAddressRange(); } else { m_disassembly_range.Clear(); } } else { set_selected_line_to_pc = context_changed; } } else if (m_sc.symbol) { if (m_disassembly_scope != m_sc.symbol) { m_disassembly_scope = m_sc.symbol; m_disassembly_sp = m_sc.symbol->GetInstructions( exe_ctx, nullptr, prefer_file_cache); if (m_disassembly_sp) { set_selected_line_to_pc = true; m_disassembly_range.GetBaseAddress() = m_sc.symbol->GetAddress(); m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize()); } else { m_disassembly_range.Clear(); } } else { set_selected_line_to_pc = context_changed; } } } } else { m_pc_line = UINT32_MAX; } } const int window_width = window.GetWidth(); window.Erase(); window.DrawTitleBox("Sources"); if (!m_title.GetString().empty()) { window.AttributeOn(A_REVERSE); window.MoveCursor(1, 1); window.PutChar(' '); window.PutCStringTruncated(m_title.GetString().str().c_str(), 1); int x = window.GetCursorX(); if (x < window_width - 1) { window.Printf("%*s", window_width - x - 1, ""); } window.AttributeOff(A_REVERSE); } Target *target = exe_ctx.GetTargetPtr(); const size_t num_source_lines = GetNumSourceLines(); if (num_source_lines > 0) { // Display source BreakpointLines bp_lines; if (target) { BreakpointList &bp_list = target->GetBreakpointList(); const size_t num_bps = bp_list.GetSize(); for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) { BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); const size_t num_bps_locs = bp_sp->GetNumLocations(); for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) { BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx); LineEntry bp_loc_line_entry; if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry( bp_loc_line_entry)) { if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) { bp_lines.insert(bp_loc_line_entry.line); } } } } } const attr_t selected_highlight_attr = A_REVERSE; const attr_t pc_highlight_attr = COLOR_PAIR(1); for (size_t i = 0; i < num_visible_lines; ++i) { const uint32_t curr_line = m_first_visible_line + i; if (curr_line < num_source_lines) { const int line_y = m_min_y + i; window.MoveCursor(1, line_y); const bool is_pc_line = curr_line == m_pc_line; const bool line_is_selected = m_selected_line == curr_line; // Highlight the line as the PC line first, then if the selected line // isn't the same as the PC line, highlight it differently attr_t highlight_attr = 0; attr_t bp_attr = 0; if (is_pc_line) highlight_attr = pc_highlight_attr; else if (line_is_selected) highlight_attr = selected_highlight_attr; if (bp_lines.find(curr_line + 1) != bp_lines.end()) bp_attr = COLOR_PAIR(2); if (bp_attr) window.AttributeOn(bp_attr); window.Printf(m_line_format, curr_line + 1); if (bp_attr) window.AttributeOff(bp_attr); window.PutChar(ACS_VLINE); // Mark the line with the PC with a diamond if (is_pc_line) window.PutChar(ACS_DIAMOND); else window.PutChar(' '); if (highlight_attr) window.AttributeOn(highlight_attr); const uint32_t line_len = m_file_sp->GetLineLength(curr_line + 1, false); if (line_len > 0) window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len); if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0) { StopInfoSP stop_info_sp; if (thread) stop_info_sp = thread->GetStopInfo(); if (stop_info_sp) { const char *stop_description = stop_info_sp->GetDescription(); if (stop_description && stop_description[0]) { size_t stop_description_len = strlen(stop_description); int desc_x = window_width - stop_description_len - 16; window.Printf("%*s", desc_x - window.GetCursorX(), ""); // window.MoveCursor(window_width - stop_description_len - 15, // line_y); window.Printf("<<< Thread %u: %s ", thread->GetIndexID(), stop_description); } } else { window.Printf("%*s", window_width - window.GetCursorX() - 1, ""); } } if (highlight_attr) window.AttributeOff(highlight_attr); } else { break; } } } else { size_t num_disassembly_lines = GetNumDisassemblyLines(); if (num_disassembly_lines > 0) { // Display disassembly BreakpointAddrs bp_file_addrs; Target *target = exe_ctx.GetTargetPtr(); if (target) { BreakpointList &bp_list = target->GetBreakpointList(); const size_t num_bps = bp_list.GetSize(); for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) { BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx); const size_t num_bps_locs = bp_sp->GetNumLocations(); for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) { BreakpointLocationSP bp_loc_sp = bp_sp->GetLocationAtIndex(bp_loc_idx); LineEntry bp_loc_line_entry; const lldb::addr_t file_addr = bp_loc_sp->GetAddress().GetFileAddress(); if (file_addr != LLDB_INVALID_ADDRESS) { if (m_disassembly_range.ContainsFileAddress(file_addr)) bp_file_addrs.insert(file_addr); } } } } const attr_t selected_highlight_attr = A_REVERSE; const attr_t pc_highlight_attr = COLOR_PAIR(1); StreamString strm; InstructionList &insts = m_disassembly_sp->GetInstructionList(); Address pc_address; if (frame_sp) pc_address = frame_sp->GetFrameCodeAddress(); const uint32_t pc_idx = pc_address.IsValid() ? insts.GetIndexOfInstructionAtAddress(pc_address) : UINT32_MAX; if (set_selected_line_to_pc) { m_selected_line = pc_idx; } const uint32_t non_visible_pc_offset = (num_visible_lines / 5); if (static_cast(m_first_visible_line) >= num_disassembly_lines) m_first_visible_line = 0; if (pc_idx < num_disassembly_lines) { if (pc_idx < static_cast(m_first_visible_line) || pc_idx >= m_first_visible_line + num_visible_lines) m_first_visible_line = pc_idx - non_visible_pc_offset; } for (size_t i = 0; i < num_visible_lines; ++i) { const uint32_t inst_idx = m_first_visible_line + i; Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get(); if (!inst) break; const int line_y = m_min_y + i; window.MoveCursor(1, line_y); const bool is_pc_line = frame_sp && inst_idx == pc_idx; const bool line_is_selected = m_selected_line == inst_idx; // Highlight the line as the PC line first, then if the selected line // isn't the same as the PC line, highlight it differently attr_t highlight_attr = 0; attr_t bp_attr = 0; if (is_pc_line) highlight_attr = pc_highlight_attr; else if (line_is_selected) highlight_attr = selected_highlight_attr; if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) != bp_file_addrs.end()) bp_attr = COLOR_PAIR(2); if (bp_attr) window.AttributeOn(bp_attr); window.Printf(" 0x%16.16llx ", static_cast( inst->GetAddress().GetLoadAddress(target))); if (bp_attr) window.AttributeOff(bp_attr); window.PutChar(ACS_VLINE); // Mark the line with the PC with a diamond if (is_pc_line) window.PutChar(ACS_DIAMOND); else window.PutChar(' '); if (highlight_attr) window.AttributeOn(highlight_attr); const char *mnemonic = inst->GetMnemonic(&exe_ctx); const char *operands = inst->GetOperands(&exe_ctx); const char *comment = inst->GetComment(&exe_ctx); if (mnemonic != nullptr && mnemonic[0] == '\0') mnemonic = nullptr; if (operands != nullptr && operands[0] == '\0') operands = nullptr; if (comment != nullptr && comment[0] == '\0') comment = nullptr; strm.Clear(); if (mnemonic != nullptr && operands != nullptr && comment != nullptr) strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment); else if (mnemonic != nullptr && operands != nullptr) strm.Printf("%-8s %s", mnemonic, operands); else if (mnemonic != nullptr) strm.Printf("%s", mnemonic); int right_pad = 1; window.PutCStringTruncated(strm.GetData(), right_pad); if (is_pc_line && frame_sp && frame_sp->GetConcreteFrameIndex() == 0) { StopInfoSP stop_info_sp; if (thread) stop_info_sp = thread->GetStopInfo(); if (stop_info_sp) { const char *stop_description = stop_info_sp->GetDescription(); if (stop_description && stop_description[0]) { size_t stop_description_len = strlen(stop_description); int desc_x = window_width - stop_description_len - 16; window.Printf("%*s", desc_x - window.GetCursorX(), ""); // window.MoveCursor(window_width - stop_description_len - 15, // line_y); window.Printf("<<< Thread %u: %s ", thread->GetIndexID(), stop_description); } } else { window.Printf("%*s", window_width - window.GetCursorX() - 1, ""); } } if (highlight_attr) window.AttributeOff(highlight_attr); } } } window.DeferredRefresh(); return true; // Drawing handled } size_t GetNumLines() { size_t num_lines = GetNumSourceLines(); if (num_lines == 0) num_lines = GetNumDisassemblyLines(); return num_lines; } size_t GetNumSourceLines() const { if (m_file_sp) return m_file_sp->GetNumLines(); return 0; } size_t GetNumDisassemblyLines() const { if (m_disassembly_sp) return m_disassembly_sp->GetInstructionList().GetSize(); return 0; } HandleCharResult WindowDelegateHandleChar(Window &window, int c) override { const uint32_t num_visible_lines = NumVisibleLines(); const size_t num_lines = GetNumLines(); switch (c) { case ',': case KEY_PPAGE: // Page up key if (static_cast(m_first_visible_line) > num_visible_lines) m_first_visible_line -= num_visible_lines; else m_first_visible_line = 0; m_selected_line = m_first_visible_line; return eKeyHandled; case '.': case KEY_NPAGE: // Page down key { if (m_first_visible_line + num_visible_lines < num_lines) m_first_visible_line += num_visible_lines; else if (num_lines < num_visible_lines) m_first_visible_line = 0; else m_first_visible_line = num_lines - num_visible_lines; m_selected_line = m_first_visible_line; } return eKeyHandled; case KEY_UP: if (m_selected_line > 0) { m_selected_line--; if (static_cast(m_first_visible_line) > m_selected_line) m_first_visible_line = m_selected_line; } return eKeyHandled; case KEY_DOWN: if (m_selected_line + 1 < num_lines) { m_selected_line++; if (m_first_visible_line + num_visible_lines < m_selected_line) m_first_visible_line++; } return eKeyHandled; case '\r': case '\n': case KEY_ENTER: // Set a breakpoint and run to the line using a one shot breakpoint if (GetNumSourceLines() > 0) { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) { BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( nullptr, // Don't limit the breakpoint to certain modules m_file_sp->GetFileSpec(), // Source file m_selected_line + 1, // Source line number (m_selected_line is zero based) 0, // No offset eLazyBoolCalculate, // Check inlines using global setting eLazyBoolCalculate, // Skip prologue using global setting, false, // internal false, // request_hardware eLazyBoolCalculate); // move_to_nearest_code // Make breakpoint one shot bp_sp->GetOptions()->SetOneShot(true); exe_ctx.GetProcessRef().Resume(); } } else if (m_selected_line < GetNumDisassemblyLines()) { const Instruction *inst = m_disassembly_sp->GetInstructionList() .GetInstructionAtIndex(m_selected_line) .get(); ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasTargetScope()) { Address addr = inst->GetAddress(); BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( addr, // lldb_private::Address false, // internal false); // request_hardware // Make breakpoint one shot bp_sp->GetOptions()->SetOneShot(true); exe_ctx.GetProcessRef().Resume(); } } return eKeyHandled; case 'b': // 'b' == toggle breakpoint on currently selected line if (m_selected_line < GetNumSourceLines()) { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasTargetScope()) { BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( nullptr, // Don't limit the breakpoint to certain modules m_file_sp->GetFileSpec(), // Source file m_selected_line + 1, // Source line number (m_selected_line is zero based) 0, // No offset eLazyBoolCalculate, // Check inlines using global setting eLazyBoolCalculate, // Skip prologue using global setting, false, // internal false, // request_hardware eLazyBoolCalculate); // move_to_nearest_code } } else if (m_selected_line < GetNumDisassemblyLines()) { const Instruction *inst = m_disassembly_sp->GetInstructionList() .GetInstructionAtIndex(m_selected_line) .get(); ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasTargetScope()) { Address addr = inst->GetAddress(); BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint( addr, // lldb_private::Address false, // internal false); // request_hardware } } return eKeyHandled; case 'd': // 'd' == detach and let run case 'D': // 'D' == detach and keep stopped { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasProcessScope()) exe_ctx.GetProcessRef().Detach(c == 'D'); } return eKeyHandled; case 'k': // 'k' == kill { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasProcessScope()) exe_ctx.GetProcessRef().Destroy(false); } return eKeyHandled; case 'c': // 'c' == continue { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasProcessScope()) exe_ctx.GetProcessRef().Resume(); } return eKeyHandled; case 'o': // 'o' == step out { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasThreadScope() && StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) { exe_ctx.GetThreadRef().StepOut(); } } return eKeyHandled; case 'n': // 'n' == step over case 'N': // 'N' == step over instruction { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasThreadScope() && StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) { bool source_step = (c == 'n'); exe_ctx.GetThreadRef().StepOver(source_step); } } return eKeyHandled; case 's': // 's' == step into case 'S': // 'S' == step into instruction { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasThreadScope() && StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) { bool source_step = (c == 's'); exe_ctx.GetThreadRef().StepIn(source_step); } } return eKeyHandled; case 'h': window.CreateHelpSubwindow(); return eKeyHandled; default: break; } return eKeyNotHandled; } protected: typedef std::set BreakpointLines; typedef std::set BreakpointAddrs; Debugger &m_debugger; SymbolContext m_sc; SourceManager::FileSP m_file_sp; SymbolContextScope *m_disassembly_scope; lldb::DisassemblerSP m_disassembly_sp; AddressRange m_disassembly_range; StreamString m_title; lldb::user_id_t m_tid; char m_line_format[8]; int m_line_width; uint32_t m_selected_line; // The selected line uint32_t m_pc_line; // The line with the PC uint32_t m_stop_id; uint32_t m_frame_idx; int m_first_visible_line; int m_min_x; int m_min_y; int m_max_x; int m_max_y; }; DisplayOptions ValueObjectListDelegate::g_options = {true}; IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger) : IOHandler(debugger, IOHandler::Type::Curses) {} void IOHandlerCursesGUI::Activate() { IOHandler::Activate(); if (!m_app_ap) { m_app_ap.reset(new Application(GetInputFILE(), GetOutputFILE())); // This is both a window and a menu delegate std::shared_ptr app_delegate_sp( new ApplicationDelegate(*m_app_ap, m_debugger)); MenuDelegateSP app_menu_delegate_sp = std::static_pointer_cast(app_delegate_sp); MenuSP lldb_menu_sp( new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB)); MenuSP exit_menuitem_sp( new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit)); exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit); lldb_menu_sp->AddSubmenu(MenuSP(new Menu( "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout))); lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator))); lldb_menu_sp->AddSubmenu(exit_menuitem_sp); MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2), ApplicationDelegate::eMenuID_Target)); target_menu_sp->AddSubmenu(MenuSP(new Menu( "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate))); target_menu_sp->AddSubmenu(MenuSP(new Menu( "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete))); MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3), ApplicationDelegate::eMenuID_Process)); process_menu_sp->AddSubmenu(MenuSP(new Menu( "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach))); process_menu_sp->AddSubmenu(MenuSP(new Menu( "Detach", nullptr, 'd', ApplicationDelegate::eMenuID_ProcessDetach))); process_menu_sp->AddSubmenu(MenuSP(new Menu( "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch))); process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator))); process_menu_sp->AddSubmenu( MenuSP(new Menu("Continue", nullptr, 'c', ApplicationDelegate::eMenuID_ProcessContinue))); process_menu_sp->AddSubmenu(MenuSP(new Menu( "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt))); process_menu_sp->AddSubmenu(MenuSP(new Menu( "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill))); MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4), ApplicationDelegate::eMenuID_Thread)); thread_menu_sp->AddSubmenu(MenuSP(new Menu( "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn))); thread_menu_sp->AddSubmenu( MenuSP(new Menu("Step Over", nullptr, 'v', ApplicationDelegate::eMenuID_ThreadStepOver))); thread_menu_sp->AddSubmenu(MenuSP(new Menu( "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut))); MenuSP view_menu_sp( new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View)); view_menu_sp->AddSubmenu( MenuSP(new Menu("Backtrace", nullptr, 'b', ApplicationDelegate::eMenuID_ViewBacktrace))); view_menu_sp->AddSubmenu( MenuSP(new Menu("Registers", nullptr, 'r', ApplicationDelegate::eMenuID_ViewRegisters))); view_menu_sp->AddSubmenu(MenuSP(new Menu( "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource))); view_menu_sp->AddSubmenu( MenuSP(new Menu("Variables", nullptr, 'v', ApplicationDelegate::eMenuID_ViewVariables))); MenuSP help_menu_sp( new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help)); help_menu_sp->AddSubmenu(MenuSP(new Menu( "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp))); m_app_ap->Initialize(); WindowSP &main_window_sp = m_app_ap->GetMainWindow(); MenuSP menubar_sp(new Menu(Menu::Type::Bar)); menubar_sp->AddSubmenu(lldb_menu_sp); menubar_sp->AddSubmenu(target_menu_sp); menubar_sp->AddSubmenu(process_menu_sp); menubar_sp->AddSubmenu(thread_menu_sp); menubar_sp->AddSubmenu(view_menu_sp); menubar_sp->AddSubmenu(help_menu_sp); menubar_sp->SetDelegate(app_menu_delegate_sp); Rect content_bounds = main_window_sp->GetFrame(); Rect menubar_bounds = content_bounds.MakeMenuBar(); Rect status_bounds = content_bounds.MakeStatusBar(); Rect source_bounds; Rect variables_bounds; Rect threads_bounds; Rect source_variables_bounds; content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds, threads_bounds); source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds, variables_bounds); WindowSP menubar_window_sp = main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false); // Let the menubar get keys if the active window doesn't handle the keys // that are typed so it can respond to menubar key presses. menubar_window_sp->SetCanBeActive( false); // Don't let the menubar become the active window menubar_window_sp->SetDelegate(menubar_sp); WindowSP source_window_sp( main_window_sp->CreateSubWindow("Source", source_bounds, true)); WindowSP variables_window_sp( main_window_sp->CreateSubWindow("Variables", variables_bounds, false)); WindowSP threads_window_sp( main_window_sp->CreateSubWindow("Threads", threads_bounds, false)); WindowSP status_window_sp( main_window_sp->CreateSubWindow("Status", status_bounds, false)); status_window_sp->SetCanBeActive( false); // Don't let the status bar become the active window main_window_sp->SetDelegate( std::static_pointer_cast(app_delegate_sp)); source_window_sp->SetDelegate( WindowDelegateSP(new SourceFileWindowDelegate(m_debugger))); variables_window_sp->SetDelegate( WindowDelegateSP(new FrameVariablesWindowDelegate(m_debugger))); TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger)); threads_window_sp->SetDelegate(WindowDelegateSP( new TreeWindowDelegate(m_debugger, thread_delegate_sp))); status_window_sp->SetDelegate( WindowDelegateSP(new StatusBarWindowDelegate(m_debugger))); // Show the main help window once the first time the curses GUI is launched static bool g_showed_help = false; if (!g_showed_help) { g_showed_help = true; main_window_sp->CreateHelpSubwindow(); } init_pair(1, COLOR_WHITE, COLOR_BLUE); init_pair(2, COLOR_BLACK, COLOR_WHITE); init_pair(3, COLOR_MAGENTA, COLOR_WHITE); init_pair(4, COLOR_MAGENTA, COLOR_BLACK); init_pair(5, COLOR_RED, COLOR_BLACK); } } void IOHandlerCursesGUI::Deactivate() { m_app_ap->Terminate(); } void IOHandlerCursesGUI::Run() { m_app_ap->Run(m_debugger); SetIsDone(true); } IOHandlerCursesGUI::~IOHandlerCursesGUI() = default; void IOHandlerCursesGUI::Cancel() {} bool IOHandlerCursesGUI::Interrupt() { return false; } void IOHandlerCursesGUI::GotEOF() {} #endif // LLDB_DISABLE_CURSES Index: vendor/lldb/dist/source/Interpreter/CommandInterpreter.cpp =================================================================== --- vendor/lldb/dist/source/Interpreter/CommandInterpreter.cpp (revision 337146) +++ vendor/lldb/dist/source/Interpreter/CommandInterpreter.cpp (revision 337147) @@ -1,3069 +1,3071 @@ //===-- CommandInterpreter.cpp ----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include #include #include "CommandObjectScript.h" #include "lldb/Interpreter/CommandObjectRegexCommand.h" #include "Commands/CommandObjectApropos.h" #include "Commands/CommandObjectBreakpoint.h" #include "Commands/CommandObjectBugreport.h" #include "Commands/CommandObjectCommands.h" #include "Commands/CommandObjectDisassemble.h" #include "Commands/CommandObjectExpression.h" #include "Commands/CommandObjectFrame.h" #include "Commands/CommandObjectGUI.h" #include "Commands/CommandObjectHelp.h" #include "Commands/CommandObjectLanguage.h" #include "Commands/CommandObjectLog.h" #include "Commands/CommandObjectMemory.h" #include "Commands/CommandObjectPlatform.h" #include "Commands/CommandObjectPlugin.h" #include "Commands/CommandObjectProcess.h" #include "Commands/CommandObjectQuit.h" #include "Commands/CommandObjectRegister.h" #include "Commands/CommandObjectSettings.h" #include "Commands/CommandObjectSource.h" #include "Commands/CommandObjectStats.h" #include "Commands/CommandObjectTarget.h" #include "Commands/CommandObjectThread.h" #include "Commands/CommandObjectType.h" #include "Commands/CommandObjectVersion.h" #include "Commands/CommandObjectWatchpoint.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/Timer.h" #ifndef LLDB_DISABLE_LIBEDIT #include "lldb/Host/Editline.h" #endif #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/Property.h" #include "lldb/Utility/Args.h" #include "lldb/Target/Process.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/Thread.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" using namespace lldb; using namespace lldb_private; static const char *k_white_space = " \t\v"; static PropertyDefinition g_properties[] = { {"expand-regex-aliases", OptionValue::eTypeBoolean, true, false, nullptr, nullptr, "If true, regular expression alias commands will show the " "expanded command that will be executed. This can be used to " "debug new regular expression alias commands."}, {"prompt-on-quit", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "If true, LLDB will prompt you before quitting if there are any live " "processes being debugged. If false, LLDB will quit without asking in any " "case."}, {"stop-command-source-on-error", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "If true, LLDB will stop running a 'command source' " "script upon encountering an error."}, {"space-repl-prompts", OptionValue::eTypeBoolean, true, false, nullptr, nullptr, "If true, blank lines will be printed between between REPL submissions."}, {nullptr, OptionValue::eTypeInvalid, true, 0, nullptr, nullptr, nullptr}}; enum { ePropertyExpandRegexAliases = 0, ePropertyPromptOnQuit = 1, ePropertyStopCmdSourceOnError = 2, eSpaceReplPrompts = 3 }; ConstString &CommandInterpreter::GetStaticBroadcasterClass() { static ConstString class_name("lldb.commandInterpreter"); return class_name; } CommandInterpreter::CommandInterpreter(Debugger &debugger, ScriptLanguage script_language, bool synchronous_execution) : Broadcaster(debugger.GetBroadcasterManager(), CommandInterpreter::GetStaticBroadcasterClass().AsCString()), Properties(OptionValuePropertiesSP( new OptionValueProperties(ConstString("interpreter")))), IOHandlerDelegate(IOHandlerDelegate::Completion::LLDBCommand), m_debugger(debugger), m_synchronous_execution(synchronous_execution), m_skip_lldbinit_files(false), m_skip_app_init_files(false), m_script_interpreter_sp(), m_command_io_handler_sp(), m_comment_char('#'), m_batch_command_mode(false), m_truncation_warning(eNoTruncation), m_command_source_depth(0), m_num_errors(0), m_quit_requested(false), m_stopped_for_crash(false) { debugger.SetScriptLanguage(script_language); SetEventName(eBroadcastBitThreadShouldExit, "thread-should-exit"); SetEventName(eBroadcastBitResetPrompt, "reset-prompt"); SetEventName(eBroadcastBitQuitCommandReceived, "quit"); CheckInWithManager(); m_collection_sp->Initialize(g_properties); } bool CommandInterpreter::GetExpandRegexAliases() const { const uint32_t idx = ePropertyExpandRegexAliases; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool CommandInterpreter::GetPromptOnQuit() const { const uint32_t idx = ePropertyPromptOnQuit; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } void CommandInterpreter::SetPromptOnQuit(bool b) { const uint32_t idx = ePropertyPromptOnQuit; m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } void CommandInterpreter::AllowExitCodeOnQuit(bool allow) { m_allow_exit_code = allow; if (!allow) m_quit_exit_code.reset(); } bool CommandInterpreter::SetQuitExitCode(int exit_code) { if (!m_allow_exit_code) return false; m_quit_exit_code = exit_code; return true; } int CommandInterpreter::GetQuitExitCode(bool &exited) const { exited = m_quit_exit_code.hasValue(); if (exited) return *m_quit_exit_code; return 0; } void CommandInterpreter::ResolveCommand(const char *command_line, CommandReturnObject &result) { std::string command = command_line; if (ResolveCommandImpl(command, result) != nullptr) { result.AppendMessageWithFormat("%s", command.c_str()); result.SetStatus(eReturnStatusSuccessFinishResult); } } bool CommandInterpreter::GetStopCmdSourceOnError() const { const uint32_t idx = ePropertyStopCmdSourceOnError; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool CommandInterpreter::GetSpaceReplPrompts() const { const uint32_t idx = eSpaceReplPrompts; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } void CommandInterpreter::Initialize() { static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION); CommandReturnObject result; LoadCommandDictionary(); // An alias arguments vector to reuse - reset it before use... OptionArgVectorSP alias_arguments_vector_sp(new OptionArgVector); // Set up some initial aliases. CommandObjectSP cmd_obj_sp = GetCommandSPExact("quit", false); if (cmd_obj_sp) { AddAlias("q", cmd_obj_sp); AddAlias("exit", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("_regexp-attach", false); if (cmd_obj_sp) AddAlias("attach", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("process detach", false); if (cmd_obj_sp) { AddAlias("detach", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("process continue", false); if (cmd_obj_sp) { AddAlias("c", cmd_obj_sp); AddAlias("continue", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("_regexp-break", false); if (cmd_obj_sp) AddAlias("b", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("_regexp-tbreak", false); if (cmd_obj_sp) AddAlias("tbreak", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("thread step-inst", false); if (cmd_obj_sp) { AddAlias("stepi", cmd_obj_sp); AddAlias("si", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("thread step-inst-over", false); if (cmd_obj_sp) { AddAlias("nexti", cmd_obj_sp); AddAlias("ni", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("thread step-in", false); if (cmd_obj_sp) { AddAlias("s", cmd_obj_sp); AddAlias("step", cmd_obj_sp); CommandAlias *sif_alias = AddAlias( "sif", cmd_obj_sp, "--end-linenumber block --step-in-target %1"); if (sif_alias) { sif_alias->SetHelp("Step through the current block, stopping if you step " "directly into a function whose name matches the " "TargetFunctionName."); sif_alias->SetSyntax("sif "); } } cmd_obj_sp = GetCommandSPExact("thread step-over", false); if (cmd_obj_sp) { AddAlias("n", cmd_obj_sp); AddAlias("next", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("thread step-out", false); if (cmd_obj_sp) { AddAlias("finish", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("frame select", false); if (cmd_obj_sp) { AddAlias("f", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("thread select", false); if (cmd_obj_sp) { AddAlias("t", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("_regexp-jump", false); if (cmd_obj_sp) { AddAlias("j", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); AddAlias("jump", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); } cmd_obj_sp = GetCommandSPExact("_regexp-list", false); if (cmd_obj_sp) { AddAlias("l", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); AddAlias("list", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); } cmd_obj_sp = GetCommandSPExact("_regexp-env", false); if (cmd_obj_sp) AddAlias("env", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("memory read", false); if (cmd_obj_sp) AddAlias("x", cmd_obj_sp); cmd_obj_sp = GetCommandSPExact("_regexp-up", false); if (cmd_obj_sp) AddAlias("up", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("_regexp-down", false); if (cmd_obj_sp) AddAlias("down", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("_regexp-display", false); if (cmd_obj_sp) AddAlias("display", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("disassemble", false); if (cmd_obj_sp) AddAlias("dis", cmd_obj_sp); cmd_obj_sp = GetCommandSPExact("disassemble", false); if (cmd_obj_sp) AddAlias("di", cmd_obj_sp); cmd_obj_sp = GetCommandSPExact("_regexp-undisplay", false); if (cmd_obj_sp) AddAlias("undisplay", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("_regexp-bt", false); if (cmd_obj_sp) AddAlias("bt", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax()); cmd_obj_sp = GetCommandSPExact("target create", false); if (cmd_obj_sp) AddAlias("file", cmd_obj_sp); cmd_obj_sp = GetCommandSPExact("target modules", false); if (cmd_obj_sp) AddAlias("image", cmd_obj_sp); alias_arguments_vector_sp.reset(new OptionArgVector); cmd_obj_sp = GetCommandSPExact("expression", false); if (cmd_obj_sp) { AddAlias("p", cmd_obj_sp, "--")->SetHelpLong(""); AddAlias("print", cmd_obj_sp, "--")->SetHelpLong(""); AddAlias("call", cmd_obj_sp, "--")->SetHelpLong(""); if (auto po = AddAlias("po", cmd_obj_sp, "-O --")) { po->SetHelp("Evaluate an expression on the current thread. Displays any " "returned value with formatting " "controlled by the type's author."); po->SetHelpLong(""); } AddAlias("parray", cmd_obj_sp, "--element-count %1 --")->SetHelpLong(""); AddAlias("poarray", cmd_obj_sp, "--object-description --element-count %1 --") ->SetHelpLong(""); } cmd_obj_sp = GetCommandSPExact("process kill", false); if (cmd_obj_sp) { AddAlias("kill", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("process launch", false); if (cmd_obj_sp) { alias_arguments_vector_sp.reset(new OptionArgVector); #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) AddAlias("r", cmd_obj_sp, "--"); AddAlias("run", cmd_obj_sp, "--"); #else #if defined(__APPLE__) std::string shell_option; shell_option.append("--shell-expand-args"); shell_option.append(" true"); shell_option.append(" --"); AddAlias("r", cmd_obj_sp, "--shell-expand-args true --"); AddAlias("run", cmd_obj_sp, "--shell-expand-args true --"); #else StreamString defaultshell; defaultshell.Printf("--shell=%s --", HostInfo::GetDefaultShell().GetPath().c_str()); AddAlias("r", cmd_obj_sp, defaultshell.GetString()); AddAlias("run", cmd_obj_sp, defaultshell.GetString()); #endif #endif } cmd_obj_sp = GetCommandSPExact("target symbols add", false); if (cmd_obj_sp) { AddAlias("add-dsym", cmd_obj_sp); } cmd_obj_sp = GetCommandSPExact("breakpoint set", false); if (cmd_obj_sp) { AddAlias("rbreak", cmd_obj_sp, "--func-regex %1"); } } void CommandInterpreter::Clear() { m_command_io_handler_sp.reset(); if (m_script_interpreter_sp) m_script_interpreter_sp->Clear(); } const char *CommandInterpreter::ProcessEmbeddedScriptCommands(const char *arg) { // This function has not yet been implemented. // Look for any embedded script command // If found, // get interpreter object from the command dictionary, // call execute_one_command on it, // get the results as a string, // substitute that string for current stuff. return arg; } void CommandInterpreter::LoadCommandDictionary() { static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION); lldb::ScriptLanguage script_language = m_debugger.GetScriptLanguage(); m_command_dict["apropos"] = CommandObjectSP(new CommandObjectApropos(*this)); m_command_dict["breakpoint"] = CommandObjectSP(new CommandObjectMultiwordBreakpoint(*this)); m_command_dict["bugreport"] = CommandObjectSP(new CommandObjectMultiwordBugreport(*this)); m_command_dict["command"] = CommandObjectSP(new CommandObjectMultiwordCommands(*this)); m_command_dict["disassemble"] = CommandObjectSP(new CommandObjectDisassemble(*this)); m_command_dict["expression"] = CommandObjectSP(new CommandObjectExpression(*this)); m_command_dict["frame"] = CommandObjectSP(new CommandObjectMultiwordFrame(*this)); m_command_dict["gui"] = CommandObjectSP(new CommandObjectGUI(*this)); m_command_dict["help"] = CommandObjectSP(new CommandObjectHelp(*this)); m_command_dict["log"] = CommandObjectSP(new CommandObjectLog(*this)); m_command_dict["memory"] = CommandObjectSP(new CommandObjectMemory(*this)); m_command_dict["platform"] = CommandObjectSP(new CommandObjectPlatform(*this)); m_command_dict["plugin"] = CommandObjectSP(new CommandObjectPlugin(*this)); m_command_dict["process"] = CommandObjectSP(new CommandObjectMultiwordProcess(*this)); m_command_dict["quit"] = CommandObjectSP(new CommandObjectQuit(*this)); m_command_dict["register"] = CommandObjectSP(new CommandObjectRegister(*this)); m_command_dict["script"] = CommandObjectSP(new CommandObjectScript(*this, script_language)); m_command_dict["settings"] = CommandObjectSP(new CommandObjectMultiwordSettings(*this)); m_command_dict["source"] = CommandObjectSP(new CommandObjectMultiwordSource(*this)); m_command_dict["statistics"] = CommandObjectSP(new CommandObjectStats(*this)); m_command_dict["target"] = CommandObjectSP(new CommandObjectMultiwordTarget(*this)); m_command_dict["thread"] = CommandObjectSP(new CommandObjectMultiwordThread(*this)); m_command_dict["type"] = CommandObjectSP(new CommandObjectType(*this)); m_command_dict["version"] = CommandObjectSP(new CommandObjectVersion(*this)); m_command_dict["watchpoint"] = CommandObjectSP(new CommandObjectMultiwordWatchpoint(*this)); m_command_dict["language"] = CommandObjectSP(new CommandObjectLanguage(*this)); const char *break_regexes[][2] = { {"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$", "breakpoint set --file '%1' --line %2"}, {"^/([^/]+)/$", "breakpoint set --source-pattern-regexp '%1'"}, {"^([[:digit:]]+)[[:space:]]*$", "breakpoint set --line %1"}, {"^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", "breakpoint set --address %1"}, {"^[\"']?([-+]?\\[.*\\])[\"']?[[:space:]]*$", "breakpoint set --name '%1'"}, {"^(-.*)$", "breakpoint set %1"}, {"^(.*[^[:space:]])`(.*[^[:space:]])[[:space:]]*$", "breakpoint set --name '%2' --shlib '%1'"}, {"^\\&(.*[^[:space:]])[[:space:]]*$", "breakpoint set --name '%1' --skip-prologue=0"}, {"^[\"']?(.*[^[:space:]\"'])[\"']?[[:space:]]*$", "breakpoint set --name '%1'"}}; size_t num_regexes = llvm::array_lengthof(break_regexes); std::unique_ptr break_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-break", - "Set a breakpoint using one of several shorthand formats.\n", + "Set a breakpoint using one of several shorthand formats.", "\n" "_regexp-break :\n" " main.c:12 // Break at line 12 of " "main.c\n\n" "_regexp-break \n" " 12 // Break at line 12 of current " "file\n\n" "_regexp-break 0x
\n" " 0x1234000 // Break at address " "0x1234000\n\n" "_regexp-break \n" " main // Break in 'main' after the " "prologue\n\n" "_regexp-break &\n" " &main // Break at first instruction " "in 'main'\n\n" "_regexp-break `\n" " libc.so`malloc // Break in 'malloc' from " "'libc.so'\n\n" "_regexp-break //\n" " /break here/ // Break on source lines in " "current file\n" " // containing text 'break " "here'.\n", 2, CommandCompletions::eSymbolCompletion | CommandCompletions::eSourceFileCompletion, false)); if (break_regex_cmd_ap.get()) { bool success = true; for (size_t i = 0; i < num_regexes; i++) { success = break_regex_cmd_ap->AddRegexCommand(break_regexes[i][0], break_regexes[i][1]); if (!success) break; } success = break_regex_cmd_ap->AddRegexCommand("^$", "breakpoint list --full"); if (success) { CommandObjectSP break_regex_cmd_sp(break_regex_cmd_ap.release()); m_command_dict[break_regex_cmd_sp->GetCommandName()] = break_regex_cmd_sp; } } std::unique_ptr tbreak_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-tbreak", - "Set a one-shot breakpoint using one of several shorthand formats.\n", + "Set a one-shot breakpoint using one of several shorthand formats.", "\n" "_regexp-break :\n" " main.c:12 // Break at line 12 of " "main.c\n\n" "_regexp-break \n" " 12 // Break at line 12 of current " "file\n\n" "_regexp-break 0x
\n" " 0x1234000 // Break at address " "0x1234000\n\n" "_regexp-break \n" " main // Break in 'main' after the " "prologue\n\n" "_regexp-break &\n" " &main // Break at first instruction " "in 'main'\n\n" "_regexp-break `\n" " libc.so`malloc // Break in 'malloc' from " "'libc.so'\n\n" "_regexp-break //\n" " /break here/ // Break on source lines in " "current file\n" " // containing text 'break " "here'.\n", 2, CommandCompletions::eSymbolCompletion | CommandCompletions::eSourceFileCompletion, false)); if (tbreak_regex_cmd_ap.get()) { bool success = true; for (size_t i = 0; i < num_regexes; i++) { // If you add a resultant command string longer than 1024 characters be // sure to increase the size of this buffer. char buffer[1024]; int num_printed = snprintf(buffer, 1024, "%s %s", break_regexes[i][1], "-o 1"); lldbassert(num_printed < 1024); UNUSED_IF_ASSERT_DISABLED(num_printed); success = tbreak_regex_cmd_ap->AddRegexCommand(break_regexes[i][0], buffer); if (!success) break; } success = tbreak_regex_cmd_ap->AddRegexCommand("^$", "breakpoint list --full"); if (success) { CommandObjectSP tbreak_regex_cmd_sp(tbreak_regex_cmd_ap.release()); m_command_dict[tbreak_regex_cmd_sp->GetCommandName()] = tbreak_regex_cmd_sp; } } std::unique_ptr attach_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-attach", "Attach to process by ID or name.", "_regexp-attach | ", 2, 0, false)); if (attach_regex_cmd_ap.get()) { if (attach_regex_cmd_ap->AddRegexCommand("^([0-9]+)[[:space:]]*$", "process attach --pid %1") && attach_regex_cmd_ap->AddRegexCommand( "^(-.*|.* -.*)$", "process attach %1") && // Any options that are // specified get passed to // 'process attach' attach_regex_cmd_ap->AddRegexCommand("^(.+)$", "process attach --name '%1'") && attach_regex_cmd_ap->AddRegexCommand("^$", "process attach")) { CommandObjectSP attach_regex_cmd_sp(attach_regex_cmd_ap.release()); m_command_dict[attach_regex_cmd_sp->GetCommandName()] = attach_regex_cmd_sp; } } std::unique_ptr down_regex_cmd_ap( new CommandObjectRegexCommand(*this, "_regexp-down", "Select a newer stack frame. Defaults to " "moving one frame, a numeric argument can " "specify an arbitrary number.", "_regexp-down []", 2, 0, false)); if (down_regex_cmd_ap.get()) { if (down_regex_cmd_ap->AddRegexCommand("^$", "frame select -r -1") && down_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "frame select -r -%1")) { CommandObjectSP down_regex_cmd_sp(down_regex_cmd_ap.release()); m_command_dict[down_regex_cmd_sp->GetCommandName()] = down_regex_cmd_sp; } } std::unique_ptr up_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-up", "Select an older stack frame. Defaults to moving one " "frame, a numeric argument can specify an arbitrary number.", "_regexp-up []", 2, 0, false)); if (up_regex_cmd_ap.get()) { if (up_regex_cmd_ap->AddRegexCommand("^$", "frame select -r 1") && up_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "frame select -r %1")) { CommandObjectSP up_regex_cmd_sp(up_regex_cmd_ap.release()); m_command_dict[up_regex_cmd_sp->GetCommandName()] = up_regex_cmd_sp; } } std::unique_ptr display_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-display", "Evaluate an expression at every stop (see 'help target stop-hook'.)", "_regexp-display expression", 2, 0, false)); if (display_regex_cmd_ap.get()) { if (display_regex_cmd_ap->AddRegexCommand( "^(.+)$", "target stop-hook add -o \"expr -- %1\"")) { CommandObjectSP display_regex_cmd_sp(display_regex_cmd_ap.release()); m_command_dict[display_regex_cmd_sp->GetCommandName()] = display_regex_cmd_sp; } } std::unique_ptr undisplay_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-undisplay", "Stop displaying expression at every " "stop (specified by stop-hook index.)", "_regexp-undisplay stop-hook-number", 2, 0, false)); if (undisplay_regex_cmd_ap.get()) { if (undisplay_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "target stop-hook delete %1")) { CommandObjectSP undisplay_regex_cmd_sp(undisplay_regex_cmd_ap.release()); m_command_dict[undisplay_regex_cmd_sp->GetCommandName()] = undisplay_regex_cmd_sp; } } std::unique_ptr connect_gdb_remote_cmd_ap( new CommandObjectRegexCommand( *this, "gdb-remote", "Connect to a process via remote GDB server. " "If no host is specifed, localhost is assumed.", "gdb-remote [:]", 2, 0, false)); if (connect_gdb_remote_cmd_ap.get()) { if (connect_gdb_remote_cmd_ap->AddRegexCommand( "^([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)$", "process connect --plugin gdb-remote connect://%1:%2") && connect_gdb_remote_cmd_ap->AddRegexCommand( "^([[:digit:]]+)$", "process connect --plugin gdb-remote connect://localhost:%1")) { CommandObjectSP command_sp(connect_gdb_remote_cmd_ap.release()); m_command_dict[command_sp->GetCommandName()] = command_sp; } } std::unique_ptr connect_kdp_remote_cmd_ap( new CommandObjectRegexCommand( *this, "kdp-remote", "Connect to a process via remote KDP server. " "If no UDP port is specified, port 41139 is " "assumed.", "kdp-remote [:]", 2, 0, false)); if (connect_kdp_remote_cmd_ap.get()) { if (connect_kdp_remote_cmd_ap->AddRegexCommand( "^([^:]+:[[:digit:]]+)$", "process connect --plugin kdp-remote udp://%1") && connect_kdp_remote_cmd_ap->AddRegexCommand( "^(.+)$", "process connect --plugin kdp-remote udp://%1:41139")) { CommandObjectSP command_sp(connect_kdp_remote_cmd_ap.release()); m_command_dict[command_sp->GetCommandName()] = command_sp; } } std::unique_ptr bt_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-bt", "Show the current thread's call stack. Any numeric argument " "displays at most that many " "frames. The argument 'all' displays all threads.", "bt [ | all]", 2, 0, false)); if (bt_regex_cmd_ap.get()) { // accept but don't document "bt -c " -- before bt was a regex // command if you wanted to backtrace three frames you would do "bt -c 3" // but the intention is to have this emulate the gdb "bt" command and so // now "bt 3" is the preferred form, in line with gdb. if (bt_regex_cmd_ap->AddRegexCommand("^([[:digit:]]+)$", "thread backtrace -c %1") && bt_regex_cmd_ap->AddRegexCommand("^-c ([[:digit:]]+)$", "thread backtrace -c %1") && bt_regex_cmd_ap->AddRegexCommand("^all$", "thread backtrace all") && bt_regex_cmd_ap->AddRegexCommand("^$", "thread backtrace")) { CommandObjectSP command_sp(bt_regex_cmd_ap.release()); m_command_dict[command_sp->GetCommandName()] = command_sp; } } std::unique_ptr list_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-list", "List relevant source code using one of several shorthand formats.", "\n" "_regexp-list : // List around specific file/line\n" "_regexp-list // List current file around specified " "line\n" "_regexp-list // List specified function\n" "_regexp-list 0x
// List around specified address\n" "_regexp-list -[] // List previous lines\n" "_regexp-list // List subsequent lines", 2, CommandCompletions::eSourceFileCompletion, false)); if (list_regex_cmd_ap.get()) { if (list_regex_cmd_ap->AddRegexCommand("^([0-9]+)[[:space:]]*$", "source list --line %1") && list_regex_cmd_ap->AddRegexCommand( "^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]" "]*$", "source list --file '%1' --line %2") && list_regex_cmd_ap->AddRegexCommand( "^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", "source list --address %1") && list_regex_cmd_ap->AddRegexCommand("^-[[:space:]]*$", "source list --reverse") && list_regex_cmd_ap->AddRegexCommand( "^-([[:digit:]]+)[[:space:]]*$", "source list --reverse --count %1") && list_regex_cmd_ap->AddRegexCommand("^(.+)$", "source list --name \"%1\"") && list_regex_cmd_ap->AddRegexCommand("^$", "source list")) { CommandObjectSP list_regex_cmd_sp(list_regex_cmd_ap.release()); m_command_dict[list_regex_cmd_sp->GetCommandName()] = list_regex_cmd_sp; } } std::unique_ptr env_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-env", "Shorthand for viewing and setting environment variables.", "\n" "_regexp-env // Show enrivonment\n" "_regexp-env = // Set an environment variable", 2, 0, false)); if (env_regex_cmd_ap.get()) { if (env_regex_cmd_ap->AddRegexCommand("^$", "settings show target.env-vars") && env_regex_cmd_ap->AddRegexCommand("^([A-Za-z_][A-Za-z_0-9]*=.*)$", "settings set target.env-vars %1")) { CommandObjectSP env_regex_cmd_sp(env_regex_cmd_ap.release()); m_command_dict[env_regex_cmd_sp->GetCommandName()] = env_regex_cmd_sp; } } std::unique_ptr jump_regex_cmd_ap( new CommandObjectRegexCommand( *this, "_regexp-jump", "Set the program counter to a new address.", "\n" "_regexp-jump \n" "_regexp-jump + | -\n" "_regexp-jump :\n" "_regexp-jump *\n", 2, 0, false)); if (jump_regex_cmd_ap.get()) { if (jump_regex_cmd_ap->AddRegexCommand("^\\*(.*)$", "thread jump --addr %1") && jump_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "thread jump --line %1") && jump_regex_cmd_ap->AddRegexCommand("^([^:]+):([0-9]+)$", "thread jump --file %1 --line %2") && jump_regex_cmd_ap->AddRegexCommand("^([+\\-][0-9]+)$", "thread jump --by %1")) { CommandObjectSP jump_regex_cmd_sp(jump_regex_cmd_ap.release()); m_command_dict[jump_regex_cmd_sp->GetCommandName()] = jump_regex_cmd_sp; } } } int CommandInterpreter::GetCommandNamesMatchingPartialString( const char *cmd_str, bool include_aliases, StringList &matches) { AddNamesMatchingPartialString(m_command_dict, cmd_str, matches); if (include_aliases) { AddNamesMatchingPartialString(m_alias_dict, cmd_str, matches); } return matches.GetSize(); } CommandObjectSP CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases, bool exact, StringList *matches) const { CommandObjectSP command_sp; std::string cmd = cmd_str; if (HasCommands()) { auto pos = m_command_dict.find(cmd); if (pos != m_command_dict.end()) command_sp = pos->second; } if (include_aliases && HasAliases()) { auto alias_pos = m_alias_dict.find(cmd); if (alias_pos != m_alias_dict.end()) command_sp = alias_pos->second; } if (HasUserCommands()) { auto pos = m_user_dict.find(cmd); if (pos != m_user_dict.end()) command_sp = pos->second; } if (!exact && !command_sp) { // We will only get into here if we didn't find any exact matches. CommandObjectSP user_match_sp, alias_match_sp, real_match_sp; StringList local_matches; if (matches == nullptr) matches = &local_matches; unsigned int num_cmd_matches = 0; unsigned int num_alias_matches = 0; unsigned int num_user_matches = 0; // Look through the command dictionaries one by one, and if we get only one // match from any of them in toto, then return that, otherwise return an // empty CommandObjectSP and the list of matches. if (HasCommands()) { num_cmd_matches = AddNamesMatchingPartialString(m_command_dict, cmd_str, *matches); } if (num_cmd_matches == 1) { cmd.assign(matches->GetStringAtIndex(0)); auto pos = m_command_dict.find(cmd); if (pos != m_command_dict.end()) real_match_sp = pos->second; } if (include_aliases && HasAliases()) { num_alias_matches = AddNamesMatchingPartialString(m_alias_dict, cmd_str, *matches); } if (num_alias_matches == 1) { cmd.assign(matches->GetStringAtIndex(num_cmd_matches)); auto alias_pos = m_alias_dict.find(cmd); if (alias_pos != m_alias_dict.end()) alias_match_sp = alias_pos->second; } if (HasUserCommands()) { num_user_matches = AddNamesMatchingPartialString(m_user_dict, cmd_str, *matches); } if (num_user_matches == 1) { cmd.assign( matches->GetStringAtIndex(num_cmd_matches + num_alias_matches)); auto pos = m_user_dict.find(cmd); if (pos != m_user_dict.end()) user_match_sp = pos->second; } // If we got exactly one match, return that, otherwise return the match // list. if (num_user_matches + num_cmd_matches + num_alias_matches == 1) { if (num_cmd_matches) return real_match_sp; else if (num_alias_matches) return alias_match_sp; else return user_match_sp; } } else if (matches && command_sp) { matches->AppendString(cmd_str); } return command_sp; } bool CommandInterpreter::AddCommand(llvm::StringRef name, const lldb::CommandObjectSP &cmd_sp, bool can_replace) { if (cmd_sp.get()) lldbassert((this == &cmd_sp->GetCommandInterpreter()) && "tried to add a CommandObject from a different interpreter"); if (name.empty()) return false; std::string name_sstr(name); auto name_iter = m_command_dict.find(name_sstr); if (name_iter != m_command_dict.end()) { if (!can_replace || !name_iter->second->IsRemovable()) return false; name_iter->second = cmd_sp; } else { m_command_dict[name_sstr] = cmd_sp; } return true; } bool CommandInterpreter::AddUserCommand(llvm::StringRef name, const lldb::CommandObjectSP &cmd_sp, bool can_replace) { if (cmd_sp.get()) lldbassert((this == &cmd_sp->GetCommandInterpreter()) && "tried to add a CommandObject from a different interpreter"); if (!name.empty()) { // do not allow replacement of internal commands if (CommandExists(name)) { if (can_replace == false) return false; if (m_command_dict[name]->IsRemovable() == false) return false; } if (UserCommandExists(name)) { if (can_replace == false) return false; if (m_user_dict[name]->IsRemovable() == false) return false; } m_user_dict[name] = cmd_sp; return true; } return false; } CommandObjectSP CommandInterpreter::GetCommandSPExact(llvm::StringRef cmd_str, bool include_aliases) const { Args cmd_words(cmd_str); // Break up the command string into words, in case // it's a multi-word command. CommandObjectSP ret_val; // Possibly empty return value. if (cmd_str.empty()) return ret_val; if (cmd_words.GetArgumentCount() == 1) return GetCommandSP(cmd_str, include_aliases, true, nullptr); else { // We have a multi-word command (seemingly), so we need to do more work. // First, get the cmd_obj_sp for the first word in the command. CommandObjectSP cmd_obj_sp = GetCommandSP(llvm::StringRef(cmd_words.GetArgumentAtIndex(0)), include_aliases, true, nullptr); if (cmd_obj_sp.get() != nullptr) { // Loop through the rest of the words in the command (everything passed // in was supposed to be part of a command name), and find the // appropriate sub-command SP for each command word.... size_t end = cmd_words.GetArgumentCount(); for (size_t j = 1; j < end; ++j) { if (cmd_obj_sp->IsMultiwordObject()) { cmd_obj_sp = cmd_obj_sp->GetSubcommandSP(cmd_words.GetArgumentAtIndex(j)); if (cmd_obj_sp.get() == nullptr) // The sub-command name was invalid. Fail and return the empty // 'ret_val'. return ret_val; } else // We have more words in the command name, but we don't have a // multiword object. Fail and return empty 'ret_val'. return ret_val; } // We successfully looped through all the command words and got valid // command objects for them. Assign the last object retrieved to // 'ret_val'. ret_val = cmd_obj_sp; } } return ret_val; } CommandObject *CommandInterpreter::GetCommandObject(llvm::StringRef cmd_str, StringList *matches) const { CommandObject *command_obj = GetCommandSP(cmd_str, false, true, matches).get(); // If we didn't find an exact match to the command string in the commands, // look in the aliases. if (command_obj) return command_obj; command_obj = GetCommandSP(cmd_str, true, true, matches).get(); if (command_obj) return command_obj; // If there wasn't an exact match then look for an inexact one in just the // commands command_obj = GetCommandSP(cmd_str, false, false, nullptr).get(); // Finally, if there wasn't an inexact match among the commands, look for an // inexact match in both the commands and aliases. if (command_obj) { if (matches) matches->AppendString(command_obj->GetCommandName()); return command_obj; } return GetCommandSP(cmd_str, true, false, matches).get(); } bool CommandInterpreter::CommandExists(llvm::StringRef cmd) const { return m_command_dict.find(cmd) != m_command_dict.end(); } bool CommandInterpreter::GetAliasFullName(llvm::StringRef cmd, std::string &full_name) const { bool exact_match = (m_alias_dict.find(cmd) != m_alias_dict.end()); if (exact_match) { full_name.assign(cmd); return exact_match; } else { StringList matches; size_t num_alias_matches; num_alias_matches = AddNamesMatchingPartialString(m_alias_dict, cmd, matches); if (num_alias_matches == 1) { // Make sure this isn't shadowing a command in the regular command space: StringList regular_matches; const bool include_aliases = false; const bool exact = false; CommandObjectSP cmd_obj_sp( GetCommandSP(cmd, include_aliases, exact, ®ular_matches)); if (cmd_obj_sp || regular_matches.GetSize() > 0) return false; else { full_name.assign(matches.GetStringAtIndex(0)); return true; } } else return false; } } bool CommandInterpreter::AliasExists(llvm::StringRef cmd) const { return m_alias_dict.find(cmd) != m_alias_dict.end(); } bool CommandInterpreter::UserCommandExists(llvm::StringRef cmd) const { return m_user_dict.find(cmd) != m_user_dict.end(); } CommandAlias * CommandInterpreter::AddAlias(llvm::StringRef alias_name, lldb::CommandObjectSP &command_obj_sp, llvm::StringRef args_string) { if (command_obj_sp.get()) lldbassert((this == &command_obj_sp->GetCommandInterpreter()) && "tried to add a CommandObject from a different interpreter"); std::unique_ptr command_alias_up( new CommandAlias(*this, command_obj_sp, args_string, alias_name)); if (command_alias_up && command_alias_up->IsValid()) { m_alias_dict[alias_name] = CommandObjectSP(command_alias_up.get()); return command_alias_up.release(); } return nullptr; } bool CommandInterpreter::RemoveAlias(llvm::StringRef alias_name) { auto pos = m_alias_dict.find(alias_name); if (pos != m_alias_dict.end()) { m_alias_dict.erase(pos); return true; } return false; } bool CommandInterpreter::RemoveCommand(llvm::StringRef cmd) { auto pos = m_command_dict.find(cmd); if (pos != m_command_dict.end()) { if (pos->second->IsRemovable()) { // Only regular expression objects or python commands are removable m_command_dict.erase(pos); return true; } } return false; } bool CommandInterpreter::RemoveUser(llvm::StringRef alias_name) { CommandObject::CommandMap::iterator pos = m_user_dict.find(alias_name); if (pos != m_user_dict.end()) { m_user_dict.erase(pos); return true; } return false; } void CommandInterpreter::GetHelp(CommandReturnObject &result, uint32_t cmd_types) { llvm::StringRef help_prologue(GetDebugger().GetIOHandlerHelpPrologue()); if (!help_prologue.empty()) { OutputFormattedHelpText(result.GetOutputStream(), llvm::StringRef(), help_prologue); } CommandObject::CommandMap::const_iterator pos; size_t max_len = FindLongestCommandWord(m_command_dict); if ((cmd_types & eCommandTypesBuiltin) == eCommandTypesBuiltin) { result.AppendMessage("Debugger commands:"); result.AppendMessage(""); for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) { if (!(cmd_types & eCommandTypesHidden) && (pos->first.compare(0, 1, "_") == 0)) continue; OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--", pos->second->GetHelp(), max_len); } result.AppendMessage(""); } if (!m_alias_dict.empty() && ((cmd_types & eCommandTypesAliases) == eCommandTypesAliases)) { result.AppendMessageWithFormat( "Current command abbreviations " "(type '%shelp command alias' for more info):\n", GetCommandPrefix()); result.AppendMessage(""); max_len = FindLongestCommandWord(m_alias_dict); for (auto alias_pos = m_alias_dict.begin(); alias_pos != m_alias_dict.end(); ++alias_pos) { OutputFormattedHelpText(result.GetOutputStream(), alias_pos->first, "--", alias_pos->second->GetHelp(), max_len); } result.AppendMessage(""); } if (!m_user_dict.empty() && ((cmd_types & eCommandTypesUserDef) == eCommandTypesUserDef)) { result.AppendMessage("Current user-defined commands:"); result.AppendMessage(""); max_len = FindLongestCommandWord(m_user_dict); for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos) { OutputFormattedHelpText(result.GetOutputStream(), pos->first, "--", pos->second->GetHelp(), max_len); } result.AppendMessage(""); } result.AppendMessageWithFormat( "For more information on any command, type '%shelp '.\n", GetCommandPrefix()); } CommandObject *CommandInterpreter::GetCommandObjectForCommand( llvm::StringRef &command_string) { // This function finds the final, lowest-level, alias-resolved command object // whose 'Execute' function will eventually be invoked by the given command // line. CommandObject *cmd_obj = nullptr; size_t start = command_string.find_first_not_of(k_white_space); size_t end = 0; bool done = false; while (!done) { if (start != std::string::npos) { // Get the next word from command_string. end = command_string.find_first_of(k_white_space, start); if (end == std::string::npos) end = command_string.size(); std::string cmd_word = command_string.substr(start, end - start); if (cmd_obj == nullptr) // Since cmd_obj is NULL we are on our first time through this loop. // Check to see if cmd_word is a valid command or alias. cmd_obj = GetCommandObject(cmd_word); else if (cmd_obj->IsMultiwordObject()) { // Our current object is a multi-word object; see if the cmd_word is a // valid sub-command for our object. CommandObject *sub_cmd_obj = cmd_obj->GetSubcommandObject(cmd_word.c_str()); if (sub_cmd_obj) cmd_obj = sub_cmd_obj; else // cmd_word was not a valid sub-command word, so we are done done = true; } else // We have a cmd_obj and it is not a multi-word object, so we are done. done = true; // If we didn't find a valid command object, or our command object is not // a multi-word object, or we are at the end of the command_string, then // we are done. Otherwise, find the start of the next word. if (!cmd_obj || !cmd_obj->IsMultiwordObject() || end >= command_string.size()) done = true; else start = command_string.find_first_not_of(k_white_space, end); } else // Unable to find any more words. done = true; } command_string = command_string.substr(end); return cmd_obj; } static const char *k_valid_command_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; static void StripLeadingSpaces(std::string &s) { if (!s.empty()) { size_t pos = s.find_first_not_of(k_white_space); if (pos == std::string::npos) s.clear(); else if (pos == 0) return; s.erase(0, pos); } } static size_t FindArgumentTerminator(const std::string &s) { const size_t s_len = s.size(); size_t offset = 0; while (offset < s_len) { size_t pos = s.find("--", offset); if (pos == std::string::npos) break; if (pos > 0) { if (isspace(s[pos - 1])) { // Check if the string ends "\s--" (where \s is a space character) or // if we have "\s--\s". if ((pos + 2 >= s_len) || isspace(s[pos + 2])) { return pos; } } } offset = pos + 2; } return std::string::npos; } static bool ExtractCommand(std::string &command_string, std::string &command, std::string &suffix, char "e_char) { command.clear(); suffix.clear(); StripLeadingSpaces(command_string); bool result = false; quote_char = '\0'; if (!command_string.empty()) { const char first_char = command_string[0]; if (first_char == '\'' || first_char == '"') { quote_char = first_char; const size_t end_quote_pos = command_string.find(quote_char, 1); if (end_quote_pos == std::string::npos) { command.swap(command_string); command_string.erase(); } else { command.assign(command_string, 1, end_quote_pos - 1); if (end_quote_pos + 1 < command_string.size()) command_string.erase(0, command_string.find_first_not_of( k_white_space, end_quote_pos + 1)); else command_string.erase(); } } else { const size_t first_space_pos = command_string.find_first_of(k_white_space); if (first_space_pos == std::string::npos) { command.swap(command_string); command_string.erase(); } else { command.assign(command_string, 0, first_space_pos); command_string.erase(0, command_string.find_first_not_of( k_white_space, first_space_pos)); } } result = true; } if (!command.empty()) { // actual commands can't start with '-' or '_' if (command[0] != '-' && command[0] != '_') { size_t pos = command.find_first_not_of(k_valid_command_chars); if (pos > 0 && pos != std::string::npos) { suffix.assign(command.begin() + pos, command.end()); command.erase(pos); } } } return result; } CommandObject *CommandInterpreter::BuildAliasResult( llvm::StringRef alias_name, std::string &raw_input_string, std::string &alias_result, CommandReturnObject &result) { CommandObject *alias_cmd_obj = nullptr; Args cmd_args(raw_input_string); alias_cmd_obj = GetCommandObject(alias_name); StreamString result_str; if (!alias_cmd_obj || !alias_cmd_obj->IsAlias()) { alias_result.clear(); return alias_cmd_obj; } std::pair desugared = ((CommandAlias *)alias_cmd_obj)->Desugar(); OptionArgVectorSP option_arg_vector_sp = desugared.second; alias_cmd_obj = desugared.first.get(); std::string alias_name_str = alias_name; if ((cmd_args.GetArgumentCount() == 0) || (alias_name_str.compare(cmd_args.GetArgumentAtIndex(0)) != 0)) cmd_args.Unshift(alias_name_str); result_str.Printf("%s", alias_cmd_obj->GetCommandName().str().c_str()); if (!option_arg_vector_sp.get()) { alias_result = result_str.GetString(); return alias_cmd_obj; } OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); int value_type; std::string option; std::string value; for (const auto &entry : *option_arg_vector) { std::tie(option, value_type, value) = entry; if (option == "") { result_str.Printf(" %s", value.c_str()); continue; } result_str.Printf(" %s", option.c_str()); if (value_type == OptionParser::eNoArgument) continue; if (value_type != OptionParser::eOptionalArgument) result_str.Printf(" "); int index = GetOptionArgumentPosition(value.c_str()); if (index == 0) result_str.Printf("%s", value.c_str()); else if (static_cast(index) >= cmd_args.GetArgumentCount()) { result.AppendErrorWithFormat("Not enough arguments provided; you " "need at least %d arguments to use " "this alias.\n", index); result.SetStatus(eReturnStatusFailed); return nullptr; } else { size_t strpos = raw_input_string.find(cmd_args.GetArgumentAtIndex(index)); if (strpos != std::string::npos) raw_input_string = raw_input_string.erase( strpos, strlen(cmd_args.GetArgumentAtIndex(index))); result_str.Printf("%s", cmd_args.GetArgumentAtIndex(index)); } } alias_result = result_str.GetString(); return alias_cmd_obj; } Status CommandInterpreter::PreprocessCommand(std::string &command) { // The command preprocessor needs to do things to the command line before any // parsing of arguments or anything else is done. The only current stuff that // gets preprocessed is anything enclosed in backtick ('`') characters is // evaluated as an expression and the result of the expression must be a // scalar that can be substituted into the command. An example would be: // (lldb) memory read `$rsp + 20` Status error; // Status for any expressions that might not evaluate size_t start_backtick; size_t pos = 0; while ((start_backtick = command.find('`', pos)) != std::string::npos) { if (start_backtick > 0 && command[start_backtick - 1] == '\\') { // The backtick was preceded by a '\' character, remove the slash and // don't treat the backtick as the start of an expression command.erase(start_backtick - 1, 1); // No need to add one to start_backtick since we just deleted a char pos = start_backtick; } else { const size_t expr_content_start = start_backtick + 1; const size_t end_backtick = command.find('`', expr_content_start); if (end_backtick == std::string::npos) return error; else if (end_backtick == expr_content_start) { // Empty expression (two backticks in a row) command.erase(start_backtick, 2); } else { std::string expr_str(command, expr_content_start, end_backtick - expr_content_start); ExecutionContext exe_ctx(GetExecutionContext()); Target *target = exe_ctx.GetTargetPtr(); // Get a dummy target to allow for calculator mode while processing // backticks. This also helps break the infinite loop caused when // target is null. if (!target) target = m_debugger.GetDummyTarget(); if (target) { ValueObjectSP expr_result_valobj_sp; EvaluateExpressionOptions options; options.SetCoerceToId(false); options.SetUnwindOnError(true); options.SetIgnoreBreakpoints(true); options.SetKeepInMemory(false); options.SetTryAllThreads(true); options.SetTimeout(llvm::None); ExpressionResults expr_result = target->EvaluateExpression( expr_str.c_str(), exe_ctx.GetFramePtr(), expr_result_valobj_sp, options); if (expr_result == eExpressionCompleted) { Scalar scalar; if (expr_result_valobj_sp) expr_result_valobj_sp = expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable( expr_result_valobj_sp->GetDynamicValueType(), true); if (expr_result_valobj_sp->ResolveValue(scalar)) { command.erase(start_backtick, end_backtick - start_backtick + 1); StreamString value_strm; const bool show_type = false; scalar.GetValue(&value_strm, show_type); size_t value_string_size = value_strm.GetSize(); if (value_string_size) { command.insert(start_backtick, value_strm.GetString()); pos = start_backtick + value_string_size; continue; } else { error.SetErrorStringWithFormat("expression value didn't result " "in a scalar value for the " "expression '%s'", expr_str.c_str()); } } else { error.SetErrorStringWithFormat("expression value didn't result " "in a scalar value for the " "expression '%s'", expr_str.c_str()); } } else { if (expr_result_valobj_sp) error = expr_result_valobj_sp->GetError(); if (error.Success()) { switch (expr_result) { case eExpressionSetupError: error.SetErrorStringWithFormat( "expression setup error for the expression '%s'", expr_str.c_str()); break; case eExpressionParseError: error.SetErrorStringWithFormat( "expression parse error for the expression '%s'", expr_str.c_str()); break; case eExpressionResultUnavailable: error.SetErrorStringWithFormat( "expression error fetching result for the expression '%s'", expr_str.c_str()); break; case eExpressionCompleted: break; case eExpressionDiscarded: error.SetErrorStringWithFormat( "expression discarded for the expression '%s'", expr_str.c_str()); break; case eExpressionInterrupted: error.SetErrorStringWithFormat( "expression interrupted for the expression '%s'", expr_str.c_str()); break; case eExpressionHitBreakpoint: error.SetErrorStringWithFormat( "expression hit breakpoint for the expression '%s'", expr_str.c_str()); break; case eExpressionTimedOut: error.SetErrorStringWithFormat( "expression timed out for the expression '%s'", expr_str.c_str()); break; case eExpressionStoppedForDebug: error.SetErrorStringWithFormat("expression stop at entry point " "for debugging for the " "expression '%s'", expr_str.c_str()); break; } } } } } if (error.Fail()) break; } } return error; } bool CommandInterpreter::HandleCommand(const char *command_line, LazyBool lazy_add_to_history, CommandReturnObject &result, ExecutionContext *override_context, bool repeat_on_empty_command, bool no_context_switching) { std::string command_string(command_line); std::string original_command_string(command_line); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_COMMANDS)); llvm::PrettyStackTraceFormat stack_trace("HandleCommand(command = \"%s\")", command_line); if (log) log->Printf("Processing command: %s", command_line); static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer(func_cat, "Handling command: %s.", command_line); if (!no_context_switching) UpdateExecutionContext(override_context); if (WasInterrupted()) { result.AppendError("interrupted"); result.SetStatus(eReturnStatusFailed); return false; } bool add_to_history; if (lazy_add_to_history == eLazyBoolCalculate) add_to_history = (m_command_source_depth == 0); else add_to_history = (lazy_add_to_history == eLazyBoolYes); bool empty_command = false; bool comment_command = false; if (command_string.empty()) empty_command = true; else { const char *k_space_characters = "\t\n\v\f\r "; size_t non_space = command_string.find_first_not_of(k_space_characters); // Check for empty line or comment line (lines whose first non-space // character is the comment character for this interpreter) if (non_space == std::string::npos) empty_command = true; else if (command_string[non_space] == m_comment_char) comment_command = true; else if (command_string[non_space] == CommandHistory::g_repeat_char) { llvm::StringRef search_str(command_string); search_str = search_str.drop_front(non_space); if (auto hist_str = m_command_history.FindString(search_str)) { add_to_history = false; command_string = *hist_str; original_command_string = *hist_str; } else { result.AppendErrorWithFormat("Could not find entry: %s in history", command_string.c_str()); result.SetStatus(eReturnStatusFailed); return false; } } } if (empty_command) { if (repeat_on_empty_command) { if (m_command_history.IsEmpty()) { result.AppendError("empty command"); result.SetStatus(eReturnStatusFailed); return false; } else { command_line = m_repeat_command.c_str(); command_string = command_line; original_command_string = command_line; if (m_repeat_command.empty()) { result.AppendErrorWithFormat("No auto repeat.\n"); result.SetStatus(eReturnStatusFailed); return false; } } add_to_history = false; } else { result.SetStatus(eReturnStatusSuccessFinishNoResult); return true; } } else if (comment_command) { result.SetStatus(eReturnStatusSuccessFinishNoResult); return true; } Status error(PreprocessCommand(command_string)); if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } // Phase 1. // Before we do ANY kind of argument processing, we need to figure out what // the real/final command object is for the specified command. This gets // complicated by the fact that the user could have specified an alias, and, // in translating the alias, there may also be command options and/or even // data (including raw text strings) that need to be found and inserted into // the command line as part of the translation. So this first step is plain // look-up and replacement, resulting in: // 1. the command object whose Execute method will actually be called // 2. a revised command string, with all substitutions and replacements // taken care of // From 1 above, we can determine whether the Execute function wants raw // input or not. CommandObject *cmd_obj = ResolveCommandImpl(command_string, result); // Although the user may have abbreviated the command, the command_string now // has the command expanded to the full name. For example, if the input was // "br s -n main", command_string is now "breakpoint set -n main". if (log) { llvm::StringRef command_name = cmd_obj ? cmd_obj->GetCommandName() : ""; log->Printf("HandleCommand, cmd_obj : '%s'", command_name.str().c_str()); log->Printf("HandleCommand, (revised) command_string: '%s'", command_string.c_str()); const bool wants_raw_input = (cmd_obj != NULL) ? cmd_obj->WantsRawCommandString() : false; log->Printf("HandleCommand, wants_raw_input:'%s'", wants_raw_input ? "True" : "False"); } // Phase 2. // Take care of things like setting up the history command & calling the // appropriate Execute method on the CommandObject, with the appropriate // arguments. if (cmd_obj != nullptr) { if (add_to_history) { Args command_args(command_string); const char *repeat_command = cmd_obj->GetRepeatCommand(command_args, 0); if (repeat_command != nullptr) m_repeat_command.assign(repeat_command); else m_repeat_command.assign(original_command_string); m_command_history.AppendString(original_command_string); } std::string remainder; const std::size_t actual_cmd_name_len = cmd_obj->GetCommandName().size(); if (actual_cmd_name_len < command_string.length()) remainder = command_string.substr(actual_cmd_name_len); // Remove any initial spaces size_t pos = remainder.find_first_not_of(k_white_space); if (pos != 0 && pos != std::string::npos) remainder.erase(0, pos); if (log) log->Printf( "HandleCommand, command line after removing command name(s): '%s'", remainder.c_str()); cmd_obj->Execute(remainder.c_str(), result); } if (log) log->Printf("HandleCommand, command %s", (result.Succeeded() ? "succeeded" : "did not succeed")); return result.Succeeded(); } int CommandInterpreter::HandleCompletionMatches(CompletionRequest &request) { - auto &matches = request.GetMatches(); int num_command_matches = 0; bool look_for_subcommand = false; // For any of the command completions a unique match will be a complete word. request.SetWordComplete(true); if (request.GetCursorIndex() == -1) { // We got nothing on the command line, so return the list of commands bool include_aliases = true; + StringList new_matches; num_command_matches = - GetCommandNamesMatchingPartialString("", include_aliases, matches); + GetCommandNamesMatchingPartialString("", include_aliases, new_matches); + request.AddCompletions(new_matches); } else if (request.GetCursorIndex() == 0) { // The cursor is in the first argument, so just do a lookup in the // dictionary. + StringList new_matches; CommandObject *cmd_obj = GetCommandObject( - request.GetParsedLine().GetArgumentAtIndex(0), &matches); - num_command_matches = matches.GetSize(); + request.GetParsedLine().GetArgumentAtIndex(0), &new_matches); if (num_command_matches == 1 && cmd_obj && cmd_obj->IsMultiwordObject() && - matches.GetStringAtIndex(0) != nullptr && + new_matches.GetStringAtIndex(0) != nullptr && strcmp(request.GetParsedLine().GetArgumentAtIndex(0), - matches.GetStringAtIndex(0)) == 0) { + new_matches.GetStringAtIndex(0)) == 0) { if (request.GetParsedLine().GetArgumentCount() == 1) { request.SetWordComplete(true); } else { look_for_subcommand = true; num_command_matches = 0; - matches.DeleteStringAtIndex(0); + new_matches.DeleteStringAtIndex(0); request.GetParsedLine().AppendArgument(llvm::StringRef()); request.SetCursorIndex(request.GetCursorIndex() + 1); request.SetCursorCharPosition(0); } } + request.AddCompletions(new_matches); + num_command_matches = request.GetNumberOfMatches(); } if (request.GetCursorIndex() > 0 || look_for_subcommand) { // We are completing further on into a commands arguments, so find the // command and tell it to complete the command. First see if there is a // matching initial command: CommandObject *command_object = GetCommandObject(request.GetParsedLine().GetArgumentAtIndex(0)); if (command_object == nullptr) { return 0; } else { request.GetParsedLine().Shift(); request.SetCursorIndex(request.GetCursorIndex() - 1); num_command_matches = command_object->HandleCompletion(request); } } return num_command_matches; } int CommandInterpreter::HandleCompletion( const char *current_line, const char *cursor, const char *last_char, int match_start_point, int max_return_elements, StringList &matches) { llvm::StringRef command_line(current_line, last_char - current_line); CompletionRequest request(command_line, cursor - current_line, match_start_point, max_return_elements, matches); // Don't complete comments, and if the line we are completing is just the // history repeat character, substitute the appropriate history line. const char *first_arg = request.GetParsedLine().GetArgumentAtIndex(0); if (first_arg) { if (first_arg[0] == m_comment_char) return 0; else if (first_arg[0] == CommandHistory::g_repeat_char) { if (auto hist_str = m_command_history.FindString(first_arg)) { - request.GetMatches().Clear(); - request.GetMatches().InsertStringAtIndex(0, *hist_str); + matches.InsertStringAtIndex(0, *hist_str); return -2; } else return 0; } } // Only max_return_elements == -1 is supported at present: lldbassert(max_return_elements == -1); int num_command_matches = HandleCompletionMatches(request); if (num_command_matches <= 0) return num_command_matches; if (request.GetParsedLine().GetArgumentCount() == 0) { // If we got an empty string, insert nothing. matches.InsertStringAtIndex(0, ""); } else { // Now figure out if there is a common substring, and if so put that in // element 0, otherwise put an empty string in element 0. std::string command_partial_str = request.GetCursorArgumentPrefix().str(); std::string common_prefix; matches.LongestCommonPrefix(common_prefix); const size_t partial_name_len = command_partial_str.size(); common_prefix.erase(0, partial_name_len); // If we matched a unique single command, add a space... Only do this if // the completer told us this was a complete word, however... if (num_command_matches == 1 && request.GetWordComplete()) { char quote_char = request.GetParsedLine()[request.GetCursorIndex()].quote; common_prefix = Args::EscapeLLDBCommandArgument(common_prefix, quote_char); if (quote_char != '\0') common_prefix.push_back(quote_char); common_prefix.push_back(' '); } - request.GetMatches().InsertStringAtIndex(0, common_prefix.c_str()); + matches.InsertStringAtIndex(0, common_prefix.c_str()); } return num_command_matches; } CommandInterpreter::~CommandInterpreter() {} void CommandInterpreter::UpdatePrompt(llvm::StringRef new_prompt) { EventSP prompt_change_event_sp( new Event(eBroadcastBitResetPrompt, new EventDataBytes(new_prompt))); ; BroadcastEvent(prompt_change_event_sp); if (m_command_io_handler_sp) m_command_io_handler_sp->SetPrompt(new_prompt); } bool CommandInterpreter::Confirm(llvm::StringRef message, bool default_answer) { // Check AutoConfirm first: if (m_debugger.GetAutoConfirm()) return default_answer; IOHandlerConfirm *confirm = new IOHandlerConfirm(m_debugger, message, default_answer); IOHandlerSP io_handler_sp(confirm); m_debugger.RunIOHandler(io_handler_sp); return confirm->GetResponse(); } const CommandAlias * CommandInterpreter::GetAlias(llvm::StringRef alias_name) const { OptionArgVectorSP ret_val; auto pos = m_alias_dict.find(alias_name); if (pos != m_alias_dict.end()) return (CommandAlias *)pos->second.get(); return nullptr; } bool CommandInterpreter::HasCommands() const { return (!m_command_dict.empty()); } bool CommandInterpreter::HasAliases() const { return (!m_alias_dict.empty()); } bool CommandInterpreter::HasUserCommands() const { return (!m_user_dict.empty()); } bool CommandInterpreter::HasAliasOptions() const { return HasAliases(); } void CommandInterpreter::BuildAliasCommandArgs(CommandObject *alias_cmd_obj, const char *alias_name, Args &cmd_args, std::string &raw_input_string, CommandReturnObject &result) { OptionArgVectorSP option_arg_vector_sp = GetAlias(alias_name)->GetOptionArguments(); bool wants_raw_input = alias_cmd_obj->WantsRawCommandString(); // Make sure that the alias name is the 0th element in cmd_args std::string alias_name_str = alias_name; if (alias_name_str.compare(cmd_args.GetArgumentAtIndex(0)) != 0) cmd_args.Unshift(alias_name_str); Args new_args(alias_cmd_obj->GetCommandName()); if (new_args.GetArgumentCount() == 2) new_args.Shift(); if (option_arg_vector_sp.get()) { if (wants_raw_input) { // We have a command that both has command options and takes raw input. // Make *sure* it has a " -- " in the right place in the // raw_input_string. size_t pos = raw_input_string.find(" -- "); if (pos == std::string::npos) { // None found; assume it goes at the beginning of the raw input string raw_input_string.insert(0, " -- "); } } OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); const size_t old_size = cmd_args.GetArgumentCount(); std::vector used(old_size + 1, false); used[0] = true; int value_type; std::string option; std::string value; for (const auto &option_entry : *option_arg_vector) { std::tie(option, value_type, value) = option_entry; if (option == "") { if (!wants_raw_input || (value != "--")) { // Since we inserted this above, make sure we don't insert it twice new_args.AppendArgument(value); } continue; } if (value_type != OptionParser::eOptionalArgument) new_args.AppendArgument(option); if (value == "") continue; int index = GetOptionArgumentPosition(value.c_str()); if (index == 0) { // value was NOT a positional argument; must be a real value if (value_type != OptionParser::eOptionalArgument) new_args.AppendArgument(value); else { char buffer[255]; ::snprintf(buffer, sizeof(buffer), "%s%s", option.c_str(), value.c_str()); new_args.AppendArgument(llvm::StringRef(buffer)); } } else if (static_cast(index) >= cmd_args.GetArgumentCount()) { result.AppendErrorWithFormat("Not enough arguments provided; you " "need at least %d arguments to use " "this alias.\n", index); result.SetStatus(eReturnStatusFailed); return; } else { // Find and remove cmd_args.GetArgumentAtIndex(i) from raw_input_string size_t strpos = raw_input_string.find(cmd_args.GetArgumentAtIndex(index)); if (strpos != std::string::npos) { raw_input_string = raw_input_string.erase( strpos, strlen(cmd_args.GetArgumentAtIndex(index))); } if (value_type != OptionParser::eOptionalArgument) new_args.AppendArgument(cmd_args.GetArgumentAtIndex(index)); else { char buffer[255]; ::snprintf(buffer, sizeof(buffer), "%s%s", option.c_str(), cmd_args.GetArgumentAtIndex(index)); new_args.AppendArgument(buffer); } used[index] = true; } } for (auto entry : llvm::enumerate(cmd_args.entries())) { if (!used[entry.index()] && !wants_raw_input) new_args.AppendArgument(entry.value().ref); } cmd_args.Clear(); cmd_args.SetArguments(new_args.GetArgumentCount(), new_args.GetConstArgumentVector()); } else { result.SetStatus(eReturnStatusSuccessFinishNoResult); // This alias was not created with any options; nothing further needs to be // done, unless it is a command that wants raw input, in which case we need // to clear the rest of the data from cmd_args, since its in the raw input // string. if (wants_raw_input) { cmd_args.Clear(); cmd_args.SetArguments(new_args.GetArgumentCount(), new_args.GetConstArgumentVector()); } return; } result.SetStatus(eReturnStatusSuccessFinishNoResult); return; } int CommandInterpreter::GetOptionArgumentPosition(const char *in_string) { int position = 0; // Any string that isn't an argument position, i.e. '%' // followed by an integer, gets a position // of zero. const char *cptr = in_string; // Does it start with '%' if (cptr[0] == '%') { ++cptr; // Is the rest of it entirely digits? if (isdigit(cptr[0])) { const char *start = cptr; while (isdigit(cptr[0])) ++cptr; // We've gotten to the end of the digits; are we at the end of the // string? if (cptr[0] == '\0') position = atoi(start); } } return position; } void CommandInterpreter::SourceInitFile(bool in_cwd, CommandReturnObject &result) { FileSpec init_file; if (in_cwd) { ExecutionContext exe_ctx(GetExecutionContext()); Target *target = exe_ctx.GetTargetPtr(); if (target) { // In the current working directory we don't load any program specific // .lldbinit files, we only look for a ".lldbinit" file. if (m_skip_lldbinit_files) return; LoadCWDlldbinitFile should_load = target->TargetProperties::GetLoadCWDlldbinitFile(); if (should_load == eLoadCWDlldbinitWarn) { FileSpec dot_lldb(".lldbinit", true); llvm::SmallString<64> home_dir_path; llvm::sys::path::home_directory(home_dir_path); FileSpec homedir_dot_lldb(home_dir_path.c_str(), false); homedir_dot_lldb.AppendPathComponent(".lldbinit"); homedir_dot_lldb.ResolvePath(); if (dot_lldb.Exists() && dot_lldb.GetDirectory() != homedir_dot_lldb.GetDirectory()) { result.AppendErrorWithFormat( "There is a .lldbinit file in the current directory which is not " "being read.\n" "To silence this warning without sourcing in the local " ".lldbinit,\n" "add the following to the lldbinit file in your home directory:\n" " settings set target.load-cwd-lldbinit false\n" "To allow lldb to source .lldbinit files in the current working " "directory,\n" "set the value of this variable to true. Only do so if you " "understand and\n" "accept the security risk."); result.SetStatus(eReturnStatusFailed); return; } } else if (should_load == eLoadCWDlldbinitTrue) { init_file.SetFile("./.lldbinit", true, FileSpec::Style::native); } } } else { // If we aren't looking in the current working directory we are looking in // the home directory. We will first see if there is an application // specific ".lldbinit" file whose name is "~/.lldbinit" followed by a "-" // and the name of the program. If this file doesn't exist, we fall back to // just the "~/.lldbinit" file. We also obey any requests to not load the // init files. llvm::SmallString<64> home_dir_path; llvm::sys::path::home_directory(home_dir_path); FileSpec profilePath(home_dir_path.c_str(), false); profilePath.AppendPathComponent(".lldbinit"); std::string init_file_path = profilePath.GetPath(); if (m_skip_app_init_files == false) { FileSpec program_file_spec(HostInfo::GetProgramFileSpec()); const char *program_name = program_file_spec.GetFilename().AsCString(); if (program_name) { char program_init_file_name[PATH_MAX]; ::snprintf(program_init_file_name, sizeof(program_init_file_name), "%s-%s", init_file_path.c_str(), program_name); init_file.SetFile(program_init_file_name, true, FileSpec::Style::native); if (!init_file.Exists()) init_file.Clear(); } } if (!init_file && !m_skip_lldbinit_files) init_file.SetFile(init_file_path, false, FileSpec::Style::native); } // If the file exists, tell HandleCommand to 'source' it; this will do the // actual broadcasting of the commands back to any appropriate listener (see // CommandObjectSource::Execute for more details). if (init_file.Exists()) { const bool saved_batch = SetBatchCommandMode(true); CommandInterpreterRunOptions options; options.SetSilent(true); options.SetStopOnError(false); options.SetStopOnContinue(true); HandleCommandsFromFile(init_file, nullptr, // Execution context options, result); SetBatchCommandMode(saved_batch); } else { // nothing to be done if the file doesn't exist result.SetStatus(eReturnStatusSuccessFinishNoResult); } } const char *CommandInterpreter::GetCommandPrefix() { const char *prefix = GetDebugger().GetIOHandlerCommandPrefix(); return prefix == NULL ? "" : prefix; } PlatformSP CommandInterpreter::GetPlatform(bool prefer_target_platform) { PlatformSP platform_sp; if (prefer_target_platform) { ExecutionContext exe_ctx(GetExecutionContext()); Target *target = exe_ctx.GetTargetPtr(); if (target) platform_sp = target->GetPlatform(); } if (!platform_sp) platform_sp = m_debugger.GetPlatformList().GetSelectedPlatform(); return platform_sp; } void CommandInterpreter::HandleCommands(const StringList &commands, ExecutionContext *override_context, CommandInterpreterRunOptions &options, CommandReturnObject &result) { size_t num_lines = commands.GetSize(); // If we are going to continue past a "continue" then we need to run the // commands synchronously. Make sure you reset this value anywhere you return // from the function. bool old_async_execution = m_debugger.GetAsyncExecution(); // If we've been given an execution context, set it at the start, but don't // keep resetting it or we will cause series of commands that change the // context, then do an operation that relies on that context to fail. if (override_context != nullptr) UpdateExecutionContext(override_context); if (!options.GetStopOnContinue()) { m_debugger.SetAsyncExecution(false); } for (size_t idx = 0; idx < num_lines && !WasInterrupted(); idx++) { const char *cmd = commands.GetStringAtIndex(idx); if (cmd[0] == '\0') continue; if (options.GetEchoCommands()) { // TODO: Add Stream support. result.AppendMessageWithFormat("%s %s\n", m_debugger.GetPrompt().str().c_str(), cmd); } CommandReturnObject tmp_result; // If override_context is not NULL, pass no_context_switching = true for // HandleCommand() since we updated our context already. // We might call into a regex or alias command, in which case the // add_to_history will get lost. This m_command_source_depth dingus is the // way we turn off adding to the history in that case, so set it up here. if (!options.GetAddToHistory()) m_command_source_depth++; bool success = HandleCommand(cmd, options.m_add_to_history, tmp_result, nullptr, /* override_context */ true, /* repeat_on_empty_command */ override_context != nullptr /* no_context_switching */); if (!options.GetAddToHistory()) m_command_source_depth--; if (options.GetPrintResults()) { if (tmp_result.Succeeded()) result.AppendMessage(tmp_result.GetOutputData()); } if (!success || !tmp_result.Succeeded()) { llvm::StringRef error_msg = tmp_result.GetErrorData(); if (error_msg.empty()) error_msg = ".\n"; if (options.GetStopOnError()) { result.AppendErrorWithFormat( "Aborting reading of commands after command #%" PRIu64 ": '%s' failed with %s", (uint64_t)idx, cmd, error_msg.str().c_str()); result.SetStatus(eReturnStatusFailed); m_debugger.SetAsyncExecution(old_async_execution); return; } else if (options.GetPrintResults()) { result.AppendMessageWithFormat( "Command #%" PRIu64 " '%s' failed with %s", (uint64_t)idx + 1, cmd, error_msg.str().c_str()); } } if (result.GetImmediateOutputStream()) result.GetImmediateOutputStream()->Flush(); if (result.GetImmediateErrorStream()) result.GetImmediateErrorStream()->Flush(); // N.B. Can't depend on DidChangeProcessState, because the state coming // into the command execution could be running (for instance in Breakpoint // Commands. So we check the return value to see if it is has running in // it. if ((tmp_result.GetStatus() == eReturnStatusSuccessContinuingNoResult) || (tmp_result.GetStatus() == eReturnStatusSuccessContinuingResult)) { if (options.GetStopOnContinue()) { // If we caused the target to proceed, and we're going to stop in that // case, set the status in our real result before returning. This is // an error if the continue was not the last command in the set of // commands to be run. if (idx != num_lines - 1) result.AppendErrorWithFormat( "Aborting reading of commands after command #%" PRIu64 ": '%s' continued the target.\n", (uint64_t)idx + 1, cmd); else result.AppendMessageWithFormat("Command #%" PRIu64 " '%s' continued the target.\n", (uint64_t)idx + 1, cmd); result.SetStatus(tmp_result.GetStatus()); m_debugger.SetAsyncExecution(old_async_execution); return; } } // Also check for "stop on crash here: bool should_stop = false; if (tmp_result.GetDidChangeProcessState() && options.GetStopOnCrash()) { TargetSP target_sp(m_debugger.GetTargetList().GetSelectedTarget()); if (target_sp) { ProcessSP process_sp(target_sp->GetProcessSP()); if (process_sp) { for (ThreadSP thread_sp : process_sp->GetThreadList().Threads()) { StopReason reason = thread_sp->GetStopReason(); if (reason == eStopReasonSignal || reason == eStopReasonException || reason == eStopReasonInstrumentation) { should_stop = true; break; } } } } if (should_stop) { if (idx != num_lines - 1) result.AppendErrorWithFormat( "Aborting reading of commands after command #%" PRIu64 ": '%s' stopped with a signal or exception.\n", (uint64_t)idx + 1, cmd); else result.AppendMessageWithFormat( "Command #%" PRIu64 " '%s' stopped with a signal or exception.\n", (uint64_t)idx + 1, cmd); result.SetStatus(tmp_result.GetStatus()); m_debugger.SetAsyncExecution(old_async_execution); return; } } } result.SetStatus(eReturnStatusSuccessFinishResult); m_debugger.SetAsyncExecution(old_async_execution); return; } // Make flags that we can pass into the IOHandler so our delegates can do the // right thing enum { eHandleCommandFlagStopOnContinue = (1u << 0), eHandleCommandFlagStopOnError = (1u << 1), eHandleCommandFlagEchoCommand = (1u << 2), eHandleCommandFlagPrintResult = (1u << 3), eHandleCommandFlagStopOnCrash = (1u << 4) }; void CommandInterpreter::HandleCommandsFromFile( FileSpec &cmd_file, ExecutionContext *context, CommandInterpreterRunOptions &options, CommandReturnObject &result) { if (cmd_file.Exists()) { StreamFileSP input_file_sp(new StreamFile()); std::string cmd_file_path = cmd_file.GetPath(); Status error = input_file_sp->GetFile().Open(cmd_file_path.c_str(), File::eOpenOptionRead); if (error.Success()) { Debugger &debugger = GetDebugger(); uint32_t flags = 0; if (options.m_stop_on_continue == eLazyBoolCalculate) { if (m_command_source_flags.empty()) { // Stop on continue by default flags |= eHandleCommandFlagStopOnContinue; } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnContinue) { flags |= eHandleCommandFlagStopOnContinue; } } else if (options.m_stop_on_continue == eLazyBoolYes) { flags |= eHandleCommandFlagStopOnContinue; } if (options.m_stop_on_error == eLazyBoolCalculate) { if (m_command_source_flags.empty()) { if (GetStopCmdSourceOnError()) flags |= eHandleCommandFlagStopOnError; } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnError) { flags |= eHandleCommandFlagStopOnError; } } else if (options.m_stop_on_error == eLazyBoolYes) { flags |= eHandleCommandFlagStopOnError; } if (options.GetStopOnCrash()) { if (m_command_source_flags.empty()) { // Echo command by default flags |= eHandleCommandFlagStopOnCrash; } else if (m_command_source_flags.back() & eHandleCommandFlagStopOnCrash) { flags |= eHandleCommandFlagStopOnCrash; } } if (options.m_echo_commands == eLazyBoolCalculate) { if (m_command_source_flags.empty()) { // Echo command by default flags |= eHandleCommandFlagEchoCommand; } else if (m_command_source_flags.back() & eHandleCommandFlagEchoCommand) { flags |= eHandleCommandFlagEchoCommand; } } else if (options.m_echo_commands == eLazyBoolYes) { flags |= eHandleCommandFlagEchoCommand; } if (options.m_print_results == eLazyBoolCalculate) { if (m_command_source_flags.empty()) { // Print output by default flags |= eHandleCommandFlagPrintResult; } else if (m_command_source_flags.back() & eHandleCommandFlagPrintResult) { flags |= eHandleCommandFlagPrintResult; } } else if (options.m_print_results == eLazyBoolYes) { flags |= eHandleCommandFlagPrintResult; } if (flags & eHandleCommandFlagPrintResult) { debugger.GetOutputFile()->Printf("Executing commands in '%s'.\n", cmd_file_path.c_str()); } // Used for inheriting the right settings when "command source" might // have nested "command source" commands lldb::StreamFileSP empty_stream_sp; m_command_source_flags.push_back(flags); IOHandlerSP io_handler_sp(new IOHandlerEditline( debugger, IOHandler::Type::CommandInterpreter, input_file_sp, empty_stream_sp, // Pass in an empty stream so we inherit the top // input reader output stream empty_stream_sp, // Pass in an empty stream so we inherit the top // input reader error stream flags, nullptr, // Pass in NULL for "editline_name" so no history is saved, // or written debugger.GetPrompt(), llvm::StringRef(), false, // Not multi-line debugger.GetUseColor(), 0, *this)); const bool old_async_execution = debugger.GetAsyncExecution(); // Set synchronous execution if we are not stopping on continue if ((flags & eHandleCommandFlagStopOnContinue) == 0) debugger.SetAsyncExecution(false); m_command_source_depth++; debugger.RunIOHandler(io_handler_sp); if (!m_command_source_flags.empty()) m_command_source_flags.pop_back(); m_command_source_depth--; result.SetStatus(eReturnStatusSuccessFinishNoResult); debugger.SetAsyncExecution(old_async_execution); } else { result.AppendErrorWithFormat( "error: an error occurred read file '%s': %s\n", cmd_file_path.c_str(), error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat( "Error reading commands from file %s - file not found.\n", cmd_file.GetFilename().AsCString("")); result.SetStatus(eReturnStatusFailed); return; } } ScriptInterpreter *CommandInterpreter::GetScriptInterpreter(bool can_create) { std::lock_guard locker(m_script_interpreter_mutex); if (!m_script_interpreter_sp) { if (!can_create) return nullptr; lldb::ScriptLanguage script_lang = GetDebugger().GetScriptLanguage(); m_script_interpreter_sp = PluginManager::GetScriptInterpreterForLanguage(script_lang, *this); } return m_script_interpreter_sp.get(); } bool CommandInterpreter::GetSynchronous() { return m_synchronous_execution; } void CommandInterpreter::SetSynchronous(bool value) { m_synchronous_execution = value; } void CommandInterpreter::OutputFormattedHelpText(Stream &strm, llvm::StringRef prefix, llvm::StringRef help_text) { const uint32_t max_columns = m_debugger.GetTerminalWidth(); size_t line_width_max = max_columns - prefix.size(); if (line_width_max < 16) line_width_max = help_text.size() + prefix.size(); strm.IndentMore(prefix.size()); bool prefixed_yet = false; while (!help_text.empty()) { // Prefix the first line, indent subsequent lines to line up if (!prefixed_yet) { strm << prefix; prefixed_yet = true; } else strm.Indent(); // Never print more than the maximum on one line. llvm::StringRef this_line = help_text.substr(0, line_width_max); // Always break on an explicit newline. std::size_t first_newline = this_line.find_first_of("\n"); // Don't break on space/tab unless the text is too long to fit on one line. std::size_t last_space = llvm::StringRef::npos; if (this_line.size() != help_text.size()) last_space = this_line.find_last_of(" \t"); // Break at whichever condition triggered first. this_line = this_line.substr(0, std::min(first_newline, last_space)); strm.PutCString(this_line); strm.EOL(); // Remove whitespace / newlines after breaking. help_text = help_text.drop_front(this_line.size()).ltrim(); } strm.IndentLess(prefix.size()); } void CommandInterpreter::OutputFormattedHelpText(Stream &strm, llvm::StringRef word_text, llvm::StringRef separator, llvm::StringRef help_text, size_t max_word_len) { StreamString prefix_stream; prefix_stream.Printf(" %-*s %*s ", (int)max_word_len, word_text.data(), (int)separator.size(), separator.data()); OutputFormattedHelpText(strm, prefix_stream.GetString(), help_text); } void CommandInterpreter::OutputHelpText(Stream &strm, llvm::StringRef word_text, llvm::StringRef separator, llvm::StringRef help_text, uint32_t max_word_len) { int indent_size = max_word_len + separator.size() + 2; strm.IndentMore(indent_size); StreamString text_strm; text_strm.Printf("%-*s ", (int)max_word_len, word_text.data()); text_strm << separator << " " << help_text; const uint32_t max_columns = m_debugger.GetTerminalWidth(); llvm::StringRef text = text_strm.GetString(); uint32_t chars_left = max_columns; auto nextWordLength = [](llvm::StringRef S) { size_t pos = S.find_first_of(' '); return pos == llvm::StringRef::npos ? S.size() : pos; }; while (!text.empty()) { if (text.front() == '\n' || (text.front() == ' ' && nextWordLength(text.ltrim(' ')) > chars_left)) { strm.EOL(); strm.Indent(); chars_left = max_columns - indent_size; if (text.front() == '\n') text = text.drop_front(); else text = text.ltrim(' '); } else { strm.PutChar(text.front()); --chars_left; text = text.drop_front(); } } strm.EOL(); strm.IndentLess(indent_size); } void CommandInterpreter::FindCommandsForApropos( llvm::StringRef search_word, StringList &commands_found, StringList &commands_help, CommandObject::CommandMap &command_map) { CommandObject::CommandMap::const_iterator pos; for (pos = command_map.begin(); pos != command_map.end(); ++pos) { llvm::StringRef command_name = pos->first; CommandObject *cmd_obj = pos->second.get(); const bool search_short_help = true; const bool search_long_help = false; const bool search_syntax = false; const bool search_options = false; if (command_name.contains_lower(search_word) || cmd_obj->HelpTextContainsWord(search_word, search_short_help, search_long_help, search_syntax, search_options)) { commands_found.AppendString(cmd_obj->GetCommandName()); commands_help.AppendString(cmd_obj->GetHelp()); } if (cmd_obj->IsMultiwordObject()) { CommandObjectMultiword *cmd_multiword = cmd_obj->GetAsMultiwordCommand(); FindCommandsForApropos(search_word, commands_found, commands_help, cmd_multiword->GetSubcommandDictionary()); } } } void CommandInterpreter::FindCommandsForApropos(llvm::StringRef search_word, StringList &commands_found, StringList &commands_help, bool search_builtin_commands, bool search_user_commands, bool search_alias_commands) { CommandObject::CommandMap::const_iterator pos; if (search_builtin_commands) FindCommandsForApropos(search_word, commands_found, commands_help, m_command_dict); if (search_user_commands) FindCommandsForApropos(search_word, commands_found, commands_help, m_user_dict); if (search_alias_commands) FindCommandsForApropos(search_word, commands_found, commands_help, m_alias_dict); } void CommandInterpreter::UpdateExecutionContext( ExecutionContext *override_context) { if (override_context != nullptr) { m_exe_ctx_ref = *override_context; } else { const bool adopt_selected = true; m_exe_ctx_ref.SetTargetPtr(m_debugger.GetSelectedTarget().get(), adopt_selected); } } size_t CommandInterpreter::GetProcessOutput() { // The process has stuff waiting for stderr; get it and write it out to the // appropriate place. char stdio_buffer[1024]; size_t len; size_t total_bytes = 0; Status error; TargetSP target_sp(m_debugger.GetTargetList().GetSelectedTarget()); if (target_sp) { ProcessSP process_sp(target_sp->GetProcessSP()); if (process_sp) { while ((len = process_sp->GetSTDOUT(stdio_buffer, sizeof(stdio_buffer), error)) > 0) { size_t bytes_written = len; m_debugger.GetOutputFile()->Write(stdio_buffer, bytes_written); total_bytes += len; } while ((len = process_sp->GetSTDERR(stdio_buffer, sizeof(stdio_buffer), error)) > 0) { size_t bytes_written = len; m_debugger.GetErrorFile()->Write(stdio_buffer, bytes_written); total_bytes += len; } } } return total_bytes; } void CommandInterpreter::StartHandlingCommand() { auto idle_state = CommandHandlingState::eIdle; if (m_command_state.compare_exchange_strong( idle_state, CommandHandlingState::eInProgress)) lldbassert(m_iohandler_nesting_level == 0); else lldbassert(m_iohandler_nesting_level > 0); ++m_iohandler_nesting_level; } void CommandInterpreter::FinishHandlingCommand() { lldbassert(m_iohandler_nesting_level > 0); if (--m_iohandler_nesting_level == 0) { auto prev_state = m_command_state.exchange(CommandHandlingState::eIdle); lldbassert(prev_state != CommandHandlingState::eIdle); } } bool CommandInterpreter::InterruptCommand() { auto in_progress = CommandHandlingState::eInProgress; return m_command_state.compare_exchange_strong( in_progress, CommandHandlingState::eInterrupted); } bool CommandInterpreter::WasInterrupted() const { bool was_interrupted = (m_command_state == CommandHandlingState::eInterrupted); lldbassert(!was_interrupted || m_iohandler_nesting_level > 0); return was_interrupted; } void CommandInterpreter::PrintCommandOutput(Stream &stream, llvm::StringRef str) { // Split the output into lines and poll for interrupt requests const char *data = str.data(); size_t size = str.size(); while (size > 0 && !WasInterrupted()) { size_t chunk_size = 0; for (; chunk_size < size; ++chunk_size) { lldbassert(data[chunk_size] != '\0'); if (data[chunk_size] == '\n') { ++chunk_size; break; } } chunk_size = stream.Write(data, chunk_size); lldbassert(size >= chunk_size); data += chunk_size; size -= chunk_size; } if (size > 0) { stream.Printf("\n... Interrupted.\n"); } } void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler, std::string &line) { // If we were interrupted, bail out... if (WasInterrupted()) return; const bool is_interactive = io_handler.GetIsInteractive(); if (is_interactive == false) { // When we are not interactive, don't execute blank lines. This will happen // sourcing a commands file. We don't want blank lines to repeat the // previous command and cause any errors to occur (like redefining an // alias, get an error and stop parsing the commands file). if (line.empty()) return; // When using a non-interactive file handle (like when sourcing commands // from a file) we need to echo the command out so we don't just see the // command output and no command... if (io_handler.GetFlags().Test(eHandleCommandFlagEchoCommand)) io_handler.GetOutputStreamFile()->Printf("%s%s\n", io_handler.GetPrompt(), line.c_str()); } StartHandlingCommand(); lldb_private::CommandReturnObject result; HandleCommand(line.c_str(), eLazyBoolCalculate, result); // Now emit the command output text from the command we just executed if (io_handler.GetFlags().Test(eHandleCommandFlagPrintResult)) { // Display any STDOUT/STDERR _prior_ to emitting the command result text GetProcessOutput(); if (!result.GetImmediateOutputStream()) { llvm::StringRef output = result.GetOutputData(); PrintCommandOutput(*io_handler.GetOutputStreamFile(), output); } // Now emit the command error text from the command we just executed if (!result.GetImmediateErrorStream()) { llvm::StringRef error = result.GetErrorData(); PrintCommandOutput(*io_handler.GetErrorStreamFile(), error); } } FinishHandlingCommand(); switch (result.GetStatus()) { case eReturnStatusInvalid: case eReturnStatusSuccessFinishNoResult: case eReturnStatusSuccessFinishResult: case eReturnStatusStarted: break; case eReturnStatusSuccessContinuingNoResult: case eReturnStatusSuccessContinuingResult: if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnContinue)) io_handler.SetIsDone(true); break; case eReturnStatusFailed: m_num_errors++; if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnError)) io_handler.SetIsDone(true); break; case eReturnStatusQuit: m_quit_requested = true; io_handler.SetIsDone(true); break; } // Finally, if we're going to stop on crash, check that here: if (!m_quit_requested && result.GetDidChangeProcessState() && io_handler.GetFlags().Test(eHandleCommandFlagStopOnCrash)) { bool should_stop = false; TargetSP target_sp(m_debugger.GetTargetList().GetSelectedTarget()); if (target_sp) { ProcessSP process_sp(target_sp->GetProcessSP()); if (process_sp) { for (ThreadSP thread_sp : process_sp->GetThreadList().Threads()) { StopReason reason = thread_sp->GetStopReason(); if ((reason == eStopReasonSignal || reason == eStopReasonException || reason == eStopReasonInstrumentation) && !result.GetAbnormalStopWasExpected()) { should_stop = true; break; } } } } if (should_stop) { io_handler.SetIsDone(true); m_stopped_for_crash = true; } } } bool CommandInterpreter::IOHandlerInterrupt(IOHandler &io_handler) { ExecutionContext exe_ctx(GetExecutionContext()); Process *process = exe_ctx.GetProcessPtr(); if (InterruptCommand()) return true; if (process) { StateType state = process->GetState(); if (StateIsRunningState(state)) { process->Halt(); return true; // Don't do any updating when we are running } } ScriptInterpreter *script_interpreter = GetScriptInterpreter(false); if (script_interpreter) { if (script_interpreter->Interrupt()) return true; } return false; } void CommandInterpreter::GetLLDBCommandsFromIOHandler( const char *prompt, IOHandlerDelegate &delegate, bool asynchronously, void *baton) { Debugger &debugger = GetDebugger(); IOHandlerSP io_handler_sp( new IOHandlerEditline(debugger, IOHandler::Type::CommandList, "lldb", // Name of input reader for history llvm::StringRef::withNullAsEmpty(prompt), // Prompt llvm::StringRef(), // Continuation prompt true, // Get multiple lines debugger.GetUseColor(), 0, // Don't show line numbers delegate)); // IOHandlerDelegate if (io_handler_sp) { io_handler_sp->SetUserData(baton); if (asynchronously) debugger.PushIOHandler(io_handler_sp); else debugger.RunIOHandler(io_handler_sp); } } void CommandInterpreter::GetPythonCommandsFromIOHandler( const char *prompt, IOHandlerDelegate &delegate, bool asynchronously, void *baton) { Debugger &debugger = GetDebugger(); IOHandlerSP io_handler_sp( new IOHandlerEditline(debugger, IOHandler::Type::PythonCode, "lldb-python", // Name of input reader for history llvm::StringRef::withNullAsEmpty(prompt), // Prompt llvm::StringRef(), // Continuation prompt true, // Get multiple lines debugger.GetUseColor(), 0, // Don't show line numbers delegate)); // IOHandlerDelegate if (io_handler_sp) { io_handler_sp->SetUserData(baton); if (asynchronously) debugger.PushIOHandler(io_handler_sp); else debugger.RunIOHandler(io_handler_sp); } } bool CommandInterpreter::IsActive() { return m_debugger.IsTopIOHandler(m_command_io_handler_sp); } lldb::IOHandlerSP CommandInterpreter::GetIOHandler(bool force_create, CommandInterpreterRunOptions *options) { // Always re-create the IOHandlerEditline in case the input changed. The old // instance might have had a non-interactive input and now it does or vice // versa. if (force_create || !m_command_io_handler_sp) { // Always re-create the IOHandlerEditline in case the input changed. The // old instance might have had a non-interactive input and now it does or // vice versa. uint32_t flags = 0; if (options) { if (options->m_stop_on_continue == eLazyBoolYes) flags |= eHandleCommandFlagStopOnContinue; if (options->m_stop_on_error == eLazyBoolYes) flags |= eHandleCommandFlagStopOnError; if (options->m_stop_on_crash == eLazyBoolYes) flags |= eHandleCommandFlagStopOnCrash; if (options->m_echo_commands != eLazyBoolNo) flags |= eHandleCommandFlagEchoCommand; if (options->m_print_results != eLazyBoolNo) flags |= eHandleCommandFlagPrintResult; } else { flags = eHandleCommandFlagEchoCommand | eHandleCommandFlagPrintResult; } m_command_io_handler_sp.reset(new IOHandlerEditline( m_debugger, IOHandler::Type::CommandInterpreter, m_debugger.GetInputFile(), m_debugger.GetOutputFile(), m_debugger.GetErrorFile(), flags, "lldb", m_debugger.GetPrompt(), llvm::StringRef(), // Continuation prompt false, // Don't enable multiple line input, just single line commands m_debugger.GetUseColor(), 0, // Don't show line numbers *this)); } return m_command_io_handler_sp; } void CommandInterpreter::RunCommandInterpreter( bool auto_handle_events, bool spawn_thread, CommandInterpreterRunOptions &options) { // Always re-create the command interpreter when we run it in case any file // handles have changed. bool force_create = true; m_debugger.PushIOHandler(GetIOHandler(force_create, &options)); m_stopped_for_crash = false; if (auto_handle_events) m_debugger.StartEventHandlerThread(); if (spawn_thread) { m_debugger.StartIOHandlerThread(); } else { m_debugger.ExecuteIOHandlers(); if (auto_handle_events) m_debugger.StopEventHandlerThread(); } } CommandObject * CommandInterpreter::ResolveCommandImpl(std::string &command_line, CommandReturnObject &result) { std::string scratch_command(command_line); // working copy so we don't modify // command_line unless we succeed CommandObject *cmd_obj = nullptr; StreamString revised_command_line; bool wants_raw_input = false; size_t actual_cmd_name_len = 0; std::string next_word; StringList matches; bool done = false; while (!done) { char quote_char = '\0'; std::string suffix; ExtractCommand(scratch_command, next_word, suffix, quote_char); if (cmd_obj == nullptr) { std::string full_name; bool is_alias = GetAliasFullName(next_word, full_name); cmd_obj = GetCommandObject(next_word, &matches); bool is_real_command = (is_alias == false) || (cmd_obj != nullptr && cmd_obj->IsAlias() == false); if (!is_real_command) { matches.Clear(); std::string alias_result; cmd_obj = BuildAliasResult(full_name, scratch_command, alias_result, result); revised_command_line.Printf("%s", alias_result.c_str()); if (cmd_obj) { wants_raw_input = cmd_obj->WantsRawCommandString(); actual_cmd_name_len = cmd_obj->GetCommandName().size(); } } else { if (cmd_obj) { llvm::StringRef cmd_name = cmd_obj->GetCommandName(); actual_cmd_name_len += cmd_name.size(); revised_command_line.Printf("%s", cmd_name.str().c_str()); wants_raw_input = cmd_obj->WantsRawCommandString(); } else { revised_command_line.Printf("%s", next_word.c_str()); } } } else { if (cmd_obj->IsMultiwordObject()) { CommandObject *sub_cmd_obj = cmd_obj->GetSubcommandObject(next_word.c_str()); if (sub_cmd_obj) { // The subcommand's name includes the parent command's name, so // restart rather than append to the revised_command_line. llvm::StringRef sub_cmd_name = sub_cmd_obj->GetCommandName(); actual_cmd_name_len = sub_cmd_name.size() + 1; revised_command_line.Clear(); revised_command_line.Printf("%s", sub_cmd_name.str().c_str()); cmd_obj = sub_cmd_obj; wants_raw_input = cmd_obj->WantsRawCommandString(); } else { if (quote_char) revised_command_line.Printf(" %c%s%s%c", quote_char, next_word.c_str(), suffix.c_str(), quote_char); else revised_command_line.Printf(" %s%s", next_word.c_str(), suffix.c_str()); done = true; } } else { if (quote_char) revised_command_line.Printf(" %c%s%s%c", quote_char, next_word.c_str(), suffix.c_str(), quote_char); else revised_command_line.Printf(" %s%s", next_word.c_str(), suffix.c_str()); done = true; } } if (cmd_obj == nullptr) { const size_t num_matches = matches.GetSize(); if (matches.GetSize() > 1) { StreamString error_msg; error_msg.Printf("Ambiguous command '%s'. Possible matches:\n", next_word.c_str()); for (uint32_t i = 0; i < num_matches; ++i) { error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i)); } result.AppendRawError(error_msg.GetString()); } else { // We didn't have only one match, otherwise we wouldn't get here. lldbassert(num_matches == 0); result.AppendErrorWithFormat("'%s' is not a valid command.\n", next_word.c_str()); } result.SetStatus(eReturnStatusFailed); return nullptr; } if (cmd_obj->IsMultiwordObject()) { if (!suffix.empty()) { result.AppendErrorWithFormat( "command '%s' did not recognize '%s%s%s' as valid (subcommand " "might be invalid).\n", cmd_obj->GetCommandName().str().c_str(), next_word.empty() ? "" : next_word.c_str(), next_word.empty() ? " -- " : " ", suffix.c_str()); result.SetStatus(eReturnStatusFailed); return nullptr; } } else { // If we found a normal command, we are done done = true; if (!suffix.empty()) { switch (suffix[0]) { case '/': // GDB format suffixes { Options *command_options = cmd_obj->GetOptions(); if (command_options && command_options->SupportsLongOption("gdb-format")) { std::string gdb_format_option("--gdb-format="); gdb_format_option += (suffix.c_str() + 1); std::string cmd = revised_command_line.GetString(); size_t arg_terminator_idx = FindArgumentTerminator(cmd); if (arg_terminator_idx != std::string::npos) { // Insert the gdb format option before the "--" that terminates // options gdb_format_option.append(1, ' '); cmd.insert(arg_terminator_idx, gdb_format_option); revised_command_line.Clear(); revised_command_line.PutCString(cmd); } else revised_command_line.Printf(" %s", gdb_format_option.c_str()); if (wants_raw_input && FindArgumentTerminator(cmd) == std::string::npos) revised_command_line.PutCString(" --"); } else { result.AppendErrorWithFormat( "the '%s' command doesn't support the --gdb-format option\n", cmd_obj->GetCommandName().str().c_str()); result.SetStatus(eReturnStatusFailed); return nullptr; } } break; default: result.AppendErrorWithFormat( "unknown command shorthand suffix: '%s'\n", suffix.c_str()); result.SetStatus(eReturnStatusFailed); return nullptr; } } } if (scratch_command.empty()) done = true; } if (!scratch_command.empty()) revised_command_line.Printf(" %s", scratch_command.c_str()); if (cmd_obj != NULL) command_line = revised_command_line.GetString(); return cmd_obj; } Index: vendor/lldb/dist/source/Interpreter/CommandObject.cpp =================================================================== --- vendor/lldb/dist/source/Interpreter/CommandObject.cpp (revision 337146) +++ vendor/lldb/dist/source/Interpreter/CommandObject.cpp (revision 337147) @@ -1,1116 +1,1115 @@ //===-- CommandObject.cpp ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Interpreter/CommandObject.h" #include #include #include #include #include #include "lldb/Core/Address.h" #include "lldb/Interpreter/Options.h" #include "lldb/Utility/ArchSpec.h" // These are for the Sourcename completers. // FIXME: Make a separate file for the completers. #include "lldb/Core/FileSpecList.h" #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Target/Language.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------------- // CommandObject //------------------------------------------------------------------------- CommandObject::CommandObject(CommandInterpreter &interpreter, llvm::StringRef name, llvm::StringRef help, llvm::StringRef syntax, uint32_t flags) : m_interpreter(interpreter), m_cmd_name(name), m_cmd_help_short(), m_cmd_help_long(), m_cmd_syntax(), m_flags(flags), m_arguments(), m_deprecated_command_override_callback(nullptr), m_command_override_callback(nullptr), m_command_override_baton(nullptr) { m_cmd_help_short = help; m_cmd_syntax = syntax; } CommandObject::~CommandObject() {} llvm::StringRef CommandObject::GetHelp() { return m_cmd_help_short; } llvm::StringRef CommandObject::GetHelpLong() { return m_cmd_help_long; } llvm::StringRef CommandObject::GetSyntax() { if (!m_cmd_syntax.empty()) return m_cmd_syntax; StreamString syntax_str; syntax_str.PutCString(GetCommandName()); if (!IsDashDashCommand() && GetOptions() != nullptr) syntax_str.PutCString(" "); if (!m_arguments.empty()) { syntax_str.PutCString(" "); if (!IsDashDashCommand() && WantsRawCommandString() && GetOptions() && GetOptions()->NumCommandOptions()) syntax_str.PutCString("-- "); GetFormattedCommandArguments(syntax_str); } m_cmd_syntax = syntax_str.GetString(); return m_cmd_syntax; } llvm::StringRef CommandObject::GetCommandName() const { return m_cmd_name; } void CommandObject::SetCommandName(llvm::StringRef name) { m_cmd_name = name; } void CommandObject::SetHelp(llvm::StringRef str) { m_cmd_help_short = str; } void CommandObject::SetHelpLong(llvm::StringRef str) { m_cmd_help_long = str; } void CommandObject::SetSyntax(llvm::StringRef str) { m_cmd_syntax = str; } Options *CommandObject::GetOptions() { // By default commands don't have options unless this virtual function is // overridden by base classes. return nullptr; } bool CommandObject::ParseOptions(Args &args, CommandReturnObject &result) { // See if the subclass has options? Options *options = GetOptions(); if (options != nullptr) { Status error; auto exe_ctx = GetCommandInterpreter().GetExecutionContext(); options->NotifyOptionParsingStarting(&exe_ctx); const bool require_validation = true; llvm::Expected args_or = options->Parse( args, &exe_ctx, GetCommandInterpreter().GetPlatform(true), require_validation); if (args_or) { args = std::move(*args_or); error = options->NotifyOptionParsingFinished(&exe_ctx); } else error = args_or.takeError(); if (error.Success()) { if (options->VerifyOptions(result)) return true; } else { const char *error_cstr = error.AsCString(); if (error_cstr) { // We got an error string, lets use that result.AppendError(error_cstr); } else { // No error string, output the usage information into result options->GenerateOptionUsage( result.GetErrorStream(), this, GetCommandInterpreter().GetDebugger().GetTerminalWidth()); } } result.SetStatus(eReturnStatusFailed); return false; } return true; } bool CommandObject::CheckRequirements(CommandReturnObject &result) { #ifdef LLDB_CONFIGURATION_DEBUG // Nothing should be stored in m_exe_ctx between running commands as // m_exe_ctx has shared pointers to the target, process, thread and frame and // we don't want any CommandObject instances to keep any of these objects // around longer than for a single command. Every command should call // CommandObject::Cleanup() after it has completed assert(m_exe_ctx.GetTargetPtr() == NULL); assert(m_exe_ctx.GetProcessPtr() == NULL); assert(m_exe_ctx.GetThreadPtr() == NULL); assert(m_exe_ctx.GetFramePtr() == NULL); #endif // Lock down the interpreter's execution context prior to running the command // so we guarantee the selected target, process, thread and frame can't go // away during the execution m_exe_ctx = m_interpreter.GetExecutionContext(); const uint32_t flags = GetFlags().Get(); if (flags & (eCommandRequiresTarget | eCommandRequiresProcess | eCommandRequiresThread | eCommandRequiresFrame | eCommandTryTargetAPILock)) { if ((flags & eCommandRequiresTarget) && !m_exe_ctx.HasTargetScope()) { result.AppendError(GetInvalidTargetDescription()); return false; } if ((flags & eCommandRequiresProcess) && !m_exe_ctx.HasProcessScope()) { if (!m_exe_ctx.HasTargetScope()) result.AppendError(GetInvalidTargetDescription()); else result.AppendError(GetInvalidProcessDescription()); return false; } if ((flags & eCommandRequiresThread) && !m_exe_ctx.HasThreadScope()) { if (!m_exe_ctx.HasTargetScope()) result.AppendError(GetInvalidTargetDescription()); else if (!m_exe_ctx.HasProcessScope()) result.AppendError(GetInvalidProcessDescription()); else result.AppendError(GetInvalidThreadDescription()); return false; } if ((flags & eCommandRequiresFrame) && !m_exe_ctx.HasFrameScope()) { if (!m_exe_ctx.HasTargetScope()) result.AppendError(GetInvalidTargetDescription()); else if (!m_exe_ctx.HasProcessScope()) result.AppendError(GetInvalidProcessDescription()); else if (!m_exe_ctx.HasThreadScope()) result.AppendError(GetInvalidThreadDescription()); else result.AppendError(GetInvalidFrameDescription()); return false; } if ((flags & eCommandRequiresRegContext) && (m_exe_ctx.GetRegisterContext() == nullptr)) { result.AppendError(GetInvalidRegContextDescription()); return false; } if (flags & eCommandTryTargetAPILock) { Target *target = m_exe_ctx.GetTargetPtr(); if (target) m_api_locker = std::unique_lock(target->GetAPIMutex()); } } if (GetFlags().AnySet(eCommandProcessMustBeLaunched | eCommandProcessMustBePaused)) { Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); if (process == nullptr) { // A process that is not running is considered paused. if (GetFlags().Test(eCommandProcessMustBeLaunched)) { result.AppendError("Process must exist."); result.SetStatus(eReturnStatusFailed); return false; } } else { StateType state = process->GetState(); switch (state) { case eStateInvalid: case eStateSuspended: case eStateCrashed: case eStateStopped: break; case eStateConnected: case eStateAttaching: case eStateLaunching: case eStateDetached: case eStateExited: case eStateUnloaded: if (GetFlags().Test(eCommandProcessMustBeLaunched)) { result.AppendError("Process must be launched."); result.SetStatus(eReturnStatusFailed); return false; } break; case eStateRunning: case eStateStepping: if (GetFlags().Test(eCommandProcessMustBePaused)) { result.AppendError("Process is running. Use 'process interrupt' to " "pause execution."); result.SetStatus(eReturnStatusFailed); return false; } } } } return true; } void CommandObject::Cleanup() { m_exe_ctx.Clear(); if (m_api_locker.owns_lock()) m_api_locker.unlock(); } int CommandObject::HandleCompletion(CompletionRequest &request) { // Default implementation of WantsCompletion() is !WantsRawCommandString(). // Subclasses who want raw command string but desire, for example, argument // completion should override WantsCompletion() to return true, instead. if (WantsRawCommandString() && !WantsCompletion()) { // FIXME: Abstract telling the completion to insert the completion // character. - request.GetMatches().Clear(); return -1; } else { // Can we do anything generic with the options? Options *cur_options = GetOptions(); CommandReturnObject result; OptionElementVector opt_element_vector; if (cur_options != nullptr) { opt_element_vector = cur_options->ParseForCompletion( request.GetParsedLine(), request.GetCursorIndex()); bool handled_by_options = cur_options->HandleOptionCompletion( request, opt_element_vector, GetCommandInterpreter()); if (handled_by_options) - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } // If we got here, the last word is not an option or an option argument. return HandleArgumentCompletion(request, opt_element_vector); } } bool CommandObject::HelpTextContainsWord(llvm::StringRef search_word, bool search_short_help, bool search_long_help, bool search_syntax, bool search_options) { std::string options_usage_help; bool found_word = false; llvm::StringRef short_help = GetHelp(); llvm::StringRef long_help = GetHelpLong(); llvm::StringRef syntax_help = GetSyntax(); if (search_short_help && short_help.contains_lower(search_word)) found_word = true; else if (search_long_help && long_help.contains_lower(search_word)) found_word = true; else if (search_syntax && syntax_help.contains_lower(search_word)) found_word = true; if (!found_word && search_options && GetOptions() != nullptr) { StreamString usage_help; GetOptions()->GenerateOptionUsage( usage_help, this, GetCommandInterpreter().GetDebugger().GetTerminalWidth()); if (!usage_help.Empty()) { llvm::StringRef usage_text = usage_help.GetString(); if (usage_text.contains_lower(search_word)) found_word = true; } } return found_word; } bool CommandObject::ParseOptionsAndNotify(Args &args, CommandReturnObject &result, OptionGroupOptions &group_options, ExecutionContext &exe_ctx) { if (!ParseOptions(args, result)) return false; Status error(group_options.NotifyOptionParsingFinished(&exe_ctx)); if (error.Fail()) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } return true; } int CommandObject::GetNumArgumentEntries() { return m_arguments.size(); } CommandObject::CommandArgumentEntry * CommandObject::GetArgumentEntryAtIndex(int idx) { if (static_cast(idx) < m_arguments.size()) return &(m_arguments[idx]); return nullptr; } const CommandObject::ArgumentTableEntry * CommandObject::FindArgumentDataByType(CommandArgumentType arg_type) { const ArgumentTableEntry *table = CommandObject::GetArgumentTable(); for (int i = 0; i < eArgTypeLastArg; ++i) if (table[i].arg_type == arg_type) return &(table[i]); return nullptr; } void CommandObject::GetArgumentHelp(Stream &str, CommandArgumentType arg_type, CommandInterpreter &interpreter) { const ArgumentTableEntry *table = CommandObject::GetArgumentTable(); const ArgumentTableEntry *entry = &(table[arg_type]); // The table is *supposed* to be kept in arg_type order, but someone *could* // have messed it up... if (entry->arg_type != arg_type) entry = CommandObject::FindArgumentDataByType(arg_type); if (!entry) return; StreamString name_str; name_str.Printf("<%s>", entry->arg_name); if (entry->help_function) { llvm::StringRef help_text = entry->help_function(); if (!entry->help_function.self_formatting) { interpreter.OutputFormattedHelpText(str, name_str.GetString(), "--", help_text, name_str.GetSize()); } else { interpreter.OutputHelpText(str, name_str.GetString(), "--", help_text, name_str.GetSize()); } } else interpreter.OutputFormattedHelpText(str, name_str.GetString(), "--", entry->help_text, name_str.GetSize()); } const char *CommandObject::GetArgumentName(CommandArgumentType arg_type) { const ArgumentTableEntry *entry = &(CommandObject::GetArgumentTable()[arg_type]); // The table is *supposed* to be kept in arg_type order, but someone *could* // have messed it up... if (entry->arg_type != arg_type) entry = CommandObject::FindArgumentDataByType(arg_type); if (entry) return entry->arg_name; return nullptr; } bool CommandObject::IsPairType(ArgumentRepetitionType arg_repeat_type) { if ((arg_repeat_type == eArgRepeatPairPlain) || (arg_repeat_type == eArgRepeatPairOptional) || (arg_repeat_type == eArgRepeatPairPlus) || (arg_repeat_type == eArgRepeatPairStar) || (arg_repeat_type == eArgRepeatPairRange) || (arg_repeat_type == eArgRepeatPairRangeOptional)) return true; return false; } static CommandObject::CommandArgumentEntry OptSetFiltered(uint32_t opt_set_mask, CommandObject::CommandArgumentEntry &cmd_arg_entry) { CommandObject::CommandArgumentEntry ret_val; for (unsigned i = 0; i < cmd_arg_entry.size(); ++i) if (opt_set_mask & cmd_arg_entry[i].arg_opt_set_association) ret_val.push_back(cmd_arg_entry[i]); return ret_val; } // Default parameter value of opt_set_mask is LLDB_OPT_SET_ALL, which means // take all the argument data into account. On rare cases where some argument // sticks with certain option sets, this function returns the option set // filtered args. void CommandObject::GetFormattedCommandArguments(Stream &str, uint32_t opt_set_mask) { int num_args = m_arguments.size(); for (int i = 0; i < num_args; ++i) { if (i > 0) str.Printf(" "); CommandArgumentEntry arg_entry = opt_set_mask == LLDB_OPT_SET_ALL ? m_arguments[i] : OptSetFiltered(opt_set_mask, m_arguments[i]); int num_alternatives = arg_entry.size(); if ((num_alternatives == 2) && IsPairType(arg_entry[0].arg_repetition)) { const char *first_name = GetArgumentName(arg_entry[0].arg_type); const char *second_name = GetArgumentName(arg_entry[1].arg_type); switch (arg_entry[0].arg_repetition) { case eArgRepeatPairPlain: str.Printf("<%s> <%s>", first_name, second_name); break; case eArgRepeatPairOptional: str.Printf("[<%s> <%s>]", first_name, second_name); break; case eArgRepeatPairPlus: str.Printf("<%s> <%s> [<%s> <%s> [...]]", first_name, second_name, first_name, second_name); break; case eArgRepeatPairStar: str.Printf("[<%s> <%s> [<%s> <%s> [...]]]", first_name, second_name, first_name, second_name); break; case eArgRepeatPairRange: str.Printf("<%s_1> <%s_1> ... <%s_n> <%s_n>", first_name, second_name, first_name, second_name); break; case eArgRepeatPairRangeOptional: str.Printf("[<%s_1> <%s_1> ... <%s_n> <%s_n>]", first_name, second_name, first_name, second_name); break; // Explicitly test for all the rest of the cases, so if new types get // added we will notice the missing case statement(s). case eArgRepeatPlain: case eArgRepeatOptional: case eArgRepeatPlus: case eArgRepeatStar: case eArgRepeatRange: // These should not be reached, as they should fail the IsPairType test // above. break; } } else { StreamString names; for (int j = 0; j < num_alternatives; ++j) { if (j > 0) names.Printf(" | "); names.Printf("%s", GetArgumentName(arg_entry[j].arg_type)); } std::string name_str = names.GetString(); switch (arg_entry[0].arg_repetition) { case eArgRepeatPlain: str.Printf("<%s>", name_str.c_str()); break; case eArgRepeatPlus: str.Printf("<%s> [<%s> [...]]", name_str.c_str(), name_str.c_str()); break; case eArgRepeatStar: str.Printf("[<%s> [<%s> [...]]]", name_str.c_str(), name_str.c_str()); break; case eArgRepeatOptional: str.Printf("[<%s>]", name_str.c_str()); break; case eArgRepeatRange: str.Printf("<%s_1> .. <%s_n>", name_str.c_str(), name_str.c_str()); break; // Explicitly test for all the rest of the cases, so if new types get // added we will notice the missing case statement(s). case eArgRepeatPairPlain: case eArgRepeatPairOptional: case eArgRepeatPairPlus: case eArgRepeatPairStar: case eArgRepeatPairRange: case eArgRepeatPairRangeOptional: // These should not be hit, as they should pass the IsPairType test // above, and control should have gone into the other branch of the if // statement. break; } } } } CommandArgumentType CommandObject::LookupArgumentName(llvm::StringRef arg_name) { CommandArgumentType return_type = eArgTypeLastArg; arg_name = arg_name.ltrim('<').rtrim('>'); const ArgumentTableEntry *table = GetArgumentTable(); for (int i = 0; i < eArgTypeLastArg; ++i) if (arg_name == table[i].arg_name) return_type = g_arguments_data[i].arg_type; return return_type; } static llvm::StringRef RegisterNameHelpTextCallback() { return "Register names can be specified using the architecture specific " "names. " "They can also be specified using generic names. Not all generic " "entities have " "registers backing them on all architectures. When they don't the " "generic name " "will return an error.\n" "The generic names defined in lldb are:\n" "\n" "pc - program counter register\n" "ra - return address register\n" "fp - frame pointer register\n" "sp - stack pointer register\n" "flags - the flags register\n" "arg{1-6} - integer argument passing registers.\n"; } static llvm::StringRef BreakpointIDHelpTextCallback() { return "Breakpoints are identified using major and minor numbers; the major " "number corresponds to the single entity that was created with a " "'breakpoint " "set' command; the minor numbers correspond to all the locations that " "were " "actually found/set based on the major breakpoint. A full breakpoint " "ID might " "look like 3.14, meaning the 14th location set for the 3rd " "breakpoint. You " "can specify all the locations of a breakpoint by just indicating the " "major " "breakpoint number. A valid breakpoint ID consists either of just the " "major " "number, or the major number followed by a dot and the location " "number (e.g. " "3 or 3.2 could both be valid breakpoint IDs.)"; } static llvm::StringRef BreakpointIDRangeHelpTextCallback() { return "A 'breakpoint ID list' is a manner of specifying multiple " "breakpoints. " "This can be done through several mechanisms. The easiest way is to " "just " "enter a space-separated list of breakpoint IDs. To specify all the " "breakpoint locations under a major breakpoint, you can use the major " "breakpoint number followed by '.*', eg. '5.*' means all the " "locations under " "breakpoint 5. You can also indicate a range of breakpoints by using " " - . The start-bp-id and end-bp-id for a " "range can " "be any valid breakpoint IDs. It is not legal, however, to specify a " "range " "using specific locations that cross major breakpoint numbers. I.e. " "3.2 - 3.7" " is legal; 2 - 5 is legal; but 3.2 - 4.4 is not legal."; } static llvm::StringRef BreakpointNameHelpTextCallback() { return "A name that can be added to a breakpoint when it is created, or " "later " "on with the \"breakpoint name add\" command. " "Breakpoint names can be used to specify breakpoints in all the " "places breakpoint IDs " "and breakpoint ID ranges can be used. As such they provide a " "convenient way to group breakpoints, " "and to operate on breakpoints you create without having to track the " "breakpoint number. " "Note, the attributes you set when using a breakpoint name in a " "breakpoint command don't " "adhere to the name, but instead are set individually on all the " "breakpoints currently tagged with that " "name. Future breakpoints " "tagged with that name will not pick up the attributes previously " "given using that name. " "In order to distinguish breakpoint names from breakpoint IDs and " "ranges, " "names must start with a letter from a-z or A-Z and cannot contain " "spaces, \".\" or \"-\". " "Also, breakpoint names can only be applied to breakpoints, not to " "breakpoint locations."; } static llvm::StringRef GDBFormatHelpTextCallback() { return "A GDB format consists of a repeat count, a format letter and a size " "letter. " "The repeat count is optional and defaults to 1. The format letter is " "optional " "and defaults to the previous format that was used. The size letter " "is optional " "and defaults to the previous size that was used.\n" "\n" "Format letters include:\n" "o - octal\n" "x - hexadecimal\n" "d - decimal\n" "u - unsigned decimal\n" "t - binary\n" "f - float\n" "a - address\n" "i - instruction\n" "c - char\n" "s - string\n" "T - OSType\n" "A - float as hex\n" "\n" "Size letters include:\n" "b - 1 byte (byte)\n" "h - 2 bytes (halfword)\n" "w - 4 bytes (word)\n" "g - 8 bytes (giant)\n" "\n" "Example formats:\n" "32xb - show 32 1 byte hexadecimal integer values\n" "16xh - show 16 2 byte hexadecimal integer values\n" "64 - show 64 2 byte hexadecimal integer values (format and size " "from the last format)\n" "dw - show 1 4 byte decimal integer value\n"; } static llvm::StringRef FormatHelpTextCallback() { static std::string help_text; if (!help_text.empty()) return help_text; StreamString sstr; sstr << "One of the format names (or one-character names) that can be used " "to show a variable's value:\n"; for (Format f = eFormatDefault; f < kNumFormats; f = Format(f + 1)) { if (f != eFormatDefault) sstr.PutChar('\n'); char format_char = FormatManager::GetFormatAsFormatChar(f); if (format_char) sstr.Printf("'%c' or ", format_char); sstr.Printf("\"%s\"", FormatManager::GetFormatAsCString(f)); } sstr.Flush(); help_text = sstr.GetString(); return help_text; } static llvm::StringRef LanguageTypeHelpTextCallback() { static std::string help_text; if (!help_text.empty()) return help_text; StreamString sstr; sstr << "One of the following languages:\n"; Language::PrintAllLanguages(sstr, " ", "\n"); sstr.Flush(); help_text = sstr.GetString(); return help_text; } static llvm::StringRef SummaryStringHelpTextCallback() { return "A summary string is a way to extract information from variables in " "order to present them using a summary.\n" "Summary strings contain static text, variables, scopes and control " "sequences:\n" " - Static text can be any sequence of non-special characters, i.e. " "anything but '{', '}', '$', or '\\'.\n" " - Variables are sequences of characters beginning with ${, ending " "with } and that contain symbols in the format described below.\n" " - Scopes are any sequence of text between { and }. Anything " "included in a scope will only appear in the output summary if there " "were no errors.\n" " - Control sequences are the usual C/C++ '\\a', '\\n', ..., plus " "'\\$', '\\{' and '\\}'.\n" "A summary string works by copying static text verbatim, turning " "control sequences into their character counterpart, expanding " "variables and trying to expand scopes.\n" "A variable is expanded by giving it a value other than its textual " "representation, and the way this is done depends on what comes after " "the ${ marker.\n" "The most common sequence if ${var followed by an expression path, " "which is the text one would type to access a member of an aggregate " "types, given a variable of that type" " (e.g. if type T has a member named x, which has a member named y, " "and if t is of type T, the expression path would be .x.y and the way " "to fit that into a summary string would be" " ${var.x.y}). You can also use ${*var followed by an expression path " "and in that case the object referred by the path will be " "dereferenced before being displayed." " If the object is not a pointer, doing so will cause an error. For " "additional details on expression paths, you can type 'help " "expr-path'. \n" "By default, summary strings attempt to display the summary for any " "variable they reference, and if that fails the value. If neither can " "be shown, nothing is displayed." "In a summary string, you can also use an array index [n], or a " "slice-like range [n-m]. This can have two different meanings " "depending on what kind of object the expression" " path refers to:\n" " - if it is a scalar type (any basic type like int, float, ...) the " "expression is a bitfield, i.e. the bits indicated by the indexing " "operator are extracted out of the number" " and displayed as an individual variable\n" " - if it is an array or pointer the array items indicated by the " "indexing operator are shown as the result of the variable. if the " "expression is an array, real array items are" " printed; if it is a pointer, the pointer-as-array syntax is used to " "obtain the values (this means, the latter case can have no range " "checking)\n" "If you are trying to display an array for which the size is known, " "you can also use [] instead of giving an exact range. This has the " "effect of showing items 0 thru size - 1.\n" "Additionally, a variable can contain an (optional) format code, as " "in ${var.x.y%code}, where code can be any of the valid formats " "described in 'help format', or one of the" " special symbols only allowed as part of a variable:\n" " %V: show the value of the object by default\n" " %S: show the summary of the object by default\n" " %@: show the runtime-provided object description (for " "Objective-C, it calls NSPrintForDebugger; for C/C++ it does " "nothing)\n" " %L: show the location of the object (memory address or a " "register name)\n" " %#: show the number of children of the object\n" " %T: show the type of the object\n" "Another variable that you can use in summary strings is ${svar . " "This sequence works exactly like ${var, including the fact that " "${*svar is an allowed sequence, but uses" " the object's synthetic children provider instead of the actual " "objects. For instance, if you are using STL synthetic children " "providers, the following summary string would" " count the number of actual elements stored in an std::list:\n" "type summary add -s \"${svar%#}\" -x \"std::list<\""; } static llvm::StringRef ExprPathHelpTextCallback() { return "An expression path is the sequence of symbols that is used in C/C++ " "to access a member variable of an aggregate object (class).\n" "For instance, given a class:\n" " class foo {\n" " int a;\n" " int b; .\n" " foo* next;\n" " };\n" "the expression to read item b in the item pointed to by next for foo " "aFoo would be aFoo.next->b.\n" "Given that aFoo could just be any object of type foo, the string " "'.next->b' is the expression path, because it can be attached to any " "foo instance to achieve the effect.\n" "Expression paths in LLDB include dot (.) and arrow (->) operators, " "and most commands using expression paths have ways to also accept " "the star (*) operator.\n" "The meaning of these operators is the same as the usual one given to " "them by the C/C++ standards.\n" "LLDB also has support for indexing ([ ]) in expression paths, and " "extends the traditional meaning of the square brackets operator to " "allow bitfield extraction:\n" "for objects of native types (int, float, char, ...) saying '[n-m]' " "as an expression path (where n and m are any positive integers, e.g. " "[3-5]) causes LLDB to extract" " bits n thru m from the value of the variable. If n == m, [n] is " "also allowed as a shortcut syntax. For arrays and pointers, " "expression paths can only contain one index" " and the meaning of the operation is the same as the one defined by " "C/C++ (item extraction). Some commands extend bitfield-like syntax " "for arrays and pointers with the" " meaning of array slicing (taking elements n thru m inside the array " "or pointed-to memory)."; } void CommandObject::FormatLongHelpText(Stream &output_strm, llvm::StringRef long_help) { CommandInterpreter &interpreter = GetCommandInterpreter(); std::stringstream lineStream(long_help); std::string line; while (std::getline(lineStream, line)) { if (line.empty()) { output_strm << "\n"; continue; } size_t result = line.find_first_not_of(" \t"); if (result == std::string::npos) { result = 0; } std::string whitespace_prefix = line.substr(0, result); std::string remainder = line.substr(result); interpreter.OutputFormattedHelpText(output_strm, whitespace_prefix.c_str(), remainder.c_str()); } } void CommandObject::GenerateHelpText(CommandReturnObject &result) { GenerateHelpText(result.GetOutputStream()); result.SetStatus(eReturnStatusSuccessFinishNoResult); } void CommandObject::GenerateHelpText(Stream &output_strm) { CommandInterpreter &interpreter = GetCommandInterpreter(); if (WantsRawCommandString()) { std::string help_text(GetHelp()); help_text.append(" Expects 'raw' input (see 'help raw-input'.)"); interpreter.OutputFormattedHelpText(output_strm, "", "", help_text.c_str(), 1); } else interpreter.OutputFormattedHelpText(output_strm, "", "", GetHelp(), 1); output_strm << "\nSyntax: " << GetSyntax() << "\n"; Options *options = GetOptions(); if (options != nullptr) { options->GenerateOptionUsage( output_strm, this, GetCommandInterpreter().GetDebugger().GetTerminalWidth()); } llvm::StringRef long_help = GetHelpLong(); if (!long_help.empty()) { FormatLongHelpText(output_strm, long_help); } if (!IsDashDashCommand() && options && options->NumCommandOptions() > 0) { if (WantsRawCommandString() && !WantsCompletion()) { // Emit the message about using ' -- ' between the end of the command // options and the raw input conditionally, i.e., only if the command // object does not want completion. interpreter.OutputFormattedHelpText( output_strm, "", "", "\nImportant Note: Because this command takes 'raw' input, if you " "use any command options" " you must use ' -- ' between the end of the command options and the " "beginning of the raw input.", 1); } else if (GetNumArgumentEntries() > 0) { // Also emit a warning about using "--" in case you are using a command // that takes options and arguments. interpreter.OutputFormattedHelpText( output_strm, "", "", "\nThis command takes options and free-form arguments. If your " "arguments resemble" " option specifiers (i.e., they start with a - or --), you must use " "' -- ' between" " the end of the command options and the beginning of the arguments.", 1); } } } void CommandObject::AddIDsArgumentData(CommandArgumentEntry &arg, CommandArgumentType ID, CommandArgumentType IDRange) { CommandArgumentData id_arg; CommandArgumentData id_range_arg; // Create the first variant for the first (and only) argument for this // command. id_arg.arg_type = ID; id_arg.arg_repetition = eArgRepeatOptional; // Create the second variant for the first (and only) argument for this // command. id_range_arg.arg_type = IDRange; id_range_arg.arg_repetition = eArgRepeatOptional; // The first (and only) argument for this command could be either an id or an // id_range. Push both variants into the entry for the first argument for // this command. arg.push_back(id_arg); arg.push_back(id_range_arg); } const char *CommandObject::GetArgumentTypeAsCString( const lldb::CommandArgumentType arg_type) { assert(arg_type < eArgTypeLastArg && "Invalid argument type passed to GetArgumentTypeAsCString"); return g_arguments_data[arg_type].arg_name; } const char *CommandObject::GetArgumentDescriptionAsCString( const lldb::CommandArgumentType arg_type) { assert(arg_type < eArgTypeLastArg && "Invalid argument type passed to GetArgumentDescriptionAsCString"); return g_arguments_data[arg_type].help_text; } Target *CommandObject::GetDummyTarget() { return m_interpreter.GetDebugger().GetDummyTarget(); } Target *CommandObject::GetSelectedOrDummyTarget(bool prefer_dummy) { return m_interpreter.GetDebugger().GetSelectedOrDummyTarget(prefer_dummy); } Thread *CommandObject::GetDefaultThread() { Thread *thread_to_use = m_exe_ctx.GetThreadPtr(); if (thread_to_use) return thread_to_use; Process *process = m_exe_ctx.GetProcessPtr(); if (!process) { Target *target = m_exe_ctx.GetTargetPtr(); if (!target) { target = m_interpreter.GetDebugger().GetSelectedTarget().get(); } if (target) process = target->GetProcessSP().get(); } if (process) return process->GetThreadList().GetSelectedThread().get(); else return nullptr; } bool CommandObjectParsed::Execute(const char *args_string, CommandReturnObject &result) { bool handled = false; Args cmd_args(args_string); if (HasOverrideCallback()) { Args full_args(GetCommandName()); full_args.AppendArguments(cmd_args); handled = InvokeOverrideCallback(full_args.GetConstArgumentVector(), result); } if (!handled) { for (auto entry : llvm::enumerate(cmd_args.entries())) { if (!entry.value().ref.empty() && entry.value().ref.front() == '`') { cmd_args.ReplaceArgumentAtIndex( entry.index(), m_interpreter.ProcessEmbeddedScriptCommands(entry.value().c_str())); } } if (CheckRequirements(result)) { if (ParseOptions(cmd_args, result)) { // Call the command-specific version of 'Execute', passing it the // already processed arguments. handled = DoExecute(cmd_args, result); } } Cleanup(); } return handled; } bool CommandObjectRaw::Execute(const char *args_string, CommandReturnObject &result) { bool handled = false; if (HasOverrideCallback()) { std::string full_command(GetCommandName()); full_command += ' '; full_command += args_string; const char *argv[2] = {nullptr, nullptr}; argv[0] = full_command.c_str(); handled = InvokeOverrideCallback(argv, result); } if (!handled) { if (CheckRequirements(result)) handled = DoExecute(args_string, result); Cleanup(); } return handled; } static llvm::StringRef arch_helper() { static StreamString g_archs_help; if (g_archs_help.Empty()) { StringList archs; ArchSpec::ListSupportedArchNames(archs); g_archs_help.Printf("These are the supported architecture names:\n"); archs.Join("\n", g_archs_help); } return g_archs_help.GetString(); } CommandObject::ArgumentTableEntry CommandObject::g_arguments_data[] = { // clang-format off { eArgTypeAddress, "address", CommandCompletions::eNoCompletion, { nullptr, false }, "A valid address in the target program's execution space." }, { eArgTypeAddressOrExpression, "address-expression", CommandCompletions::eNoCompletion, { nullptr, false }, "An expression that resolves to an address." }, { eArgTypeAliasName, "alias-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of an abbreviation (alias) for a debugger command." }, { eArgTypeAliasOptions, "options-for-aliased-command", CommandCompletions::eNoCompletion, { nullptr, false }, "Command options to be used as part of an alias (abbreviation) definition. (See 'help commands alias' for more information.)" }, { eArgTypeArchitecture, "arch", CommandCompletions::eArchitectureCompletion, { arch_helper, true }, "The architecture name, e.g. i386 or x86_64." }, { eArgTypeBoolean, "boolean", CommandCompletions::eNoCompletion, { nullptr, false }, "A Boolean value: 'true' or 'false'" }, { eArgTypeBreakpointID, "breakpt-id", CommandCompletions::eNoCompletion, { BreakpointIDHelpTextCallback, false }, nullptr }, { eArgTypeBreakpointIDRange, "breakpt-id-list", CommandCompletions::eNoCompletion, { BreakpointIDRangeHelpTextCallback, false }, nullptr }, { eArgTypeBreakpointName, "breakpoint-name", CommandCompletions::eNoCompletion, { BreakpointNameHelpTextCallback, false }, nullptr }, { eArgTypeByteSize, "byte-size", CommandCompletions::eNoCompletion, { nullptr, false }, "Number of bytes to use." }, { eArgTypeClassName, "class-name", CommandCompletions::eNoCompletion, { nullptr, false }, "Then name of a class from the debug information in the program." }, { eArgTypeCommandName, "cmd-name", CommandCompletions::eNoCompletion, { nullptr, false }, "A debugger command (may be multiple words), without any options or arguments." }, { eArgTypeCount, "count", CommandCompletions::eNoCompletion, { nullptr, false }, "An unsigned integer." }, { eArgTypeDirectoryName, "directory", CommandCompletions::eDiskDirectoryCompletion, { nullptr, false }, "A directory name." }, { eArgTypeDisassemblyFlavor, "disassembly-flavor", CommandCompletions::eNoCompletion, { nullptr, false }, "A disassembly flavor recognized by your disassembly plugin. Currently the only valid options are \"att\" and \"intel\" for Intel targets" }, { eArgTypeDescriptionVerbosity, "description-verbosity", CommandCompletions::eNoCompletion, { nullptr, false }, "How verbose the output of 'po' should be." }, { eArgTypeEndAddress, "end-address", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." }, { eArgTypeExpression, "expr", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." }, { eArgTypeExpressionPath, "expr-path", CommandCompletions::eNoCompletion, { ExprPathHelpTextCallback, true }, nullptr }, { eArgTypeExprFormat, "expression-format", CommandCompletions::eNoCompletion, { nullptr, false }, "[ [bool|b] | [bin] | [char|c] | [oct|o] | [dec|i|d|u] | [hex|x] | [float|f] | [cstr|s] ]" }, { eArgTypeFilename, "filename", CommandCompletions::eDiskFileCompletion, { nullptr, false }, "The name of a file (can include path)." }, { eArgTypeFormat, "format", CommandCompletions::eNoCompletion, { FormatHelpTextCallback, true }, nullptr }, { eArgTypeFrameIndex, "frame-index", CommandCompletions::eNoCompletion, { nullptr, false }, "Index into a thread's list of frames." }, { eArgTypeFullName, "fullname", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." }, { eArgTypeFunctionName, "function-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a function." }, { eArgTypeFunctionOrSymbol, "function-or-symbol", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a function or symbol." }, { eArgTypeGDBFormat, "gdb-format", CommandCompletions::eNoCompletion, { GDBFormatHelpTextCallback, true }, nullptr }, { eArgTypeHelpText, "help-text", CommandCompletions::eNoCompletion, { nullptr, false }, "Text to be used as help for some other entity in LLDB" }, { eArgTypeIndex, "index", CommandCompletions::eNoCompletion, { nullptr, false }, "An index into a list." }, { eArgTypeLanguage, "source-language", CommandCompletions::eNoCompletion, { LanguageTypeHelpTextCallback, true }, nullptr }, { eArgTypeLineNum, "linenum", CommandCompletions::eNoCompletion, { nullptr, false }, "Line number in a source file." }, { eArgTypeLogCategory, "log-category", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a category within a log channel, e.g. all (try \"log list\" to see a list of all channels and their categories." }, { eArgTypeLogChannel, "log-channel", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a log channel, e.g. process.gdb-remote (try \"log list\" to see a list of all channels and their categories)." }, { eArgTypeMethod, "method", CommandCompletions::eNoCompletion, { nullptr, false }, "A C++ method name." }, { eArgTypeName, "name", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." }, { eArgTypeNewPathPrefix, "new-path-prefix", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." }, { eArgTypeNumLines, "num-lines", CommandCompletions::eNoCompletion, { nullptr, false }, "The number of lines to use." }, { eArgTypeNumberPerLine, "number-per-line", CommandCompletions::eNoCompletion, { nullptr, false }, "The number of items per line to display." }, { eArgTypeOffset, "offset", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." }, { eArgTypeOldPathPrefix, "old-path-prefix", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." }, { eArgTypeOneLiner, "one-line-command", CommandCompletions::eNoCompletion, { nullptr, false }, "A command that is entered as a single line of text." }, { eArgTypePath, "path", CommandCompletions::eDiskFileCompletion, { nullptr, false }, "Path." }, { eArgTypePermissionsNumber, "perms-numeric", CommandCompletions::eNoCompletion, { nullptr, false }, "Permissions given as an octal number (e.g. 755)." }, { eArgTypePermissionsString, "perms=string", CommandCompletions::eNoCompletion, { nullptr, false }, "Permissions given as a string value (e.g. rw-r-xr--)." }, { eArgTypePid, "pid", CommandCompletions::eNoCompletion, { nullptr, false }, "The process ID number." }, { eArgTypePlugin, "plugin", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." }, { eArgTypeProcessName, "process-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of the process." }, { eArgTypePythonClass, "python-class", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a Python class." }, { eArgTypePythonFunction, "python-function", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a Python function." }, { eArgTypePythonScript, "python-script", CommandCompletions::eNoCompletion, { nullptr, false }, "Source code written in Python." }, { eArgTypeQueueName, "queue-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of the thread queue." }, { eArgTypeRegisterName, "register-name", CommandCompletions::eNoCompletion, { RegisterNameHelpTextCallback, true }, nullptr }, { eArgTypeRegularExpression, "regular-expression", CommandCompletions::eNoCompletion, { nullptr, false }, "A regular expression." }, { eArgTypeRunArgs, "run-args", CommandCompletions::eNoCompletion, { nullptr, false }, "Arguments to be passed to the target program when it starts executing." }, { eArgTypeRunMode, "run-mode", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." }, { eArgTypeScriptedCommandSynchronicity, "script-cmd-synchronicity", CommandCompletions::eNoCompletion, { nullptr, false }, "The synchronicity to use to run scripted commands with regard to LLDB event system." }, { eArgTypeScriptLang, "script-language", CommandCompletions::eNoCompletion, { nullptr, false }, "The scripting language to be used for script-based commands. Currently only Python is valid." }, { eArgTypeSearchWord, "search-word", CommandCompletions::eNoCompletion, { nullptr, false }, "Any word of interest for search purposes." }, { eArgTypeSelector, "selector", CommandCompletions::eNoCompletion, { nullptr, false }, "An Objective-C selector name." }, { eArgTypeSettingIndex, "setting-index", CommandCompletions::eNoCompletion, { nullptr, false }, "An index into a settings variable that is an array (try 'settings list' to see all the possible settings variables and their types)." }, { eArgTypeSettingKey, "setting-key", CommandCompletions::eNoCompletion, { nullptr, false }, "A key into a settings variables that is a dictionary (try 'settings list' to see all the possible settings variables and their types)." }, { eArgTypeSettingPrefix, "setting-prefix", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a settable internal debugger variable up to a dot ('.'), e.g. 'target.process.'" }, { eArgTypeSettingVariableName, "setting-variable-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a settable internal debugger variable. Type 'settings list' to see a complete list of such variables." }, { eArgTypeShlibName, "shlib-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a shared library." }, { eArgTypeSourceFile, "source-file", CommandCompletions::eSourceFileCompletion, { nullptr, false }, "The name of a source file.." }, { eArgTypeSortOrder, "sort-order", CommandCompletions::eNoCompletion, { nullptr, false }, "Specify a sort order when dumping lists." }, { eArgTypeStartAddress, "start-address", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." }, { eArgTypeSummaryString, "summary-string", CommandCompletions::eNoCompletion, { SummaryStringHelpTextCallback, true }, nullptr }, { eArgTypeSymbol, "symbol", CommandCompletions::eSymbolCompletion, { nullptr, false }, "Any symbol name (function name, variable, argument, etc.)" }, { eArgTypeThreadID, "thread-id", CommandCompletions::eNoCompletion, { nullptr, false }, "Thread ID number." }, { eArgTypeThreadIndex, "thread-index", CommandCompletions::eNoCompletion, { nullptr, false }, "Index into the process' list of threads." }, { eArgTypeThreadName, "thread-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The thread's name." }, { eArgTypeTypeName, "type-name", CommandCompletions::eNoCompletion, { nullptr, false }, "A type name." }, { eArgTypeUnsignedInteger, "unsigned-integer", CommandCompletions::eNoCompletion, { nullptr, false }, "An unsigned integer." }, { eArgTypeUnixSignal, "unix-signal", CommandCompletions::eNoCompletion, { nullptr, false }, "A valid Unix signal name or number (e.g. SIGKILL, KILL or 9)." }, { eArgTypeVarName, "variable-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a variable in your program." }, { eArgTypeValue, "value", CommandCompletions::eNoCompletion, { nullptr, false }, "A value could be anything, depending on where and how it is used." }, { eArgTypeWidth, "width", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." }, { eArgTypeNone, "none", CommandCompletions::eNoCompletion, { nullptr, false }, "No help available for this." }, { eArgTypePlatform, "platform-name", CommandCompletions::ePlatformPluginCompletion, { nullptr, false }, "The name of an installed platform plug-in . Type 'platform list' to see a complete list of installed platforms." }, { eArgTypeWatchpointID, "watchpt-id", CommandCompletions::eNoCompletion, { nullptr, false }, "Watchpoint IDs are positive integers." }, { eArgTypeWatchpointIDRange, "watchpt-id-list", CommandCompletions::eNoCompletion, { nullptr, false }, "For example, '1-3' or '1 to 3'." }, { eArgTypeWatchType, "watch-type", CommandCompletions::eNoCompletion, { nullptr, false }, "Specify the type for a watchpoint." }, { eArgRawInput, "raw-input", CommandCompletions::eNoCompletion, { nullptr, false }, "Free-form text passed to a command without prior interpretation, allowing spaces without requiring quotes. To pass arguments and free form text put two dashes ' -- ' between the last argument and any raw input." }, { eArgTypeCommand, "command", CommandCompletions::eNoCompletion, { nullptr, false }, "An LLDB Command line command." } // clang-format on }; const CommandObject::ArgumentTableEntry *CommandObject::GetArgumentTable() { // If this assertion fires, then the table above is out of date with the // CommandArgumentType enumeration assert((sizeof(CommandObject::g_arguments_data) / sizeof(CommandObject::ArgumentTableEntry)) == eArgTypeLastArg); return CommandObject::g_arguments_data; } Index: vendor/lldb/dist/source/Interpreter/CommandObjectRegexCommand.cpp =================================================================== --- vendor/lldb/dist/source/Interpreter/CommandObjectRegexCommand.cpp (revision 337146) +++ vendor/lldb/dist/source/Interpreter/CommandObjectRegexCommand.cpp (revision 337147) @@ -1,106 +1,105 @@ //===-- CommandObjectRegexCommand.cpp ---------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Interpreter/CommandObjectRegexCommand.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" using namespace lldb; using namespace lldb_private; //---------------------------------------------------------------------- // CommandObjectRegexCommand constructor //---------------------------------------------------------------------- CommandObjectRegexCommand::CommandObjectRegexCommand( CommandInterpreter &interpreter, llvm::StringRef name, llvm::StringRef help, llvm::StringRef syntax, uint32_t max_matches, uint32_t completion_type_mask, bool is_removable) : CommandObjectRaw(interpreter, name, help, syntax), m_max_matches(max_matches), m_completion_type_mask(completion_type_mask), m_entries(), m_is_removable(is_removable) {} //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- CommandObjectRegexCommand::~CommandObjectRegexCommand() {} bool CommandObjectRegexCommand::DoExecute(llvm::StringRef command, CommandReturnObject &result) { EntryCollection::const_iterator pos, end = m_entries.end(); for (pos = m_entries.begin(); pos != end; ++pos) { RegularExpression::Match regex_match(m_max_matches); if (pos->regex.Execute(command, ®ex_match)) { std::string new_command(pos->command); std::string match_str; char percent_var[8]; size_t idx, percent_var_idx; for (uint32_t match_idx = 1; match_idx <= m_max_matches; ++match_idx) { if (regex_match.GetMatchAtIndex(command, match_idx, match_str)) { const int percent_var_len = ::snprintf(percent_var, sizeof(percent_var), "%%%u", match_idx); for (idx = 0; (percent_var_idx = new_command.find( percent_var, idx)) != std::string::npos;) { new_command.erase(percent_var_idx, percent_var_len); new_command.insert(percent_var_idx, match_str); idx += percent_var_idx + match_str.size(); } } } // Interpret the new command and return this as the result! if (m_interpreter.GetExpandRegexAliases()) result.GetOutputStream().Printf("%s\n", new_command.c_str()); // Pass in true for "no context switching". The command that called us // should have set up the context appropriately, we shouldn't have to // redo that. return m_interpreter.HandleCommand( new_command.c_str(), eLazyBoolCalculate, result, nullptr, true, true); } } result.SetStatus(eReturnStatusFailed); if (!GetSyntax().empty()) result.AppendError(GetSyntax()); else result.GetOutputStream() << "Command contents '" << command << "' failed to match any " "regular expression in the '" << m_cmd_name << "' regex "; return false; } bool CommandObjectRegexCommand::AddRegexCommand(const char *re_cstr, const char *command_cstr) { m_entries.resize(m_entries.size() + 1); // Only add the regular expression if it compiles if (m_entries.back().regex.Compile( llvm::StringRef::withNullAsEmpty(re_cstr))) { m_entries.back().command.assign(command_cstr); return true; } // The regex didn't compile... m_entries.pop_back(); return false; } int CommandObjectRegexCommand::HandleCompletion(CompletionRequest &request) { if (m_completion_type_mask) { CommandCompletions::InvokeCommonCompletionCallbacks( GetCommandInterpreter(), m_completion_type_mask, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } else { - request.GetMatches().Clear(); request.SetWordComplete(false); } return 0; } Index: vendor/lldb/dist/source/Interpreter/OptionValue.cpp =================================================================== --- vendor/lldb/dist/source/Interpreter/OptionValue.cpp (revision 337146) +++ vendor/lldb/dist/source/Interpreter/OptionValue.cpp (revision 337147) @@ -1,623 +1,622 @@ //===-- OptionValue.cpp -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Interpreter/OptionValue.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Interpreter/OptionValues.h" #include "lldb/Utility/StringList.h" using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------------- // Get this value as a uint64_t value if it is encoded as a boolean, uint64_t // or int64_t. Other types will cause "fail_value" to be returned //------------------------------------------------------------------------- uint64_t OptionValue::GetUInt64Value(uint64_t fail_value, bool *success_ptr) { if (success_ptr) *success_ptr = true; switch (GetType()) { case OptionValue::eTypeBoolean: return static_cast(this)->GetCurrentValue(); case OptionValue::eTypeSInt64: return static_cast(this)->GetCurrentValue(); case OptionValue::eTypeUInt64: return static_cast(this)->GetCurrentValue(); default: break; } if (success_ptr) *success_ptr = false; return fail_value; } Status OptionValue::SetSubValue(const ExecutionContext *exe_ctx, VarSetOperationType op, llvm::StringRef name, llvm::StringRef value) { Status error; error.SetErrorStringWithFormat("SetSubValue is not supported"); return error; } OptionValueBoolean *OptionValue::GetAsBoolean() { if (GetType() == OptionValue::eTypeBoolean) return static_cast(this); return nullptr; } const OptionValueBoolean *OptionValue::GetAsBoolean() const { if (GetType() == OptionValue::eTypeBoolean) return static_cast(this); return nullptr; } const OptionValueChar *OptionValue::GetAsChar() const { if (GetType() == OptionValue::eTypeChar) return static_cast(this); return nullptr; } OptionValueChar *OptionValue::GetAsChar() { if (GetType() == OptionValue::eTypeChar) return static_cast(this); return nullptr; } OptionValueFileSpec *OptionValue::GetAsFileSpec() { if (GetType() == OptionValue::eTypeFileSpec) return static_cast(this); return nullptr; } const OptionValueFileSpec *OptionValue::GetAsFileSpec() const { if (GetType() == OptionValue::eTypeFileSpec) return static_cast(this); return nullptr; } OptionValueFileSpecList *OptionValue::GetAsFileSpecList() { if (GetType() == OptionValue::eTypeFileSpecList) return static_cast(this); return nullptr; } const OptionValueFileSpecList *OptionValue::GetAsFileSpecList() const { if (GetType() == OptionValue::eTypeFileSpecList) return static_cast(this); return nullptr; } OptionValueArch *OptionValue::GetAsArch() { if (GetType() == OptionValue::eTypeArch) return static_cast(this); return nullptr; } const OptionValueArch *OptionValue::GetAsArch() const { if (GetType() == OptionValue::eTypeArch) return static_cast(this); return nullptr; } OptionValueArray *OptionValue::GetAsArray() { if (GetType() == OptionValue::eTypeArray) return static_cast(this); return nullptr; } const OptionValueArray *OptionValue::GetAsArray() const { if (GetType() == OptionValue::eTypeArray) return static_cast(this); return nullptr; } OptionValueArgs *OptionValue::GetAsArgs() { if (GetType() == OptionValue::eTypeArgs) return static_cast(this); return nullptr; } const OptionValueArgs *OptionValue::GetAsArgs() const { if (GetType() == OptionValue::eTypeArgs) return static_cast(this); return nullptr; } OptionValueDictionary *OptionValue::GetAsDictionary() { if (GetType() == OptionValue::eTypeDictionary) return static_cast(this); return nullptr; } const OptionValueDictionary *OptionValue::GetAsDictionary() const { if (GetType() == OptionValue::eTypeDictionary) return static_cast(this); return nullptr; } OptionValueEnumeration *OptionValue::GetAsEnumeration() { if (GetType() == OptionValue::eTypeEnum) return static_cast(this); return nullptr; } const OptionValueEnumeration *OptionValue::GetAsEnumeration() const { if (GetType() == OptionValue::eTypeEnum) return static_cast(this); return nullptr; } OptionValueFormat *OptionValue::GetAsFormat() { if (GetType() == OptionValue::eTypeFormat) return static_cast(this); return nullptr; } const OptionValueFormat *OptionValue::GetAsFormat() const { if (GetType() == OptionValue::eTypeFormat) return static_cast(this); return nullptr; } OptionValueLanguage *OptionValue::GetAsLanguage() { if (GetType() == OptionValue::eTypeLanguage) return static_cast(this); return NULL; } const OptionValueLanguage *OptionValue::GetAsLanguage() const { if (GetType() == OptionValue::eTypeLanguage) return static_cast(this); return NULL; } OptionValueFormatEntity *OptionValue::GetAsFormatEntity() { if (GetType() == OptionValue::eTypeFormatEntity) return static_cast(this); return nullptr; } const OptionValueFormatEntity *OptionValue::GetAsFormatEntity() const { if (GetType() == OptionValue::eTypeFormatEntity) return static_cast(this); return nullptr; } OptionValuePathMappings *OptionValue::GetAsPathMappings() { if (GetType() == OptionValue::eTypePathMap) return static_cast(this); return nullptr; } const OptionValuePathMappings *OptionValue::GetAsPathMappings() const { if (GetType() == OptionValue::eTypePathMap) return static_cast(this); return nullptr; } OptionValueProperties *OptionValue::GetAsProperties() { if (GetType() == OptionValue::eTypeProperties) return static_cast(this); return nullptr; } const OptionValueProperties *OptionValue::GetAsProperties() const { if (GetType() == OptionValue::eTypeProperties) return static_cast(this); return nullptr; } OptionValueRegex *OptionValue::GetAsRegex() { if (GetType() == OptionValue::eTypeRegex) return static_cast(this); return nullptr; } const OptionValueRegex *OptionValue::GetAsRegex() const { if (GetType() == OptionValue::eTypeRegex) return static_cast(this); return nullptr; } OptionValueSInt64 *OptionValue::GetAsSInt64() { if (GetType() == OptionValue::eTypeSInt64) return static_cast(this); return nullptr; } const OptionValueSInt64 *OptionValue::GetAsSInt64() const { if (GetType() == OptionValue::eTypeSInt64) return static_cast(this); return nullptr; } OptionValueString *OptionValue::GetAsString() { if (GetType() == OptionValue::eTypeString) return static_cast(this); return nullptr; } const OptionValueString *OptionValue::GetAsString() const { if (GetType() == OptionValue::eTypeString) return static_cast(this); return nullptr; } OptionValueUInt64 *OptionValue::GetAsUInt64() { if (GetType() == OptionValue::eTypeUInt64) return static_cast(this); return nullptr; } const OptionValueUInt64 *OptionValue::GetAsUInt64() const { if (GetType() == OptionValue::eTypeUInt64) return static_cast(this); return nullptr; } OptionValueUUID *OptionValue::GetAsUUID() { if (GetType() == OptionValue::eTypeUUID) return static_cast(this); return nullptr; } const OptionValueUUID *OptionValue::GetAsUUID() const { if (GetType() == OptionValue::eTypeUUID) return static_cast(this); return nullptr; } bool OptionValue::GetBooleanValue(bool fail_value) const { const OptionValueBoolean *option_value = GetAsBoolean(); if (option_value) return option_value->GetCurrentValue(); return fail_value; } bool OptionValue::SetBooleanValue(bool new_value) { OptionValueBoolean *option_value = GetAsBoolean(); if (option_value) { option_value->SetCurrentValue(new_value); return true; } return false; } char OptionValue::GetCharValue(char fail_value) const { const OptionValueChar *option_value = GetAsChar(); if (option_value) return option_value->GetCurrentValue(); return fail_value; } char OptionValue::SetCharValue(char new_value) { OptionValueChar *option_value = GetAsChar(); if (option_value) { option_value->SetCurrentValue(new_value); return true; } return false; } int64_t OptionValue::GetEnumerationValue(int64_t fail_value) const { const OptionValueEnumeration *option_value = GetAsEnumeration(); if (option_value) return option_value->GetCurrentValue(); return fail_value; } bool OptionValue::SetEnumerationValue(int64_t value) { OptionValueEnumeration *option_value = GetAsEnumeration(); if (option_value) { option_value->SetCurrentValue(value); return true; } return false; } FileSpec OptionValue::GetFileSpecValue() const { const OptionValueFileSpec *option_value = GetAsFileSpec(); if (option_value) return option_value->GetCurrentValue(); return FileSpec(); } bool OptionValue::SetFileSpecValue(const FileSpec &file_spec) { OptionValueFileSpec *option_value = GetAsFileSpec(); if (option_value) { option_value->SetCurrentValue(file_spec, false); return true; } return false; } FileSpecList OptionValue::GetFileSpecListValue() const { const OptionValueFileSpecList *option_value = GetAsFileSpecList(); if (option_value) return option_value->GetCurrentValue(); return FileSpecList(); } lldb::Format OptionValue::GetFormatValue(lldb::Format fail_value) const { const OptionValueFormat *option_value = GetAsFormat(); if (option_value) return option_value->GetCurrentValue(); return fail_value; } bool OptionValue::SetFormatValue(lldb::Format new_value) { OptionValueFormat *option_value = GetAsFormat(); if (option_value) { option_value->SetCurrentValue(new_value); return true; } return false; } lldb::LanguageType OptionValue::GetLanguageValue(lldb::LanguageType fail_value) const { const OptionValueLanguage *option_value = GetAsLanguage(); if (option_value) return option_value->GetCurrentValue(); return fail_value; } bool OptionValue::SetLanguageValue(lldb::LanguageType new_language) { OptionValueLanguage *option_value = GetAsLanguage(); if (option_value) { option_value->SetCurrentValue(new_language); return true; } return false; } const FormatEntity::Entry *OptionValue::GetFormatEntity() const { const OptionValueFormatEntity *option_value = GetAsFormatEntity(); if (option_value) return &option_value->GetCurrentValue(); return nullptr; } const RegularExpression *OptionValue::GetRegexValue() const { const OptionValueRegex *option_value = GetAsRegex(); if (option_value) return option_value->GetCurrentValue(); return nullptr; } int64_t OptionValue::GetSInt64Value(int64_t fail_value) const { const OptionValueSInt64 *option_value = GetAsSInt64(); if (option_value) return option_value->GetCurrentValue(); return fail_value; } bool OptionValue::SetSInt64Value(int64_t new_value) { OptionValueSInt64 *option_value = GetAsSInt64(); if (option_value) { option_value->SetCurrentValue(new_value); return true; } return false; } llvm::StringRef OptionValue::GetStringValue(llvm::StringRef fail_value) const { const OptionValueString *option_value = GetAsString(); if (option_value) return option_value->GetCurrentValueAsRef(); return fail_value; } bool OptionValue::SetStringValue(llvm::StringRef new_value) { OptionValueString *option_value = GetAsString(); if (option_value) { option_value->SetCurrentValue(new_value); return true; } return false; } uint64_t OptionValue::GetUInt64Value(uint64_t fail_value) const { const OptionValueUInt64 *option_value = GetAsUInt64(); if (option_value) return option_value->GetCurrentValue(); return fail_value; } bool OptionValue::SetUInt64Value(uint64_t new_value) { OptionValueUInt64 *option_value = GetAsUInt64(); if (option_value) { option_value->SetCurrentValue(new_value); return true; } return false; } UUID OptionValue::GetUUIDValue() const { const OptionValueUUID *option_value = GetAsUUID(); if (option_value) return option_value->GetCurrentValue(); return UUID(); } bool OptionValue::SetUUIDValue(const UUID &uuid) { OptionValueUUID *option_value = GetAsUUID(); if (option_value) { option_value->SetCurrentValue(uuid); return true; } return false; } const char *OptionValue::GetBuiltinTypeAsCString(Type t) { switch (t) { case eTypeInvalid: return "invalid"; case eTypeArch: return "arch"; case eTypeArgs: return "arguments"; case eTypeArray: return "array"; case eTypeBoolean: return "boolean"; case eTypeChar: return "char"; case eTypeDictionary: return "dictionary"; case eTypeEnum: return "enum"; case eTypeFileSpec: return "file"; case eTypeFileSpecList: return "file-list"; case eTypeFormat: return "format"; case eTypeFormatEntity: return "format-string"; case eTypeLanguage: return "language"; case eTypePathMap: return "path-map"; case eTypeProperties: return "properties"; case eTypeRegex: return "regex"; case eTypeSInt64: return "int"; case eTypeString: return "string"; case eTypeUInt64: return "unsigned"; case eTypeUUID: return "uuid"; } return nullptr; } lldb::OptionValueSP OptionValue::CreateValueFromCStringForTypeMask( const char *value_cstr, uint32_t type_mask, Status &error) { // If only 1 bit is set in the type mask for a dictionary or array then we // know how to decode a value from a cstring lldb::OptionValueSP value_sp; switch (type_mask) { case 1u << eTypeArch: value_sp.reset(new OptionValueArch()); break; case 1u << eTypeBoolean: value_sp.reset(new OptionValueBoolean(false)); break; case 1u << eTypeChar: value_sp.reset(new OptionValueChar('\0')); break; case 1u << eTypeFileSpec: value_sp.reset(new OptionValueFileSpec()); break; case 1u << eTypeFormat: value_sp.reset(new OptionValueFormat(eFormatInvalid)); break; case 1u << eTypeFormatEntity: value_sp.reset(new OptionValueFormatEntity(NULL)); break; case 1u << eTypeLanguage: value_sp.reset(new OptionValueLanguage(eLanguageTypeUnknown)); break; case 1u << eTypeSInt64: value_sp.reset(new OptionValueSInt64()); break; case 1u << eTypeString: value_sp.reset(new OptionValueString()); break; case 1u << eTypeUInt64: value_sp.reset(new OptionValueUInt64()); break; case 1u << eTypeUUID: value_sp.reset(new OptionValueUUID()); break; } if (value_sp) error = value_sp->SetValueFromString( llvm::StringRef::withNullAsEmpty(value_cstr), eVarSetOperationAssign); else error.SetErrorString("unsupported type mask"); return value_sp; } bool OptionValue::DumpQualifiedName(Stream &strm) const { bool dumped_something = false; lldb::OptionValueSP m_parent_sp(m_parent_wp.lock()); if (m_parent_sp) { if (m_parent_sp->DumpQualifiedName(strm)) dumped_something = true; } ConstString name(GetName()); if (name) { if (dumped_something) strm.PutChar('.'); else dumped_something = true; strm << name; } return dumped_something; } size_t OptionValue::AutoComplete(CommandInterpreter &interpreter, CompletionRequest &request) { request.SetWordComplete(false); - request.GetMatches().Clear(); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } Status OptionValue::SetValueFromString(llvm::StringRef value, VarSetOperationType op) { Status error; switch (op) { case eVarSetOperationReplace: error.SetErrorStringWithFormat( "%s objects do not support the 'replace' operation", GetTypeAsCString()); break; case eVarSetOperationInsertBefore: error.SetErrorStringWithFormat( "%s objects do not support the 'insert-before' operation", GetTypeAsCString()); break; case eVarSetOperationInsertAfter: error.SetErrorStringWithFormat( "%s objects do not support the 'insert-after' operation", GetTypeAsCString()); break; case eVarSetOperationRemove: error.SetErrorStringWithFormat( "%s objects do not support the 'remove' operation", GetTypeAsCString()); break; case eVarSetOperationAppend: error.SetErrorStringWithFormat( "%s objects do not support the 'append' operation", GetTypeAsCString()); break; case eVarSetOperationClear: error.SetErrorStringWithFormat( "%s objects do not support the 'clear' operation", GetTypeAsCString()); break; case eVarSetOperationAssign: error.SetErrorStringWithFormat( "%s objects do not support the 'assign' operation", GetTypeAsCString()); break; case eVarSetOperationInvalid: error.SetErrorStringWithFormat("invalid operation performed on a %s object", GetTypeAsCString()); break; } return error; } Index: vendor/lldb/dist/source/Interpreter/OptionValueArch.cpp =================================================================== --- vendor/lldb/dist/source/Interpreter/OptionValueArch.cpp (revision 337146) +++ vendor/lldb/dist/source/Interpreter/OptionValueArch.cpp (revision 337147) @@ -1,84 +1,83 @@ //===-- OptionValueArch.cpp ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Interpreter/OptionValueArch.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/State.h" #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Utility/Args.h" using namespace lldb; using namespace lldb_private; void OptionValueArch::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) { if (dump_mask & eDumpOptionType) strm.Printf("(%s)", GetTypeAsCString()); if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); if (m_current_value.IsValid()) { const char *arch_name = m_current_value.GetArchitectureName(); if (arch_name) strm.PutCString(arch_name); } } } Status OptionValueArch::SetValueFromString(llvm::StringRef value, VarSetOperationType op) { Status error; switch (op) { case eVarSetOperationClear: Clear(); NotifyValueChanged(); break; case eVarSetOperationReplace: case eVarSetOperationAssign: { std::string value_str = value.trim().str(); if (m_current_value.SetTriple(value_str.c_str())) { m_value_was_set = true; NotifyValueChanged(); } else error.SetErrorStringWithFormat("unsupported architecture '%s'", value_str.c_str()); break; } case eVarSetOperationInsertBefore: case eVarSetOperationInsertAfter: case eVarSetOperationRemove: case eVarSetOperationAppend: case eVarSetOperationInvalid: error = OptionValue::SetValueFromString(value, op); break; } return error; } lldb::OptionValueSP OptionValueArch::DeepCopy() const { return OptionValueSP(new OptionValueArch(*this)); } size_t OptionValueArch::AutoComplete(CommandInterpreter &interpreter, CompletionRequest &request) { request.SetWordComplete(false); - request.GetMatches().Clear(); CommandCompletions::InvokeCommonCompletionCallbacks( interpreter, CommandCompletions::eArchitectureCompletion, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } Index: vendor/lldb/dist/source/Interpreter/OptionValueBoolean.cpp =================================================================== --- vendor/lldb/dist/source/Interpreter/OptionValueBoolean.cpp (revision 337146) +++ vendor/lldb/dist/source/Interpreter/OptionValueBoolean.cpp (revision 337147) @@ -1,97 +1,96 @@ //===-- OptionValueBoolean.cpp ----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Interpreter/OptionValueBoolean.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Host/PosixApi.h" #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" #include "llvm/ADT/STLExtras.h" using namespace lldb; using namespace lldb_private; void OptionValueBoolean::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) { if (dump_mask & eDumpOptionType) strm.Printf("(%s)", GetTypeAsCString()); // if (dump_mask & eDumpOptionName) // DumpQualifiedName (strm); if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); strm.PutCString(m_current_value ? "true" : "false"); } } Status OptionValueBoolean::SetValueFromString(llvm::StringRef value_str, VarSetOperationType op) { Status error; switch (op) { case eVarSetOperationClear: Clear(); NotifyValueChanged(); break; case eVarSetOperationReplace: case eVarSetOperationAssign: { bool success = false; bool value = OptionArgParser::ToBoolean(value_str, false, &success); if (success) { m_value_was_set = true; m_current_value = value; NotifyValueChanged(); } else { if (value_str.size() == 0) error.SetErrorString("invalid boolean string value "); else error.SetErrorStringWithFormat("invalid boolean string value: '%s'", value_str.str().c_str()); } } break; case eVarSetOperationInsertBefore: case eVarSetOperationInsertAfter: case eVarSetOperationRemove: case eVarSetOperationAppend: case eVarSetOperationInvalid: error = OptionValue::SetValueFromString(value_str, op); break; } return error; } lldb::OptionValueSP OptionValueBoolean::DeepCopy() const { return OptionValueSP(new OptionValueBoolean(*this)); } size_t OptionValueBoolean::AutoComplete(CommandInterpreter &interpreter, CompletionRequest &request) { request.SetWordComplete(false); - request.GetMatches().Clear(); static const llvm::StringRef g_autocomplete_entries[] = { "true", "false", "on", "off", "yes", "no", "1", "0"}; auto entries = llvm::makeArrayRef(g_autocomplete_entries); // only suggest "true" or "false" by default if (request.GetCursorArgumentPrefix().empty()) entries = entries.take_front(2); for (auto entry : entries) { if (entry.startswith_lower(request.GetCursorArgumentPrefix())) - request.GetMatches().AppendString(entry); + request.AddCompletion(entry); } - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } Index: vendor/lldb/dist/source/Interpreter/OptionValueEnumeration.cpp =================================================================== --- vendor/lldb/dist/source/Interpreter/OptionValueEnumeration.cpp (revision 337146) +++ vendor/lldb/dist/source/Interpreter/OptionValueEnumeration.cpp (revision 337147) @@ -1,131 +1,129 @@ //===-- OptionValueEnumeration.cpp ------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Interpreter/OptionValueEnumeration.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Utility/StringList.h" using namespace lldb; using namespace lldb_private; OptionValueEnumeration::OptionValueEnumeration( const OptionEnumValueElement *enumerators, enum_type value) : OptionValue(), m_current_value(value), m_default_value(value), m_enumerations() { SetEnumerations(enumerators); } OptionValueEnumeration::~OptionValueEnumeration() {} void OptionValueEnumeration::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) { if (dump_mask & eDumpOptionType) strm.Printf("(%s)", GetTypeAsCString()); if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); const size_t count = m_enumerations.GetSize(); for (size_t i = 0; i < count; ++i) { if (m_enumerations.GetValueAtIndexUnchecked(i).value == m_current_value) { strm.PutCString(m_enumerations.GetCStringAtIndex(i).GetStringRef()); return; } } strm.Printf("%" PRIu64, (uint64_t)m_current_value); } } Status OptionValueEnumeration::SetValueFromString(llvm::StringRef value, VarSetOperationType op) { Status error; switch (op) { case eVarSetOperationClear: Clear(); NotifyValueChanged(); break; case eVarSetOperationReplace: case eVarSetOperationAssign: { ConstString const_enumerator_name(value.trim()); const EnumerationMapEntry *enumerator_entry = m_enumerations.FindFirstValueForName(const_enumerator_name); if (enumerator_entry) { m_current_value = enumerator_entry->value.value; NotifyValueChanged(); } else { StreamString error_strm; error_strm.Printf("invalid enumeration value '%s'", value.str().c_str()); const size_t count = m_enumerations.GetSize(); if (count) { error_strm.Printf(", valid values are: %s", m_enumerations.GetCStringAtIndex(0).GetCString()); for (size_t i = 1; i < count; ++i) { error_strm.Printf(", %s", m_enumerations.GetCStringAtIndex(i).GetCString()); } } error.SetErrorString(error_strm.GetString()); } break; } case eVarSetOperationInsertBefore: case eVarSetOperationInsertAfter: case eVarSetOperationRemove: case eVarSetOperationAppend: case eVarSetOperationInvalid: error = OptionValue::SetValueFromString(value, op); break; } return error; } void OptionValueEnumeration::SetEnumerations( const OptionEnumValueElement *enumerators) { m_enumerations.Clear(); if (enumerators) { for (size_t i = 0; enumerators[i].string_value != nullptr; ++i) { ConstString const_enumerator_name(enumerators[i].string_value); EnumeratorInfo enumerator_info = {enumerators[i].value, enumerators[i].usage}; m_enumerations.Append(const_enumerator_name, enumerator_info); } m_enumerations.Sort(); } } lldb::OptionValueSP OptionValueEnumeration::DeepCopy() const { return OptionValueSP(new OptionValueEnumeration(*this)); } size_t OptionValueEnumeration::AutoComplete(CommandInterpreter &interpreter, CompletionRequest &request) { request.SetWordComplete(false); - request.GetMatches().Clear(); const uint32_t num_enumerators = m_enumerations.GetSize(); if (!request.GetCursorArgumentPrefix().empty()) { for (size_t i = 0; i < num_enumerators; ++i) { llvm::StringRef name = m_enumerations.GetCStringAtIndex(i).GetStringRef(); if (name.startswith(request.GetCursorArgumentPrefix())) - request.GetMatches().AppendString(name); + request.AddCompletion(name); } } else { // only suggest "true" or "false" by default for (size_t i = 0; i < num_enumerators; ++i) - request.GetMatches().AppendString( - m_enumerations.GetCStringAtIndex(i).GetStringRef()); + request.AddCompletion(m_enumerations.GetCStringAtIndex(i).GetStringRef()); } - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } Index: vendor/lldb/dist/source/Interpreter/OptionValueFileSpec.cpp =================================================================== --- vendor/lldb/dist/source/Interpreter/OptionValueFileSpec.cpp (revision 337146) +++ vendor/lldb/dist/source/Interpreter/OptionValueFileSpec.cpp (revision 337147) @@ -1,120 +1,119 @@ //===-- OptionValueFileSpec.cpp ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Interpreter/OptionValueFileSpec.h" #include "lldb/Core/State.h" #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Host/FileSystem.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/DataBufferLLVM.h" using namespace lldb; using namespace lldb_private; OptionValueFileSpec::OptionValueFileSpec(bool resolve) : OptionValue(), m_current_value(), m_default_value(), m_data_sp(), m_data_mod_time(), m_completion_mask(CommandCompletions::eDiskFileCompletion), m_resolve(resolve) {} OptionValueFileSpec::OptionValueFileSpec(const FileSpec &value, bool resolve) : OptionValue(), m_current_value(value), m_default_value(value), m_data_sp(), m_data_mod_time(), m_completion_mask(CommandCompletions::eDiskFileCompletion), m_resolve(resolve) {} OptionValueFileSpec::OptionValueFileSpec(const FileSpec ¤t_value, const FileSpec &default_value, bool resolve) : OptionValue(), m_current_value(current_value), m_default_value(default_value), m_data_sp(), m_data_mod_time(), m_completion_mask(CommandCompletions::eDiskFileCompletion), m_resolve(resolve) {} void OptionValueFileSpec::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) { if (dump_mask & eDumpOptionType) strm.Printf("(%s)", GetTypeAsCString()); if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); if (m_current_value) { strm << '"' << m_current_value.GetPath().c_str() << '"'; } } } Status OptionValueFileSpec::SetValueFromString(llvm::StringRef value, VarSetOperationType op) { Status error; switch (op) { case eVarSetOperationClear: Clear(); NotifyValueChanged(); break; case eVarSetOperationReplace: case eVarSetOperationAssign: if (value.size() > 0) { // The setting value may have whitespace, double-quotes, or single-quotes // around the file path to indicate that internal spaces are not word // breaks. Strip off any ws & quotes from the start and end of the file // path - we aren't doing any word // breaking here so the quoting is // unnecessary. NB this will cause a problem if someone tries to specify // a file path that legitimately begins or ends with a " or ' character, // or whitespace. value = value.trim("\"' \t"); m_value_was_set = true; m_current_value.SetFile(value.str(), m_resolve, FileSpec::Style::native); m_data_sp.reset(); m_data_mod_time = llvm::sys::TimePoint<>(); NotifyValueChanged(); } else { error.SetErrorString("invalid value string"); } break; case eVarSetOperationInsertBefore: case eVarSetOperationInsertAfter: case eVarSetOperationRemove: case eVarSetOperationAppend: case eVarSetOperationInvalid: error = OptionValue::SetValueFromString(value, op); break; } return error; } lldb::OptionValueSP OptionValueFileSpec::DeepCopy() const { return OptionValueSP(new OptionValueFileSpec(*this)); } size_t OptionValueFileSpec::AutoComplete(CommandInterpreter &interpreter, CompletionRequest &request) { request.SetWordComplete(false); - request.GetMatches().Clear(); CommandCompletions::InvokeCommonCompletionCallbacks( interpreter, m_completion_mask, request, nullptr); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } const lldb::DataBufferSP &OptionValueFileSpec::GetFileContents() { if (m_current_value) { const auto file_mod_time = FileSystem::GetModificationTime(m_current_value); if (m_data_sp && m_data_mod_time == file_mod_time) return m_data_sp; m_data_sp = DataBufferLLVM::CreateFromPath(m_current_value.GetPath()); m_data_mod_time = file_mod_time; } return m_data_sp; } Index: vendor/lldb/dist/source/Interpreter/OptionValueUUID.cpp =================================================================== --- vendor/lldb/dist/source/Interpreter/OptionValueUUID.cpp (revision 337146) +++ vendor/lldb/dist/source/Interpreter/OptionValueUUID.cpp (revision 337147) @@ -1,97 +1,96 @@ //===-- OptionValueUUID.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Interpreter/OptionValueUUID.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/Module.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" using namespace lldb; using namespace lldb_private; void OptionValueUUID::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) { if (dump_mask & eDumpOptionType) strm.Printf("(%s)", GetTypeAsCString()); if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); m_uuid.Dump(&strm); } } Status OptionValueUUID::SetValueFromString(llvm::StringRef value, VarSetOperationType op) { Status error; switch (op) { case eVarSetOperationClear: Clear(); NotifyValueChanged(); break; case eVarSetOperationReplace: case eVarSetOperationAssign: { if (m_uuid.SetFromStringRef(value) == 0) error.SetErrorStringWithFormat("invalid uuid string value '%s'", value.str().c_str()); else { m_value_was_set = true; NotifyValueChanged(); } } break; case eVarSetOperationInsertBefore: case eVarSetOperationInsertAfter: case eVarSetOperationRemove: case eVarSetOperationAppend: case eVarSetOperationInvalid: error = OptionValue::SetValueFromString(value, op); break; } return error; } lldb::OptionValueSP OptionValueUUID::DeepCopy() const { return OptionValueSP(new OptionValueUUID(*this)); } size_t OptionValueUUID::AutoComplete(CommandInterpreter &interpreter, CompletionRequest &request) { request.SetWordComplete(false); - request.GetMatches().Clear(); ExecutionContext exe_ctx(interpreter.GetExecutionContext()); Target *target = exe_ctx.GetTargetPtr(); if (target) { auto prefix = request.GetCursorArgumentPrefix(); llvm::SmallVector uuid_bytes; if (UUID::DecodeUUIDBytesFromString(prefix, uuid_bytes).empty()) { const size_t num_modules = target->GetImages().GetSize(); for (size_t i = 0; i < num_modules; ++i) { ModuleSP module_sp(target->GetImages().GetModuleAtIndex(i)); if (module_sp) { const UUID &module_uuid = module_sp->GetUUID(); if (module_uuid.IsValid()) { llvm::ArrayRef module_bytes = module_uuid.GetBytes(); if (module_bytes.size() >= uuid_bytes.size() && module_bytes.take_front(uuid_bytes.size()).equals(uuid_bytes)) { - request.GetMatches().AppendString(module_uuid.GetAsString()); + request.AddCompletion(module_uuid.GetAsString()); } } } } } } - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } Index: vendor/lldb/dist/source/Interpreter/Options.cpp =================================================================== --- vendor/lldb/dist/source/Interpreter/Options.cpp (revision 337146) +++ vendor/lldb/dist/source/Interpreter/Options.cpp (revision 337147) @@ -1,1457 +1,1447 @@ //===-- Options.cpp ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Interpreter/Options.h" // C Includes // C++ Includes #include #include #include #include // Other libraries and framework includes // Project includes #include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Target/Target.h" #include "lldb/Utility/StreamString.h" using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------------- // Options //------------------------------------------------------------------------- Options::Options() : m_getopt_table() { BuildValidOptionSets(); } Options::~Options() {} void Options::NotifyOptionParsingStarting(ExecutionContext *execution_context) { m_seen_options.clear(); // Let the subclass reset its option values OptionParsingStarting(execution_context); } Status Options::NotifyOptionParsingFinished(ExecutionContext *execution_context) { return OptionParsingFinished(execution_context); } void Options::OptionSeen(int option_idx) { m_seen_options.insert(option_idx); } // Returns true is set_a is a subset of set_b; Otherwise returns false. bool Options::IsASubset(const OptionSet &set_a, const OptionSet &set_b) { bool is_a_subset = true; OptionSet::const_iterator pos_a; OptionSet::const_iterator pos_b; // set_a is a subset of set_b if every member of set_a is also a member of // set_b for (pos_a = set_a.begin(); pos_a != set_a.end() && is_a_subset; ++pos_a) { pos_b = set_b.find(*pos_a); if (pos_b == set_b.end()) is_a_subset = false; } return is_a_subset; } // Returns the set difference set_a - set_b, i.e. { x | ElementOf (x, set_a) && // !ElementOf (x, set_b) } size_t Options::OptionsSetDiff(const OptionSet &set_a, const OptionSet &set_b, OptionSet &diffs) { size_t num_diffs = 0; OptionSet::const_iterator pos_a; OptionSet::const_iterator pos_b; for (pos_a = set_a.begin(); pos_a != set_a.end(); ++pos_a) { pos_b = set_b.find(*pos_a); if (pos_b == set_b.end()) { ++num_diffs; diffs.insert(*pos_a); } } return num_diffs; } // Returns the union of set_a and set_b. Does not put duplicate members into // the union. void Options::OptionsSetUnion(const OptionSet &set_a, const OptionSet &set_b, OptionSet &union_set) { OptionSet::const_iterator pos; OptionSet::iterator pos_union; // Put all the elements of set_a into the union. for (pos = set_a.begin(); pos != set_a.end(); ++pos) union_set.insert(*pos); // Put all the elements of set_b that are not already there into the union. for (pos = set_b.begin(); pos != set_b.end(); ++pos) { pos_union = union_set.find(*pos); if (pos_union == union_set.end()) union_set.insert(*pos); } } bool Options::VerifyOptions(CommandReturnObject &result) { bool options_are_valid = false; int num_levels = GetRequiredOptions().size(); if (num_levels) { for (int i = 0; i < num_levels && !options_are_valid; ++i) { // This is the correct set of options if: 1). m_seen_options contains // all of m_required_options[i] (i.e. all the required options at this // level are a subset of m_seen_options); AND 2). { m_seen_options - // m_required_options[i] is a subset of m_options_options[i] (i.e. all // the rest of m_seen_options are in the set of optional options at this // level. // Check to see if all of m_required_options[i] are a subset of // m_seen_options if (IsASubset(GetRequiredOptions()[i], m_seen_options)) { // Construct the set difference: remaining_options = {m_seen_options} - // {m_required_options[i]} OptionSet remaining_options; OptionsSetDiff(m_seen_options, GetRequiredOptions()[i], remaining_options); // Check to see if remaining_options is a subset of // m_optional_options[i] if (IsASubset(remaining_options, GetOptionalOptions()[i])) options_are_valid = true; } } } else { options_are_valid = true; } if (options_are_valid) { result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendError("invalid combination of options for the given command"); result.SetStatus(eReturnStatusFailed); } return options_are_valid; } // This is called in the Options constructor, though we could call it lazily if // that ends up being a performance problem. void Options::BuildValidOptionSets() { // Check to see if we already did this. if (m_required_options.size() != 0) return; // Check to see if there are any options. int num_options = NumCommandOptions(); if (num_options == 0) return; auto opt_defs = GetDefinitions(); m_required_options.resize(1); m_optional_options.resize(1); // First count the number of option sets we've got. Ignore // LLDB_ALL_OPTION_SETS... uint32_t num_option_sets = 0; for (const auto &def : opt_defs) { uint32_t this_usage_mask = def.usage_mask; if (this_usage_mask == LLDB_OPT_SET_ALL) { if (num_option_sets == 0) num_option_sets = 1; } else { for (uint32_t j = 0; j < LLDB_MAX_NUM_OPTION_SETS; j++) { if (this_usage_mask & (1 << j)) { if (num_option_sets <= j) num_option_sets = j + 1; } } } } if (num_option_sets > 0) { m_required_options.resize(num_option_sets); m_optional_options.resize(num_option_sets); for (const auto &def : opt_defs) { for (uint32_t j = 0; j < num_option_sets; j++) { if (def.usage_mask & 1 << j) { if (def.required) m_required_options[j].insert(def.short_option); else m_optional_options[j].insert(def.short_option); } } } } } uint32_t Options::NumCommandOptions() { return GetDefinitions().size(); } Option *Options::GetLongOptions() { // Check to see if this has already been done. if (m_getopt_table.empty()) { auto defs = GetDefinitions(); if (defs.empty()) return nullptr; std::map option_seen; m_getopt_table.resize(defs.size() + 1); for (size_t i = 0; i < defs.size(); ++i) { const int short_opt = defs[i].short_option; m_getopt_table[i].definition = &defs[i]; m_getopt_table[i].flag = nullptr; m_getopt_table[i].val = short_opt; if (option_seen.find(short_opt) == option_seen.end()) { option_seen[short_opt] = i; } else if (short_opt) { m_getopt_table[i].val = 0; std::map::const_iterator pos = option_seen.find(short_opt); StreamString strm; if (isprint8(short_opt)) Host::SystemLog(Host::eSystemLogError, "option[%u] --%s has a short option -%c that " "conflicts with option[%u] --%s, short option won't " "be used for --%s\n", (int)i, defs[i].long_option, short_opt, pos->second, m_getopt_table[pos->second].definition->long_option, defs[i].long_option); else Host::SystemLog(Host::eSystemLogError, "option[%u] --%s has a short option 0x%x that " "conflicts with option[%u] --%s, short option won't " "be used for --%s\n", (int)i, defs[i].long_option, short_opt, pos->second, m_getopt_table[pos->second].definition->long_option, defs[i].long_option); } } // getopt_long_only requires a NULL final entry in the table: m_getopt_table.back().definition = nullptr; m_getopt_table.back().flag = nullptr; m_getopt_table.back().val = 0; } if (m_getopt_table.empty()) return nullptr; return &m_getopt_table.front(); } // This function takes INDENT, which tells how many spaces to output at the // front of each line; SPACES, which is a string containing 80 spaces; and // TEXT, which is the text that is to be output. It outputs the text, on // multiple lines if necessary, to RESULT, with INDENT spaces at the front of // each line. It breaks lines on spaces, tabs or newlines, shortening the line // if necessary to not break in the middle of a word. It assumes that each // output line should contain a maximum of OUTPUT_MAX_COLUMNS characters. void Options::OutputFormattedUsageText(Stream &strm, const OptionDefinition &option_def, uint32_t output_max_columns) { std::string actual_text; if (option_def.validator) { const char *condition = option_def.validator->ShortConditionString(); if (condition) { actual_text = "["; actual_text.append(condition); actual_text.append("] "); } } actual_text.append(option_def.usage_text); // Will it all fit on one line? if (static_cast(actual_text.length() + strm.GetIndentLevel()) < output_max_columns) { // Output it as a single line. strm.Indent(actual_text.c_str()); strm.EOL(); } else { // We need to break it up into multiple lines. int text_width = output_max_columns - strm.GetIndentLevel() - 1; int start = 0; int end = start; int final_end = actual_text.length(); int sub_len; while (end < final_end) { // Don't start the 'text' on a space, since we're already outputting the // indentation. while ((start < final_end) && (actual_text[start] == ' ')) start++; end = start + text_width; if (end > final_end) end = final_end; else { // If we're not at the end of the text, make sure we break the line on // white space. while (end > start && actual_text[end] != ' ' && actual_text[end] != '\t' && actual_text[end] != '\n') end--; } sub_len = end - start; if (start != 0) strm.EOL(); strm.Indent(); assert(start < final_end); assert(start + sub_len <= final_end); strm.Write(actual_text.c_str() + start, sub_len); start = end + 1; } strm.EOL(); } } bool Options::SupportsLongOption(const char *long_option) { if (!long_option || !long_option[0]) return false; auto opt_defs = GetDefinitions(); if (opt_defs.empty()) return false; const char *long_option_name = long_option; if (long_option[0] == '-' && long_option[1] == '-') long_option_name += 2; for (auto &def : opt_defs) { if (!def.long_option) continue; if (strcmp(def.long_option, long_option_name) == 0) return true; } return false; } enum OptionDisplayType { eDisplayBestOption, eDisplayShortOption, eDisplayLongOption }; static bool PrintOption(const OptionDefinition &opt_def, OptionDisplayType display_type, const char *header, const char *footer, bool show_optional, Stream &strm) { const bool has_short_option = isprint8(opt_def.short_option) != 0; if (display_type == eDisplayShortOption && !has_short_option) return false; if (header && header[0]) strm.PutCString(header); if (show_optional && !opt_def.required) strm.PutChar('['); const bool show_short_option = has_short_option && display_type != eDisplayLongOption; if (show_short_option) strm.Printf("-%c", opt_def.short_option); else strm.Printf("--%s", opt_def.long_option); switch (opt_def.option_has_arg) { case OptionParser::eNoArgument: break; case OptionParser::eRequiredArgument: strm.Printf(" <%s>", CommandObject::GetArgumentName(opt_def.argument_type)); break; case OptionParser::eOptionalArgument: strm.Printf("%s[<%s>]", show_short_option ? "" : "=", CommandObject::GetArgumentName(opt_def.argument_type)); break; } if (show_optional && !opt_def.required) strm.PutChar(']'); if (footer && footer[0]) strm.PutCString(footer); return true; } void Options::GenerateOptionUsage(Stream &strm, CommandObject *cmd, uint32_t screen_width) { const bool only_print_args = cmd->IsDashDashCommand(); auto opt_defs = GetDefinitions(); const uint32_t save_indent_level = strm.GetIndentLevel(); llvm::StringRef name; StreamString arguments_str; if (cmd) { name = cmd->GetCommandName(); cmd->GetFormattedCommandArguments(arguments_str); } else name = ""; strm.PutCString("\nCommand Options Usage:\n"); strm.IndentMore(2); // First, show each usage level set of options, e.g. [options-for- // level-0] // // [options-for-level-1] // etc. const uint32_t num_options = NumCommandOptions(); if (num_options == 0) return; uint32_t num_option_sets = GetRequiredOptions().size(); uint32_t i; if (!only_print_args) { for (uint32_t opt_set = 0; opt_set < num_option_sets; ++opt_set) { uint32_t opt_set_mask; opt_set_mask = 1 << opt_set; if (opt_set > 0) strm.Printf("\n"); strm.Indent(name); // Different option sets may require different args. StreamString args_str; if (cmd) cmd->GetFormattedCommandArguments(args_str, opt_set_mask); // First go through and print all options that take no arguments as a // single string. If a command has "-a" "-b" and "-c", this will show up // as [-abc] std::set options; std::set::const_iterator options_pos, options_end; for (auto &def : opt_defs) { if (def.usage_mask & opt_set_mask && isprint8(def.short_option)) { // Add current option to the end of out_stream. if (def.required && def.option_has_arg == OptionParser::eNoArgument) { options.insert(def.short_option); } } } if (options.empty() == false) { // We have some required options with no arguments strm.PutCString(" -"); for (i = 0; i < 2; ++i) for (options_pos = options.begin(), options_end = options.end(); options_pos != options_end; ++options_pos) { if (i == 0 && ::islower(*options_pos)) continue; if (i == 1 && ::isupper(*options_pos)) continue; strm << (char)*options_pos; } } options.clear(); for (auto &def : opt_defs) { if (def.usage_mask & opt_set_mask && isprint8(def.short_option)) { // Add current option to the end of out_stream. if (def.required == false && def.option_has_arg == OptionParser::eNoArgument) { options.insert(def.short_option); } } } if (options.empty() == false) { // We have some required options with no arguments strm.PutCString(" [-"); for (i = 0; i < 2; ++i) for (options_pos = options.begin(), options_end = options.end(); options_pos != options_end; ++options_pos) { if (i == 0 && ::islower(*options_pos)) continue; if (i == 1 && ::isupper(*options_pos)) continue; strm << (char)*options_pos; } strm.PutChar(']'); } // First go through and print the required options (list them up front). for (auto &def : opt_defs) { if (def.usage_mask & opt_set_mask && isprint8(def.short_option)) { if (def.required && def.option_has_arg != OptionParser::eNoArgument) PrintOption(def, eDisplayBestOption, " ", nullptr, true, strm); } } // Now go through again, and this time only print the optional options. for (auto &def : opt_defs) { if (def.usage_mask & opt_set_mask) { // Add current option to the end of out_stream. if (!def.required && def.option_has_arg != OptionParser::eNoArgument) PrintOption(def, eDisplayBestOption, " ", nullptr, true, strm); } } if (args_str.GetSize() > 0) { if (cmd->WantsRawCommandString() && !only_print_args) strm.Printf(" --"); strm << " " << args_str.GetString(); if (only_print_args) break; } } } if (cmd && (only_print_args || cmd->WantsRawCommandString()) && arguments_str.GetSize() > 0) { if (!only_print_args) strm.PutChar('\n'); strm.Indent(name); strm << " " << arguments_str.GetString(); } strm.Printf("\n\n"); if (!only_print_args) { // Now print out all the detailed information about the various options: // long form, short form and help text: // -short ( --long_name ) // help text // This variable is used to keep track of which options' info we've printed // out, because some options can be in more than one usage level, but we // only want to print the long form of its information once. std::multimap options_seen; strm.IndentMore(5); // Put the unique command options in a vector & sort it, so we can output // them alphabetically (by short_option) when writing out detailed help for // each option. i = 0; for (auto &def : opt_defs) options_seen.insert(std::make_pair(def.short_option, i++)); // Go through the unique'd and alphabetically sorted vector of options, // find the table entry for each option and write out the detailed help // information for that option. bool first_option_printed = false; for (auto pos : options_seen) { i = pos.second; // Print out the help information for this option. // Put a newline separation between arguments if (first_option_printed) strm.EOL(); else first_option_printed = true; CommandArgumentType arg_type = opt_defs[i].argument_type; StreamString arg_name_str; arg_name_str.Printf("<%s>", CommandObject::GetArgumentName(arg_type)); strm.Indent(); if (opt_defs[i].short_option && isprint8(opt_defs[i].short_option)) { PrintOption(opt_defs[i], eDisplayShortOption, nullptr, nullptr, false, strm); PrintOption(opt_defs[i], eDisplayLongOption, " ( ", " )", false, strm); } else { // Short option is not printable, just print long option PrintOption(opt_defs[i], eDisplayLongOption, nullptr, nullptr, false, strm); } strm.EOL(); strm.IndentMore(5); if (opt_defs[i].usage_text) OutputFormattedUsageText(strm, opt_defs[i], screen_width); if (opt_defs[i].enum_values != nullptr) { strm.Indent(); strm.Printf("Values: "); for (int k = 0; opt_defs[i].enum_values[k].string_value != nullptr; k++) { if (k == 0) strm.Printf("%s", opt_defs[i].enum_values[k].string_value); else strm.Printf(" | %s", opt_defs[i].enum_values[k].string_value); } strm.EOL(); } strm.IndentLess(5); } } // Restore the indent level strm.SetIndentLevel(save_indent_level); } // This function is called when we have been given a potentially incomplete set // of options, such as when an alias has been defined (more options might be // added at at the time the alias is invoked). We need to verify that the // options in the set m_seen_options are all part of a set that may be used // together, but m_seen_options may be missing some of the "required" options. bool Options::VerifyPartialOptions(CommandReturnObject &result) { bool options_are_valid = false; int num_levels = GetRequiredOptions().size(); if (num_levels) { for (int i = 0; i < num_levels && !options_are_valid; ++i) { // In this case we are treating all options as optional rather than // required. Therefore a set of options is correct if m_seen_options is a // subset of the union of m_required_options and m_optional_options. OptionSet union_set; OptionsSetUnion(GetRequiredOptions()[i], GetOptionalOptions()[i], union_set); if (IsASubset(m_seen_options, union_set)) options_are_valid = true; } } return options_are_valid; } bool Options::HandleOptionCompletion(CompletionRequest &request, OptionElementVector &opt_element_vector, CommandInterpreter &interpreter) { request.SetWordComplete(true); // For now we just scan the completions to see if the cursor position is in // an option or its argument. Otherwise we'll call HandleArgumentCompletion. // In the future we can use completion to validate options as well if we // want. auto opt_defs = GetDefinitions(); std::string cur_opt_std_str = request.GetCursorArgumentPrefix().str(); const char *cur_opt_str = cur_opt_std_str.c_str(); for (size_t i = 0; i < opt_element_vector.size(); i++) { int opt_pos = opt_element_vector[i].opt_pos; int opt_arg_pos = opt_element_vector[i].opt_arg_pos; int opt_defs_index = opt_element_vector[i].opt_defs_index; if (opt_pos == request.GetCursorIndex()) { // We're completing the option itself. if (opt_defs_index == OptionArgElement::eBareDash) { // We're completing a bare dash. That means all options are open. // FIXME: We should scan the other options provided and only complete // options // within the option group they belong to. char opt_str[3] = {'-', 'a', '\0'}; for (auto &def : opt_defs) { if (!def.short_option) continue; opt_str[1] = def.short_option; - request.GetMatches().AppendString(opt_str); + request.AddCompletion(opt_str); } return true; } else if (opt_defs_index == OptionArgElement::eBareDoubleDash) { std::string full_name("--"); for (auto &def : opt_defs) { if (!def.short_option) continue; full_name.erase(full_name.begin() + 2, full_name.end()); full_name.append(def.long_option); - request.GetMatches().AppendString(full_name.c_str()); + request.AddCompletion(full_name.c_str()); } return true; } else if (opt_defs_index != OptionArgElement::eUnrecognizedArg) { // We recognized it, if it an incomplete long option, complete it // anyway (getopt_long_only is happy with shortest unique string, but // it's still a nice thing to do.) Otherwise return The string so the // upper level code will know this is a full match and add the " ". if (cur_opt_str && strlen(cur_opt_str) > 2 && cur_opt_str[0] == '-' && cur_opt_str[1] == '-' && strcmp(opt_defs[opt_defs_index].long_option, cur_opt_str) != 0) { std::string full_name("--"); full_name.append(opt_defs[opt_defs_index].long_option); - request.GetMatches().AppendString(full_name.c_str()); + request.AddCompletion(full_name.c_str()); return true; } else { - request.GetMatches().AppendString(request.GetCursorArgument()); + request.AddCompletion(request.GetCursorArgument()); return true; } } else { // FIXME - not handling wrong options yet: // Check to see if they are writing a long option & complete it. // I think we will only get in here if the long option table has two // elements // that are not unique up to this point. getopt_long_only does // shortest unique match for long options already. if (cur_opt_str && strlen(cur_opt_str) > 2 && cur_opt_str[0] == '-' && cur_opt_str[1] == '-') { for (auto &def : opt_defs) { if (!def.long_option) continue; if (strstr(def.long_option, cur_opt_str + 2) == def.long_option) { std::string full_name("--"); full_name.append(def.long_option); - // The options definitions table has duplicates because of the - // way the grouping information is stored, so only add once. - bool duplicate = false; - for (size_t k = 0; k < request.GetMatches().GetSize(); k++) { - if (request.GetMatches().GetStringAtIndex(k) == full_name) { - duplicate = true; - break; - } - } - if (!duplicate) - request.GetMatches().AppendString(full_name.c_str()); + request.AddCompletion(full_name.c_str()); } } } return true; } } else if (opt_arg_pos == request.GetCursorIndex()) { // Okay the cursor is on the completion of an argument. See if it has a // completion, otherwise return no matches. CompletionRequest subrequest = request; subrequest.SetCursorCharPosition(subrequest.GetCursorArgument().size()); if (opt_defs_index != -1) { HandleOptionArgumentCompletion(subrequest, opt_element_vector, i, interpreter); request.SetWordComplete(subrequest.GetWordComplete()); return true; } else { // No completion callback means no completions... return true; } } else { // Not the last element, keep going. continue; } } return false; } bool Options::HandleOptionArgumentCompletion( CompletionRequest &request, OptionElementVector &opt_element_vector, int opt_element_index, CommandInterpreter &interpreter) { auto opt_defs = GetDefinitions(); std::unique_ptr filter_ap; int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos; int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index; // See if this is an enumeration type option, and if so complete it here: OptionEnumValueElement *enum_values = opt_defs[opt_defs_index].enum_values; if (enum_values != nullptr) { bool return_value = false; std::string match_string( request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos), request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos) + request.GetCursorCharPosition()); for (int i = 0; enum_values[i].string_value != nullptr; i++) { if (strstr(enum_values[i].string_value, match_string.c_str()) == enum_values[i].string_value) { - request.GetMatches().AppendString(enum_values[i].string_value); + request.AddCompletion(enum_values[i].string_value); return_value = true; } } return return_value; } // If this is a source file or symbol type completion, and there is a -shlib // option somewhere in the supplied arguments, then make a search filter for // that shared library. // FIXME: Do we want to also have an "OptionType" so we don't have to match // string names? uint32_t completion_mask = opt_defs[opt_defs_index].completion_type; if (completion_mask == 0) { lldb::CommandArgumentType option_arg_type = opt_defs[opt_defs_index].argument_type; if (option_arg_type != eArgTypeNone) { const CommandObject::ArgumentTableEntry *arg_entry = CommandObject::FindArgumentDataByType( opt_defs[opt_defs_index].argument_type); if (arg_entry) completion_mask = arg_entry->completion_type; } } if (completion_mask & CommandCompletions::eSourceFileCompletion || completion_mask & CommandCompletions::eSymbolCompletion) { for (size_t i = 0; i < opt_element_vector.size(); i++) { int cur_defs_index = opt_element_vector[i].opt_defs_index; // trying to use <0 indices will definitely cause problems if (cur_defs_index == OptionArgElement::eUnrecognizedArg || cur_defs_index == OptionArgElement::eBareDash || cur_defs_index == OptionArgElement::eBareDoubleDash) continue; int cur_arg_pos = opt_element_vector[i].opt_arg_pos; const char *cur_opt_name = opt_defs[cur_defs_index].long_option; // If this is the "shlib" option and there was an argument provided, // restrict it to that shared library. if (cur_opt_name && strcmp(cur_opt_name, "shlib") == 0 && cur_arg_pos != -1) { const char *module_name = request.GetParsedLine().GetArgumentAtIndex(cur_arg_pos); if (module_name) { FileSpec module_spec(module_name, false); lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); // Search filters require a target... if (target_sp) filter_ap.reset(new SearchFilterByModule(target_sp, module_spec)); } break; } } } return CommandCompletions::InvokeCommonCompletionCallbacks( interpreter, completion_mask, request, filter_ap.get()); } void OptionGroupOptions::Append(OptionGroup *group) { auto group_option_defs = group->GetDefinitions(); for (uint32_t i = 0; i < group_option_defs.size(); ++i) { m_option_infos.push_back(OptionInfo(group, i)); m_option_defs.push_back(group_option_defs[i]); } } const OptionGroup *OptionGroupOptions::GetGroupWithOption(char short_opt) { for (uint32_t i = 0; i < m_option_defs.size(); i++) { OptionDefinition opt_def = m_option_defs[i]; if (opt_def.short_option == short_opt) return m_option_infos[i].option_group; } return nullptr; } void OptionGroupOptions::Append(OptionGroup *group, uint32_t src_mask, uint32_t dst_mask) { auto group_option_defs = group->GetDefinitions(); for (uint32_t i = 0; i < group_option_defs.size(); ++i) { if (group_option_defs[i].usage_mask & src_mask) { m_option_infos.push_back(OptionInfo(group, i)); m_option_defs.push_back(group_option_defs[i]); m_option_defs.back().usage_mask = dst_mask; } } } void OptionGroupOptions::Finalize() { m_did_finalize = true; } Status OptionGroupOptions::SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, ExecutionContext *execution_context) { // After calling OptionGroupOptions::Append(...), you must finalize the // groups by calling OptionGroupOptions::Finlize() assert(m_did_finalize); Status error; if (option_idx < m_option_infos.size()) { error = m_option_infos[option_idx].option_group->SetOptionValue( m_option_infos[option_idx].option_index, option_value, execution_context); } else { error.SetErrorString("invalid option index"); // Shouldn't happen... } return error; } void OptionGroupOptions::OptionParsingStarting( ExecutionContext *execution_context) { std::set group_set; OptionInfos::iterator pos, end = m_option_infos.end(); for (pos = m_option_infos.begin(); pos != end; ++pos) { OptionGroup *group = pos->option_group; if (group_set.find(group) == group_set.end()) { group->OptionParsingStarting(execution_context); group_set.insert(group); } } } Status OptionGroupOptions::OptionParsingFinished(ExecutionContext *execution_context) { std::set group_set; Status error; OptionInfos::iterator pos, end = m_option_infos.end(); for (pos = m_option_infos.begin(); pos != end; ++pos) { OptionGroup *group = pos->option_group; if (group_set.find(group) == group_set.end()) { error = group->OptionParsingFinished(execution_context); group_set.insert(group); if (error.Fail()) return error; } } return error; } // OptionParser permutes the arguments while processing them, so we create a // temporary array holding to avoid modification of the input arguments. The // options themselves are never modified, but the API expects a char * anyway, // hence the const_cast. static std::vector GetArgvForParsing(const Args &args) { std::vector result; // OptionParser always skips the first argument as it is based on getopt(). result.push_back(const_cast("")); for (const Args::ArgEntry &entry : args) result.push_back(const_cast(entry.c_str())); return result; } // Given a permuted argument, find it's position in the original Args vector. static Args::const_iterator FindOriginalIter(const char *arg, const Args &original) { return llvm::find_if( original, [arg](const Args::ArgEntry &D) { return D.c_str() == arg; }); } // Given a permuted argument, find it's index in the original Args vector. static size_t FindOriginalIndex(const char *arg, const Args &original) { return std::distance(original.begin(), FindOriginalIter(arg, original)); } // Construct a new Args object, consisting of the entries from the original // arguments, but in the permuted order. static Args ReconstituteArgsAfterParsing(llvm::ArrayRef parsed, const Args &original) { Args result; for (const char *arg : parsed) { auto pos = FindOriginalIter(arg, original); assert(pos != original.end()); result.AppendArgument(pos->ref, pos->quote); } return result; } static size_t FindArgumentIndexForOption(const Args &args, const Option &long_option) { std::string short_opt = llvm::formatv("-{0}", char(long_option.val)).str(); std::string long_opt = llvm::formatv("--{0}", long_option.definition->long_option); for (const auto &entry : llvm::enumerate(args)) { if (entry.value().ref.startswith(short_opt) || entry.value().ref.startswith(long_opt)) return entry.index(); } return size_t(-1); } llvm::Expected Options::ParseAlias(const Args &args, OptionArgVector *option_arg_vector, std::string &input_line) { StreamString sstr; int i; Option *long_options = GetLongOptions(); if (long_options == nullptr) { return llvm::make_error("Invalid long options", llvm::inconvertibleErrorCode()); } for (i = 0; long_options[i].definition != nullptr; ++i) { if (long_options[i].flag == nullptr) { sstr << (char)long_options[i].val; switch (long_options[i].definition->option_has_arg) { default: case OptionParser::eNoArgument: break; case OptionParser::eRequiredArgument: sstr << ":"; break; case OptionParser::eOptionalArgument: sstr << "::"; break; } } } Args args_copy = args; std::vector argv = GetArgvForParsing(args); std::unique_lock lock; OptionParser::Prepare(lock); int val; while (1) { int long_options_index = -1; val = OptionParser::Parse(argv.size(), &*argv.begin(), sstr.GetString(), long_options, &long_options_index); if (val == -1) break; if (val == '?') { return llvm::make_error( "Unknown or ambiguous option", llvm::inconvertibleErrorCode()); } if (val == 0) continue; OptionSeen(val); // Look up the long option index if (long_options_index == -1) { for (int j = 0; long_options[j].definition || long_options[j].flag || long_options[j].val; ++j) { if (long_options[j].val == val) { long_options_index = j; break; } } } // See if the option takes an argument, and see if one was supplied. if (long_options_index == -1) { return llvm::make_error( llvm::formatv("Invalid option with value '{0}'.", char(val)).str(), llvm::inconvertibleErrorCode()); } StreamString option_str; option_str.Printf("-%c", val); const OptionDefinition *def = long_options[long_options_index].definition; int has_arg = (def == nullptr) ? OptionParser::eNoArgument : def->option_has_arg; const char *option_arg = nullptr; switch (has_arg) { case OptionParser::eRequiredArgument: if (OptionParser::GetOptionArgument() == nullptr) { return llvm::make_error( llvm::formatv("Option '{0}' is missing argument specifier.", option_str.GetString()) .str(), llvm::inconvertibleErrorCode()); } LLVM_FALLTHROUGH; case OptionParser::eOptionalArgument: option_arg = OptionParser::GetOptionArgument(); LLVM_FALLTHROUGH; case OptionParser::eNoArgument: break; default: return llvm::make_error( llvm::formatv("error with options table; invalid value in has_arg " "field for option '{0}'.", char(val)) .str(), llvm::inconvertibleErrorCode()); } if (!option_arg) option_arg = ""; option_arg_vector->emplace_back(option_str.GetString(), has_arg, option_arg); // Find option in the argument list; also see if it was supposed to take an // argument and if one was supplied. Remove option (and argument, if // given) from the argument list. Also remove them from the // raw_input_string, if one was passed in. size_t idx = FindArgumentIndexForOption(args_copy, long_options[long_options_index]); if (idx == size_t(-1)) continue; if (!input_line.empty()) { auto tmp_arg = args_copy[idx].ref; size_t pos = input_line.find(tmp_arg); if (pos != std::string::npos) input_line.erase(pos, tmp_arg.size()); } args_copy.DeleteArgumentAtIndex(idx); if ((long_options[long_options_index].definition->option_has_arg != OptionParser::eNoArgument) && (OptionParser::GetOptionArgument() != nullptr) && (idx < args_copy.GetArgumentCount()) && (args_copy[idx].ref == OptionParser::GetOptionArgument())) { if (input_line.size() > 0) { auto tmp_arg = args_copy[idx].ref; size_t pos = input_line.find(tmp_arg); if (pos != std::string::npos) input_line.erase(pos, tmp_arg.size()); } args_copy.DeleteArgumentAtIndex(idx); } } return std::move(args_copy); } OptionElementVector Options::ParseForCompletion(const Args &args, uint32_t cursor_index) { OptionElementVector option_element_vector; StreamString sstr; Option *long_options = GetLongOptions(); option_element_vector.clear(); if (long_options == nullptr) return option_element_vector; // Leading : tells getopt to return a : for a missing option argument AND to // suppress error messages. sstr << ":"; for (int i = 0; long_options[i].definition != nullptr; ++i) { if (long_options[i].flag == nullptr) { sstr << (char)long_options[i].val; switch (long_options[i].definition->option_has_arg) { default: case OptionParser::eNoArgument: break; case OptionParser::eRequiredArgument: sstr << ":"; break; case OptionParser::eOptionalArgument: sstr << "::"; break; } } } std::unique_lock lock; OptionParser::Prepare(lock); OptionParser::EnableError(false); int val; auto opt_defs = GetDefinitions(); std::vector dummy_vec = GetArgvForParsing(args); // I stick an element on the end of the input, because if the last element // is option that requires an argument, getopt_long_only will freak out. dummy_vec.push_back(const_cast("")); bool failed_once = false; uint32_t dash_dash_pos = -1; while (1) { bool missing_argument = false; int long_options_index = -1; val = OptionParser::Parse(dummy_vec.size(), &dummy_vec[0], sstr.GetString(), long_options, &long_options_index); if (val == -1) { // When we're completing a "--" which is the last option on line, if (failed_once) break; failed_once = true; // If this is a bare "--" we mark it as such so we can complete it // successfully later. Handling the "--" is a little tricky, since that // may mean end of options or arguments, or the user might want to // complete options by long name. I make this work by checking whether // the cursor is in the "--" argument, and if so I assume we're // completing the long option, otherwise I let it pass to // OptionParser::Parse which will terminate the option parsing. Note, in // either case we continue parsing the line so we can figure out what // other options were passed. This will be useful when we come to // restricting completions based on what other options we've seen on the // line. if (static_cast(OptionParser::GetOptionIndex()) < dummy_vec.size() && (strcmp(dummy_vec[OptionParser::GetOptionIndex() - 1], "--") == 0)) { dash_dash_pos = FindOriginalIndex( dummy_vec[OptionParser::GetOptionIndex() - 1], args); if (dash_dash_pos == cursor_index) { option_element_vector.push_back( OptionArgElement(OptionArgElement::eBareDoubleDash, dash_dash_pos, OptionArgElement::eBareDoubleDash)); continue; } else break; } else break; } else if (val == '?') { option_element_vector.push_back(OptionArgElement( OptionArgElement::eUnrecognizedArg, FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], args), OptionArgElement::eUnrecognizedArg)); continue; } else if (val == 0) { continue; } else if (val == ':') { // This is a missing argument. val = OptionParser::GetOptionErrorCause(); missing_argument = true; } OptionSeen(val); // Look up the long option index if (long_options_index == -1) { for (int j = 0; long_options[j].definition || long_options[j].flag || long_options[j].val; ++j) { if (long_options[j].val == val) { long_options_index = j; break; } } } // See if the option takes an argument, and see if one was supplied. if (long_options_index >= 0) { int opt_defs_index = -1; for (size_t i = 0; i < opt_defs.size(); i++) { if (opt_defs[i].short_option != val) continue; opt_defs_index = i; break; } const OptionDefinition *def = long_options[long_options_index].definition; int has_arg = (def == nullptr) ? OptionParser::eNoArgument : def->option_has_arg; switch (has_arg) { case OptionParser::eNoArgument: option_element_vector.push_back(OptionArgElement( opt_defs_index, FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], args), 0)); break; case OptionParser::eRequiredArgument: if (OptionParser::GetOptionArgument() != nullptr) { int arg_index; if (missing_argument) arg_index = -1; else arg_index = OptionParser::GetOptionIndex() - 2; option_element_vector.push_back(OptionArgElement( opt_defs_index, FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2], args), arg_index)); } else { option_element_vector.push_back(OptionArgElement( opt_defs_index, FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], args), -1)); } break; case OptionParser::eOptionalArgument: if (OptionParser::GetOptionArgument() != nullptr) { option_element_vector.push_back(OptionArgElement( opt_defs_index, FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2], args), FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], args))); } else { option_element_vector.push_back(OptionArgElement( opt_defs_index, FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2], args), FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], args))); } break; default: // The options table is messed up. Here we'll just continue option_element_vector.push_back(OptionArgElement( OptionArgElement::eUnrecognizedArg, FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], args), OptionArgElement::eUnrecognizedArg)); break; } } else { option_element_vector.push_back(OptionArgElement( OptionArgElement::eUnrecognizedArg, FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], args), OptionArgElement::eUnrecognizedArg)); } } // Finally we have to handle the case where the cursor index points at a // single "-". We want to mark that in the option_element_vector, but only // if it is not after the "--". But it turns out that OptionParser::Parse // just ignores an isolated "-". So we have to look it up by hand here. We // only care if it is AT the cursor position. Note, a single quoted dash is // not the same as a single dash... const Args::ArgEntry &cursor = args[cursor_index]; if ((static_cast(dash_dash_pos) == -1 || cursor_index < dash_dash_pos) && !cursor.IsQuoted() && cursor.ref == "-") { option_element_vector.push_back( OptionArgElement(OptionArgElement::eBareDash, cursor_index, OptionArgElement::eBareDash)); } return option_element_vector; } llvm::Expected Options::Parse(const Args &args, ExecutionContext *execution_context, lldb::PlatformSP platform_sp, bool require_validation) { StreamString sstr; Status error; Option *long_options = GetLongOptions(); if (long_options == nullptr) { return llvm::make_error("Invalid long options.", llvm::inconvertibleErrorCode()); } for (int i = 0; long_options[i].definition != nullptr; ++i) { if (long_options[i].flag == nullptr) { if (isprint8(long_options[i].val)) { sstr << (char)long_options[i].val; switch (long_options[i].definition->option_has_arg) { default: case OptionParser::eNoArgument: break; case OptionParser::eRequiredArgument: sstr << ':'; break; case OptionParser::eOptionalArgument: sstr << "::"; break; } } } } std::vector argv = GetArgvForParsing(args); std::unique_lock lock; OptionParser::Prepare(lock); int val; while (1) { int long_options_index = -1; val = OptionParser::Parse(argv.size(), &*argv.begin(), sstr.GetString(), long_options, &long_options_index); if (val == -1) break; // Did we get an error? if (val == '?') { error.SetErrorStringWithFormat("unknown or ambiguous option"); break; } // The option auto-set itself if (val == 0) continue; OptionSeen(val); // Lookup the long option index if (long_options_index == -1) { for (int i = 0; long_options[i].definition || long_options[i].flag || long_options[i].val; ++i) { if (long_options[i].val == val) { long_options_index = i; break; } } } // Call the callback with the option if (long_options_index >= 0 && long_options[long_options_index].definition) { const OptionDefinition *def = long_options[long_options_index].definition; if (!platform_sp) { // User did not pass in an explicit platform. Try to grab from the // execution context. TargetSP target_sp = execution_context ? execution_context->GetTargetSP() : TargetSP(); platform_sp = target_sp ? target_sp->GetPlatform() : PlatformSP(); } OptionValidator *validator = def->validator; if (!platform_sp && require_validation) { // Caller requires validation but we cannot validate as we don't have // the mandatory platform against which to validate. return llvm::make_error( "cannot validate options: no platform available", llvm::inconvertibleErrorCode()); } bool validation_failed = false; if (platform_sp) { // Ensure we have an execution context, empty or not. ExecutionContext dummy_context; ExecutionContext *exe_ctx_p = execution_context ? execution_context : &dummy_context; if (validator && !validator->IsValid(*platform_sp, *exe_ctx_p)) { validation_failed = true; error.SetErrorStringWithFormat("Option \"%s\" invalid. %s", def->long_option, def->validator->LongConditionString()); } } // As long as validation didn't fail, we set the option value. if (!validation_failed) error = SetOptionValue(long_options_index, (def->option_has_arg == OptionParser::eNoArgument) ? nullptr : OptionParser::GetOptionArgument(), execution_context); } else { error.SetErrorStringWithFormat("invalid option with value '%i'", val); } if (error.Fail()) return error.ToError(); } argv.erase(argv.begin(), argv.begin() + OptionParser::GetOptionIndex()); return ReconstituteArgsAfterParsing(argv, args); } Index: vendor/lldb/dist/source/Plugins/Platform/Android/PlatformAndroid.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Platform/Android/PlatformAndroid.cpp (revision 337146) +++ vendor/lldb/dist/source/Plugins/Platform/Android/PlatformAndroid.cpp (revision 337147) @@ -1,400 +1,400 @@ //===-- PlatformAndroid.cpp -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/Section.h" #include "lldb/Core/ValueObject.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/StringConvert.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/UriParser.h" // Project includes #include "AdbClient.h" #include "PlatformAndroid.h" #include "PlatformAndroidRemoteGDBServer.h" #include "lldb/Target/Target.h" using namespace lldb; using namespace lldb_private; using namespace lldb_private::platform_android; using namespace std::chrono; static uint32_t g_initialize_count = 0; static const unsigned int g_android_default_cache_size = 2048; // Fits inside 4k adb packet. void PlatformAndroid::Initialize() { PlatformLinux::Initialize(); if (g_initialize_count++ == 0) { #if defined(__ANDROID__) PlatformSP default_platform_sp(new PlatformAndroid(true)); default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); Platform::SetHostPlatform(default_platform_sp); #endif PluginManager::RegisterPlugin( PlatformAndroid::GetPluginNameStatic(false), PlatformAndroid::GetPluginDescriptionStatic(false), PlatformAndroid::CreateInstance); } } void PlatformAndroid::Terminate() { if (g_initialize_count > 0) { if (--g_initialize_count == 0) { PluginManager::UnregisterPlugin(PlatformAndroid::CreateInstance); } } PlatformLinux::Terminate(); } PlatformSP PlatformAndroid::CreateInstance(bool force, const ArchSpec *arch) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log) { const char *arch_name; if (arch && arch->GetArchitectureName()) arch_name = arch->GetArchitectureName(); else arch_name = ""; const char *triple_cstr = arch ? arch->GetTriple().getTriple().c_str() : ""; log->Printf("PlatformAndroid::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); } bool create = force; if (create == false && arch && arch->IsValid()) { const llvm::Triple &triple = arch->GetTriple(); switch (triple.getVendor()) { case llvm::Triple::PC: create = true; break; #if defined(__ANDROID__) - // Only accept "unknown" for the vendor if the host is android and it + // Only accept "unknown" for the vendor if the host is android and if // "unknown" wasn't specified (it was just returned because it was NOT - // specified_ + // specified). case llvm::Triple::VendorType::UnknownVendor: create = !arch->TripleVendorWasSpecified(); break; #endif default: break; } if (create) { - switch (triple.getOS()) { + switch (triple.getEnvironment()) { case llvm::Triple::Android: break; #if defined(__ANDROID__) // Only accept "unknown" for the OS if the host is android and it // "unknown" wasn't specified (it was just returned because it was NOT // specified) - case llvm::Triple::OSType::UnknownOS: - create = !arch->TripleOSWasSpecified(); + case llvm::Triple::EnvironmentType::UnknownEnvironment: + create = !arch->TripleEnvironmentWasSpecified(); break; #endif default: create = false; break; } } } if (create) { if (log) log->Printf("PlatformAndroid::%s() creating remote-android platform", __FUNCTION__); return PlatformSP(new PlatformAndroid(false)); } if (log) log->Printf( "PlatformAndroid::%s() aborting creation of remote-android platform", __FUNCTION__); return PlatformSP(); } PlatformAndroid::PlatformAndroid(bool is_host) : PlatformLinux(is_host), m_sdk_version(0) {} PlatformAndroid::~PlatformAndroid() {} ConstString PlatformAndroid::GetPluginNameStatic(bool is_host) { if (is_host) { static ConstString g_host_name(Platform::GetHostPlatformName()); return g_host_name; } else { static ConstString g_remote_name("remote-android"); return g_remote_name; } } const char *PlatformAndroid::GetPluginDescriptionStatic(bool is_host) { if (is_host) return "Local Android user platform plug-in."; else return "Remote Android user platform plug-in."; } ConstString PlatformAndroid::GetPluginName() { return GetPluginNameStatic(IsHost()); } Status PlatformAndroid::ConnectRemote(Args &args) { m_device_id.clear(); if (IsHost()) { return Status("can't connect to the host platform '%s', always connected", GetPluginName().GetCString()); } if (!m_remote_platform_sp) m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer()); int port; llvm::StringRef scheme, host, path; const char *url = args.GetArgumentAtIndex(0); if (!url) return Status("URL is null."); if (!UriParser::Parse(url, scheme, host, port, path)) return Status("Invalid URL: %s", url); if (host != "localhost") m_device_id = host; auto error = PlatformLinux::ConnectRemote(args); if (error.Success()) { AdbClient adb; error = AdbClient::CreateByDeviceID(m_device_id, adb); if (error.Fail()) return error; m_device_id = adb.GetDeviceID(); } return error; } Status PlatformAndroid::GetFile(const FileSpec &source, const FileSpec &destination) { if (IsHost() || !m_remote_platform_sp) return PlatformLinux::GetFile(source, destination); FileSpec source_spec(source.GetPath(false), false, FileSpec::Style::posix); if (source_spec.IsRelative()) source_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent( source_spec.GetCString(false)); Status error; auto sync_service = GetSyncService(error); if (error.Fail()) return error; uint32_t mode = 0, size = 0, mtime = 0; error = sync_service->Stat(source_spec, mode, size, mtime); if (error.Fail()) return error; if (mode != 0) return sync_service->PullFile(source_spec, destination); auto source_file = source_spec.GetCString(false); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log) log->Printf("Got mode == 0 on '%s': try to get file via 'shell cat'", source_file); if (strchr(source_file, '\'') != nullptr) return Status("Doesn't support single-quotes in filenames"); // mode == 0 can signify that adbd cannot access the file due security // constraints - try "cat ..." as a fallback. AdbClient adb(m_device_id); char cmd[PATH_MAX]; snprintf(cmd, sizeof(cmd), "cat '%s'", source_file); return adb.ShellToFile(cmd, minutes(1), destination); } Status PlatformAndroid::PutFile(const FileSpec &source, const FileSpec &destination, uint32_t uid, uint32_t gid) { if (IsHost() || !m_remote_platform_sp) return PlatformLinux::PutFile(source, destination, uid, gid); FileSpec destination_spec(destination.GetPath(false), false, FileSpec::Style::posix); if (destination_spec.IsRelative()) destination_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent( destination_spec.GetCString(false)); // TODO: Set correct uid and gid on remote file. Status error; auto sync_service = GetSyncService(error); if (error.Fail()) return error; return sync_service->PushFile(source, destination_spec); } const char *PlatformAndroid::GetCacheHostname() { return m_device_id.c_str(); } Status PlatformAndroid::DownloadModuleSlice(const FileSpec &src_file_spec, const uint64_t src_offset, const uint64_t src_size, const FileSpec &dst_file_spec) { if (src_offset != 0) return Status("Invalid offset - %" PRIu64, src_offset); return GetFile(src_file_spec, dst_file_spec); } Status PlatformAndroid::DisconnectRemote() { Status error = PlatformLinux::DisconnectRemote(); if (error.Success()) { m_device_id.clear(); m_sdk_version = 0; } return error; } uint32_t PlatformAndroid::GetDefaultMemoryCacheLineSize() { return g_android_default_cache_size; } uint32_t PlatformAndroid::GetSdkVersion() { if (!IsConnected()) return 0; if (m_sdk_version != 0) return m_sdk_version; std::string version_string; AdbClient adb(m_device_id); Status error = adb.Shell("getprop ro.build.version.sdk", seconds(5), &version_string); version_string = llvm::StringRef(version_string).trim().str(); if (error.Fail() || version_string.empty()) { Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf("Get SDK version failed. (error: %s, output: %s)", error.AsCString(), version_string.c_str()); return 0; } m_sdk_version = StringConvert::ToUInt32(version_string.c_str()); return m_sdk_version; } Status PlatformAndroid::DownloadSymbolFile(const lldb::ModuleSP &module_sp, const FileSpec &dst_file_spec) { // For oat file we can try to fetch additional debug info from the device ConstString extension = module_sp->GetFileSpec().GetFileNameExtension(); if (extension != ConstString(".oat") && extension != ConstString(".odex")) return Status( "Symbol file downloading only supported for oat and odex files"); // If we have no information about the platform file we can't execute oatdump if (!module_sp->GetPlatformFileSpec()) return Status("No platform file specified"); // Symbolizer isn't available before SDK version 23 if (GetSdkVersion() < 23) return Status("Symbol file generation only supported on SDK 23+"); // If we already have symtab then we don't have to try and generate one if (module_sp->GetSectionList()->FindSectionByName(ConstString(".symtab")) != nullptr) return Status("Symtab already available in the module"); AdbClient adb(m_device_id); std::string tmpdir; Status error = adb.Shell("mktemp --directory --tmpdir /data/local/tmp", seconds(5), &tmpdir); if (error.Fail() || tmpdir.empty()) return Status("Failed to generate temporary directory on the device (%s)", error.AsCString()); tmpdir = llvm::StringRef(tmpdir).trim().str(); // Create file remover for the temporary directory created on the device std::unique_ptr> tmpdir_remover(&tmpdir, [&adb](std::string *s) { StreamString command; command.Printf("rm -rf %s", s->c_str()); Status error = adb.Shell(command.GetData(), seconds(5), nullptr); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log && error.Fail()) log->Printf("Failed to remove temp directory: %s", error.AsCString()); }); FileSpec symfile_platform_filespec(tmpdir, false); symfile_platform_filespec.AppendPathComponent("symbolized.oat"); // Execute oatdump on the remote device to generate a file with symtab StreamString command; command.Printf("oatdump --symbolize=%s --output=%s", module_sp->GetPlatformFileSpec().GetCString(false), symfile_platform_filespec.GetCString(false)); error = adb.Shell(command.GetData(), minutes(1), nullptr); if (error.Fail()) return Status("Oatdump failed: %s", error.AsCString()); // Download the symbolfile from the remote device return GetFile(symfile_platform_filespec, dst_file_spec); } bool PlatformAndroid::GetRemoteOSVersion() { m_os_version = llvm::VersionTuple(GetSdkVersion()); return !m_os_version.empty(); } llvm::StringRef PlatformAndroid::GetLibdlFunctionDeclarations(lldb_private::Process *process) { SymbolContextList matching_symbols; std::vector dl_open_names = { "__dl_dlopen", "dlopen" }; const char *dl_open_name = nullptr; Target &target = process->GetTarget(); for (auto name: dl_open_names) { if (target.GetImages().FindFunctionSymbols(ConstString(name), eFunctionNameTypeFull, matching_symbols)) { dl_open_name = name; break; } } // Older platform versions have the dl function symbols mangled if (dl_open_name == dl_open_names[0]) return R"( extern "C" void* dlopen(const char*, int) asm("__dl_dlopen"); extern "C" void* dlsym(void*, const char*) asm("__dl_dlsym"); extern "C" int dlclose(void*) asm("__dl_dlclose"); extern "C" char* dlerror(void) asm("__dl_dlerror"); )"; return PlatformPOSIX::GetLibdlFunctionDeclarations(process); } AdbClient::SyncService *PlatformAndroid::GetSyncService(Status &error) { if (m_adb_sync_svc && m_adb_sync_svc->IsConnected()) return m_adb_sync_svc.get(); AdbClient adb(m_device_id); m_adb_sync_svc = adb.GetSyncService(error); return (error.Success()) ? m_adb_sync_svc.get() : nullptr; } Index: vendor/lldb/dist/source/Plugins/Platform/Windows/PlatformWindows.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Platform/Windows/PlatformWindows.cpp (revision 337146) +++ vendor/lldb/dist/source/Plugins/Platform/Windows/PlatformWindows.cpp (revision 337147) @@ -1,595 +1,595 @@ //===-- PlatformWindows.cpp -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "PlatformWindows.h" // C Includes #include #if defined(_WIN32) #include "lldb/Host/windows/windows.h" #include #endif // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/BreakpointSite.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Host/HostInfo.h" #include "lldb/Target/Process.h" #include "lldb/Utility/Status.h" using namespace lldb; using namespace lldb_private; static uint32_t g_initialize_count = 0; namespace { class SupportedArchList { public: SupportedArchList() { AddArch(ArchSpec("i686-pc-windows")); AddArch(HostInfo::GetArchitecture(HostInfo::eArchKindDefault)); AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind32)); AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind64)); AddArch(ArchSpec("i386-pc-windows")); } size_t Count() const { return m_archs.size(); } const ArchSpec &operator[](int idx) { return m_archs[idx]; } private: void AddArch(const ArchSpec &spec) { auto iter = std::find_if( m_archs.begin(), m_archs.end(), [spec](const ArchSpec &rhs) { return spec.IsExactMatch(rhs); }); if (iter != m_archs.end()) return; if (spec.IsValid()) m_archs.push_back(spec); } std::vector m_archs; }; } // anonymous namespace PlatformSP PlatformWindows::CreateInstance(bool force, const lldb_private::ArchSpec *arch) { // The only time we create an instance is when we are creating a remote // windows platform const bool is_host = false; bool create = force; if (create == false && arch && arch->IsValid()) { const llvm::Triple &triple = arch->GetTriple(); switch (triple.getVendor()) { case llvm::Triple::PC: create = true; break; - case llvm::Triple::UnknownArch: + case llvm::Triple::UnknownVendor: create = !arch->TripleVendorWasSpecified(); break; default: break; } if (create) { switch (triple.getOS()) { case llvm::Triple::Win32: break; case llvm::Triple::UnknownOS: create = arch->TripleOSWasSpecified(); break; default: create = false; break; } } } if (create) return PlatformSP(new PlatformWindows(is_host)); return PlatformSP(); } lldb_private::ConstString PlatformWindows::GetPluginNameStatic(bool is_host) { if (is_host) { static ConstString g_host_name(Platform::GetHostPlatformName()); return g_host_name; } else { static ConstString g_remote_name("remote-windows"); return g_remote_name; } } const char *PlatformWindows::GetPluginDescriptionStatic(bool is_host) { return is_host ? "Local Windows user platform plug-in." : "Remote Windows user platform plug-in."; } lldb_private::ConstString PlatformWindows::GetPluginName() { return GetPluginNameStatic(IsHost()); } void PlatformWindows::Initialize() { Platform::Initialize(); if (g_initialize_count++ == 0) { #if defined(_WIN32) WSADATA dummy; WSAStartup(MAKEWORD(2, 2), &dummy); // Force a host flag to true for the default platform object. PlatformSP default_platform_sp(new PlatformWindows(true)); default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); Platform::SetHostPlatform(default_platform_sp); #endif PluginManager::RegisterPlugin( PlatformWindows::GetPluginNameStatic(false), PlatformWindows::GetPluginDescriptionStatic(false), PlatformWindows::CreateInstance); } } void PlatformWindows::Terminate(void) { if (g_initialize_count > 0) { if (--g_initialize_count == 0) { #ifdef _WIN32 WSACleanup(); #endif PluginManager::UnregisterPlugin(PlatformWindows::CreateInstance); } } Platform::Terminate(); } //------------------------------------------------------------------ /// Default Constructor //------------------------------------------------------------------ PlatformWindows::PlatformWindows(bool is_host) : Platform(is_host) {} //------------------------------------------------------------------ /// Destructor. /// /// The destructor is virtual since this class is designed to be /// inherited from by the plug-in instance. //------------------------------------------------------------------ PlatformWindows::~PlatformWindows() = default; bool PlatformWindows::GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec) { if (m_remote_platform_sp) return m_remote_platform_sp->GetModuleSpec(module_file_spec, arch, module_spec); return Platform::GetModuleSpec(module_file_spec, arch, module_spec); } Status PlatformWindows::ResolveExecutable( const ModuleSpec &ms, lldb::ModuleSP &exe_module_sp, const FileSpecList *module_search_paths_ptr) { Status error; // Nothing special to do here, just use the actual file and architecture char exe_path[PATH_MAX]; ModuleSpec resolved_module_spec(ms); if (IsHost()) { // if we cant resolve the executable loation based on the current path // variables if (!resolved_module_spec.GetFileSpec().Exists()) { resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); resolved_module_spec.GetFileSpec().SetFile(exe_path, true, FileSpec::Style::native); } if (!resolved_module_spec.GetFileSpec().Exists()) resolved_module_spec.GetFileSpec().ResolveExecutableLocation(); if (resolved_module_spec.GetFileSpec().Exists()) error.Clear(); else { ms.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); error.SetErrorStringWithFormat("unable to find executable for '%s'", exe_path); } } else { if (m_remote_platform_sp) { error = GetCachedExecutable(resolved_module_spec, exe_module_sp, nullptr, *m_remote_platform_sp); } else { // We may connect to a process and use the provided executable (Don't use // local $PATH). if (resolved_module_spec.GetFileSpec().Exists()) error.Clear(); else error.SetErrorStringWithFormat("the platform is not currently " "connected, and '%s' doesn't exist in " "the system root.", exe_path); } } if (error.Success()) { if (resolved_module_spec.GetArchitecture().IsValid()) { error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, nullptr, nullptr, nullptr); if (!exe_module_sp || exe_module_sp->GetObjectFile() == nullptr) { exe_module_sp.reset(); error.SetErrorStringWithFormat( "'%s' doesn't contain the architecture %s", resolved_module_spec.GetFileSpec().GetPath().c_str(), resolved_module_spec.GetArchitecture().GetArchitectureName()); } } else { // No valid architecture was specified, ask the platform for the // architectures that we should be using (in the correct order) and see // if we can find a match that way StreamString arch_names; for (uint32_t idx = 0; GetSupportedArchitectureAtIndex( idx, resolved_module_spec.GetArchitecture()); ++idx) { error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, nullptr, nullptr, nullptr); // Did we find an executable using one of the if (error.Success()) { if (exe_module_sp && exe_module_sp->GetObjectFile()) break; else error.SetErrorToGenericError(); } if (idx > 0) arch_names.PutCString(", "); arch_names.PutCString( resolved_module_spec.GetArchitecture().GetArchitectureName()); } if (error.Fail() || !exe_module_sp) { if (resolved_module_spec.GetFileSpec().Readable()) { error.SetErrorStringWithFormat( "'%s' doesn't contain any '%s' platform architectures: %s", resolved_module_spec.GetFileSpec().GetPath().c_str(), GetPluginName().GetCString(), arch_names.GetData()); } else { error.SetErrorStringWithFormat( "'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); } } } } return error; } bool PlatformWindows::GetRemoteOSVersion() { if (m_remote_platform_sp) { m_os_version = m_remote_platform_sp->GetOSVersion(); return !m_os_version.empty(); } return false; } bool PlatformWindows::GetRemoteOSBuildString(std::string &s) { if (m_remote_platform_sp) return m_remote_platform_sp->GetRemoteOSBuildString(s); s.clear(); return false; } bool PlatformWindows::GetRemoteOSKernelDescription(std::string &s) { if (m_remote_platform_sp) return m_remote_platform_sp->GetRemoteOSKernelDescription(s); s.clear(); return false; } // Remote Platform subclasses need to override this function ArchSpec PlatformWindows::GetRemoteSystemArchitecture() { if (m_remote_platform_sp) return m_remote_platform_sp->GetRemoteSystemArchitecture(); return ArchSpec(); } const char *PlatformWindows::GetHostname() { if (IsHost()) return Platform::GetHostname(); if (m_remote_platform_sp) return m_remote_platform_sp->GetHostname(); return nullptr; } bool PlatformWindows::IsConnected() const { if (IsHost()) return true; else if (m_remote_platform_sp) return m_remote_platform_sp->IsConnected(); return false; } Status PlatformWindows::ConnectRemote(Args &args) { Status error; if (IsHost()) { error.SetErrorStringWithFormat( "can't connect to the host platform '%s', always connected", GetPluginName().AsCString()); } else { if (!m_remote_platform_sp) m_remote_platform_sp = Platform::Create(ConstString("remote-gdb-server"), error); if (m_remote_platform_sp) { if (error.Success()) { if (m_remote_platform_sp) { error = m_remote_platform_sp->ConnectRemote(args); } else { error.SetErrorString( "\"platform connect\" takes a single argument: "); } } } else error.SetErrorString("failed to create a 'remote-gdb-server' platform"); if (error.Fail()) m_remote_platform_sp.reset(); } return error; } Status PlatformWindows::DisconnectRemote() { Status error; if (IsHost()) { error.SetErrorStringWithFormat( "can't disconnect from the host platform '%s', always connected", GetPluginName().AsCString()); } else { if (m_remote_platform_sp) error = m_remote_platform_sp->DisconnectRemote(); else error.SetErrorString("the platform is not currently connected"); } return error; } bool PlatformWindows::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { bool success = false; if (IsHost()) { success = Platform::GetProcessInfo(pid, process_info); } else if (m_remote_platform_sp) { success = m_remote_platform_sp->GetProcessInfo(pid, process_info); } return success; } uint32_t PlatformWindows::FindProcesses(const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { uint32_t match_count = 0; if (IsHost()) { // Let the base class figure out the host details match_count = Platform::FindProcesses(match_info, process_infos); } else { // If we are remote, we can only return results if we are connected if (m_remote_platform_sp) match_count = m_remote_platform_sp->FindProcesses(match_info, process_infos); } return match_count; } Status PlatformWindows::LaunchProcess(ProcessLaunchInfo &launch_info) { Status error; if (IsHost()) { error = Platform::LaunchProcess(launch_info); } else { if (m_remote_platform_sp) error = m_remote_platform_sp->LaunchProcess(launch_info); else error.SetErrorString("the platform is not currently connected"); } return error; } ProcessSP PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, Status &error) { // Windows has special considerations that must be followed when launching or // attaching to a process. The key requirement is that when launching or // attaching to a process, you must do it from the same the thread that will // go into a permanent loop which will then receive debug events from the // process. In particular, this means we can't use any of LLDB's generic // mechanisms to do it for us, because it doesn't have the special knowledge // required for setting up the background thread or passing the right flags. // // Another problem is that that LLDB's standard model for debugging a process // is to first launch it, have it stop at the entry point, and then attach to // it. In Windows this doesn't quite work, you have to specify as an // argument to CreateProcess() that you're going to debug the process. So we // override DebugProcess here to handle this. Launch operations go directly // to the process plugin, and attach operations almost go directly to the // process plugin (but we hijack the events first). In essence, we // encapsulate all the logic of Launching and Attaching in the process // plugin, and PlatformWindows::DebugProcess is just a pass-through to get to // the process plugin. if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { // This is a process attach. Don't need to launch anything. ProcessAttachInfo attach_info(launch_info); return Attach(attach_info, debugger, target, error); } else { ProcessSP process_sp = target->CreateProcess(launch_info.GetListenerForProcess(debugger), launch_info.GetProcessPluginName(), nullptr); // We need to launch and attach to the process. launch_info.GetFlags().Set(eLaunchFlagDebug); if (process_sp) error = process_sp->Launch(launch_info); return process_sp; } } lldb::ProcessSP PlatformWindows::Attach(ProcessAttachInfo &attach_info, Debugger &debugger, Target *target, Status &error) { error.Clear(); lldb::ProcessSP process_sp; if (!IsHost()) { if (m_remote_platform_sp) process_sp = m_remote_platform_sp->Attach(attach_info, debugger, target, error); else error.SetErrorString("the platform is not currently connected"); return process_sp; } if (target == nullptr) { TargetSP new_target_sp; FileSpec emptyFileSpec; ArchSpec emptyArchSpec; error = debugger.GetTargetList().CreateTarget(debugger, "", "", false, nullptr, new_target_sp); target = new_target_sp.get(); } if (!target || error.Fail()) return process_sp; debugger.GetTargetList().SetSelectedTarget(target); const char *plugin_name = attach_info.GetProcessPluginName(); process_sp = target->CreateProcess( attach_info.GetListenerForProcess(debugger), plugin_name, nullptr); process_sp->HijackProcessEvents(attach_info.GetHijackListener()); if (process_sp) error = process_sp->Attach(attach_info); return process_sp; } const char *PlatformWindows::GetUserName(uint32_t uid) { // Check the cache in Platform in case we have already looked this uid up const char *user_name = Platform::GetUserName(uid); if (user_name) return user_name; if (IsRemote() && m_remote_platform_sp) return m_remote_platform_sp->GetUserName(uid); return nullptr; } const char *PlatformWindows::GetGroupName(uint32_t gid) { const char *group_name = Platform::GetGroupName(gid); if (group_name) return group_name; if (IsRemote() && m_remote_platform_sp) return m_remote_platform_sp->GetGroupName(gid); return nullptr; } Status PlatformWindows::GetFileWithUUID(const FileSpec &platform_file, const UUID *uuid_ptr, FileSpec &local_file) { if (IsRemote()) { if (m_remote_platform_sp) return m_remote_platform_sp->GetFileWithUUID(platform_file, uuid_ptr, local_file); } // Default to the local case local_file = platform_file; return Status(); } Status PlatformWindows::GetSharedModule( const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, const FileSpecList *module_search_paths_ptr, ModuleSP *old_module_sp_ptr, bool *did_create_ptr) { Status error; module_sp.reset(); if (IsRemote()) { // If we have a remote platform always, let it try and locate the shared // module first. if (m_remote_platform_sp) { error = m_remote_platform_sp->GetSharedModule( module_spec, process, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); } } if (!module_sp) { // Fall back to the local platform and find the file locally error = Platform::GetSharedModule(module_spec, process, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr); } if (module_sp) module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); return error; } bool PlatformWindows::GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) { static SupportedArchList architectures; if (idx >= architectures.Count()) return false; arch = architectures[idx]; return true; } void PlatformWindows::GetStatus(Stream &strm) { Platform::GetStatus(strm); #ifdef _WIN32 llvm::VersionTuple version = HostInfo::GetOSVersion(); strm << "Host: Windows " << version.getAsString() << '\n'; #endif } bool PlatformWindows::CanDebugProcess() { return true; } Environment PlatformWindows::GetEnvironment() { if (IsRemote()) { if (m_remote_platform_sp) return m_remote_platform_sp->GetEnvironment(); return Environment(); } return Host::GetEnvironment(); } ConstString PlatformWindows::GetFullNameForDylib(ConstString basename) { if (basename.IsEmpty()) return basename; StreamString stream; stream.Printf("%s.dll", basename.GetCString()); return ConstString(stream.GetString()); } Index: vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h =================================================================== --- vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h (revision 337146) +++ vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h (revision 337147) @@ -1,49 +1,47 @@ //===-- DWARFCompileUnit.h --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef SymbolFileDWARF_DWARFCompileUnit_h_ #define SymbolFileDWARF_DWARFCompileUnit_h_ #include "DWARFUnit.h" class DWARFCompileUnit : public DWARFUnit { - friend class DWARFUnit; - public: static DWARFUnitSP Extract(SymbolFileDWARF *dwarf2Data, const lldb_private::DWARFDataExtractor &debug_info, lldb::offset_t *offset_ptr); void Dump(lldb_private::Stream *s) const override; //------------------------------------------------------------------ /// Get the data that contains the DIE information for this unit. /// /// @return /// The correct data (.debug_types for DWARF 4 and earlier, and /// .debug_info for DWARF 5 and later) for the DIE information in /// this unit. //------------------------------------------------------------------ const lldb_private::DWARFDataExtractor &GetData() const override; //------------------------------------------------------------------ /// Get the size in bytes of the header. /// /// @return /// Byte size of the compile unit header //------------------------------------------------------------------ uint32_t GetHeaderByteSize() const override { return m_is_dwarf64 ? 23 : 11; } private: DWARFCompileUnit(SymbolFileDWARF *dwarf2Data); DISALLOW_COPY_AND_ASSIGN(DWARFCompileUnit); }; #endif // SymbolFileDWARF_DWARFCompileUnit_h_ Index: vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFUnit.h =================================================================== --- vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFUnit.h (revision 337146) +++ vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFUnit.h (revision 337147) @@ -1,252 +1,250 @@ //===-- DWARFUnit.h ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef SymbolFileDWARF_DWARFUnit_h_ #define SymbolFileDWARF_DWARFUnit_h_ #include "DWARFDIE.h" #include "DWARFDebugInfoEntry.h" #include "lldb/lldb-enumerations.h" #include "llvm/Support/RWMutex.h" #include class DWARFUnit; class DWARFCompileUnit; class NameToDIE; class SymbolFileDWARF; class SymbolFileDWARFDwo; typedef std::shared_ptr DWARFUnitSP; enum DWARFProducer { eProducerInvalid = 0, eProducerClang, eProducerGCC, eProducerLLVMGCC, eProcucerOther }; class DWARFUnit { - friend class DWARFCompileUnit; - using die_iterator_range = llvm::iterator_range; public: virtual ~DWARFUnit(); void ExtractUnitDIEIfNeeded(); void ExtractDIEsIfNeeded(); class ScopedExtractDIEs { DWARFUnit *m_cu; public: bool m_clear_dies = false; ScopedExtractDIEs(DWARFUnit *cu); ~ScopedExtractDIEs(); DISALLOW_COPY_AND_ASSIGN(ScopedExtractDIEs); ScopedExtractDIEs(ScopedExtractDIEs &&rhs); ScopedExtractDIEs &operator=(ScopedExtractDIEs &&rhs); }; ScopedExtractDIEs ExtractDIEsScoped(); DWARFDIE LookupAddress(const dw_addr_t address); size_t AppendDIEsWithTag(const dw_tag_t tag, DWARFDIECollection &matching_dies, uint32_t depth = UINT32_MAX) const; bool Verify(lldb_private::Stream *s) const; virtual void Dump(lldb_private::Stream *s) const = 0; //------------------------------------------------------------------ /// Get the data that contains the DIE information for this unit. /// /// This will return the correct bytes that contain the data for /// this DWARFUnit. It could be .debug_info or .debug_types /// depending on where the data for this unit originates. /// /// @return /// The correct data for the DIE information in this unit. //------------------------------------------------------------------ virtual const lldb_private::DWARFDataExtractor &GetData() const = 0; //------------------------------------------------------------------ /// Get the size in bytes of the compile unit header. /// /// @return /// Byte size of the compile unit header //------------------------------------------------------------------ virtual uint32_t GetHeaderByteSize() const = 0; // Offset of the initial length field. dw_offset_t GetOffset() const { return m_offset; } lldb::user_id_t GetID() const; //------------------------------------------------------------------ /// Get the size in bytes of the length field in the header. /// /// In DWARF32 this is just 4 bytes, and DWARF64 it is 12 where 4 /// are 0xFFFFFFFF followed by the actual 64 bit length. /// /// @return /// Byte size of the compile unit header length field //------------------------------------------------------------------ size_t GetLengthByteSize() const { return IsDWARF64() ? 12 : 4; } bool ContainsDIEOffset(dw_offset_t die_offset) const { return die_offset >= GetFirstDIEOffset() && die_offset < GetNextCompileUnitOffset(); } dw_offset_t GetFirstDIEOffset() const { return m_offset + GetHeaderByteSize(); } dw_offset_t GetNextCompileUnitOffset() const; // Size of the CU data (without initial length and without header). size_t GetDebugInfoSize() const; // Size of the CU data incl. header but without initial length. uint32_t GetLength() const { return m_length; } uint16_t GetVersion() const { return m_version; } const DWARFAbbreviationDeclarationSet *GetAbbreviations() const; dw_offset_t GetAbbrevOffset() const; uint8_t GetAddressByteSize() const { return m_addr_size; } dw_addr_t GetBaseAddress() const { return m_base_addr; } dw_addr_t GetAddrBase() const { return m_addr_base; } dw_addr_t GetRangesBase() const { return m_ranges_base; } void SetAddrBase(dw_addr_t addr_base, dw_addr_t ranges_base, dw_offset_t base_obj_offset); void BuildAddressRangeTable(SymbolFileDWARF *dwarf, DWARFDebugAranges *debug_aranges); lldb::ByteOrder GetByteOrder() const; lldb_private::TypeSystem *GetTypeSystem(); const DWARFDebugAranges &GetFunctionAranges(); DWARFFormValue::FixedFormSizes GetFixedFormSizes(); void SetBaseAddress(dw_addr_t base_addr); DWARFBaseDIE GetUnitDIEOnly() { return DWARFDIE(this, GetUnitDIEPtrOnly()); } DWARFDIE DIE() { return DWARFDIE(this, DIEPtr()); } DWARFDIE GetDIE(dw_offset_t die_offset); static uint8_t GetAddressByteSize(const DWARFUnit *cu); static bool IsDWARF64(const DWARFUnit *cu); static uint8_t GetDefaultAddressSize(); void *GetUserData() const; void SetUserData(void *d); bool Supports_DW_AT_APPLE_objc_complete_type(); bool DW_AT_decl_file_attributes_are_invalid(); bool Supports_unnamed_objc_bitfields(); SymbolFileDWARF *GetSymbolFileDWARF() const; DWARFProducer GetProducer(); uint32_t GetProducerVersionMajor(); uint32_t GetProducerVersionMinor(); uint32_t GetProducerVersionUpdate(); static lldb::LanguageType LanguageTypeFromDWARF(uint64_t val); lldb::LanguageType GetLanguageType(); bool IsDWARF64() const { return m_is_dwarf64; } bool GetIsOptimized(); SymbolFileDWARFDwo *GetDwoSymbolFile() const; dw_offset_t GetBaseObjOffset() const; die_iterator_range dies() { ExtractDIEsIfNeeded(); return die_iterator_range(m_die_array.begin(), m_die_array.end()); } protected: DWARFUnit(SymbolFileDWARF *dwarf); SymbolFileDWARF *m_dwarf = nullptr; std::unique_ptr m_dwo_symbol_file; const DWARFAbbreviationDeclarationSet *m_abbrevs = nullptr; void *m_user_data = nullptr; // The compile unit debug information entry item DWARFDebugInfoEntry::collection m_die_array; mutable llvm::sys::RWMutex m_die_array_mutex; // It is used for tracking of ScopedExtractDIEs instances. mutable llvm::sys::RWMutex m_die_array_scoped_mutex; // ScopedExtractDIEs instances should not call ClearDIEsRWLocked() // as someone called ExtractDIEsIfNeeded(). std::atomic m_cancel_scopes; // GetUnitDIEPtrOnly() needs to return pointer to the first DIE. // But the first element of m_die_array after ExtractUnitDIEIfNeeded() // would possibly move in memory after later ExtractDIEsIfNeeded(). DWARFDebugInfoEntry m_first_die; llvm::sys::RWMutex m_first_die_mutex; // A table similar to the .debug_aranges table, but this one points to the // exact DW_TAG_subprogram DIEs std::unique_ptr m_func_aranges_ap; dw_addr_t m_base_addr = 0; dw_offset_t m_length = 0; uint16_t m_version = 0; uint8_t m_addr_size = 0; DWARFProducer m_producer = eProducerInvalid; uint32_t m_producer_version_major = 0; uint32_t m_producer_version_minor = 0; uint32_t m_producer_version_update = 0; lldb::LanguageType m_language_type = lldb::eLanguageTypeUnknown; bool m_is_dwarf64 = false; lldb_private::LazyBool m_is_optimized = lldb_private::eLazyBoolCalculate; dw_addr_t m_addr_base = 0; // Value of DW_AT_addr_base dw_addr_t m_ranges_base = 0; // Value of DW_AT_ranges_base // If this is a dwo compile unit this is the offset of the base compile unit // in the main object file dw_offset_t m_base_obj_offset = DW_INVALID_OFFSET; // Offset of the initial length field. dw_offset_t m_offset; private: void ParseProducerInfo(); void ExtractDIEsRWLocked(); void ClearDIEsRWLocked(); // Get the DWARF unit DWARF debug informration entry. Parse the single DIE // if needed. const DWARFDebugInfoEntry *GetUnitDIEPtrOnly() { ExtractUnitDIEIfNeeded(); // m_first_die_mutex is not required as m_first_die is never cleared. if (!m_first_die) return NULL; return &m_first_die; } // Get all DWARF debug informration entries. Parse all DIEs if needed. const DWARFDebugInfoEntry *DIEPtr() { ExtractDIEsIfNeeded(); if (m_die_array.empty()) return NULL; return &m_die_array[0]; } void AddUnitDIE(const DWARFDebugInfoEntry &cu_die); void ExtractDIEsEndCheck(lldb::offset_t offset) const; DISALLOW_COPY_AND_ASSIGN(DWARFUnit); }; #endif // SymbolFileDWARF_DWARFUnit_h_ Index: vendor/lldb/dist/source/Symbol/Variable.cpp =================================================================== --- vendor/lldb/dist/source/Symbol/Variable.cpp (revision 337146) +++ vendor/lldb/dist/source/Symbol/Variable.cpp (revision 337147) @@ -1,768 +1,771 @@ //===-- Variable.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Symbol/Variable.h" #include "lldb/Core/Module.h" #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/CompilerDecl.h" #include "lldb/Symbol/CompilerDeclContext.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ABI.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Stream.h" #include "llvm/ADT/Twine.h" using namespace lldb; using namespace lldb_private; //---------------------------------------------------------------------- // Variable constructor //---------------------------------------------------------------------- Variable::Variable( lldb::user_id_t uid, const char *name, const char *mangled, // The mangled or fully qualified name of the variable. const lldb::SymbolFileTypeSP &symfile_type_sp, ValueType scope, SymbolContextScope *context, const RangeList &scope_range, Declaration *decl_ptr, const DWARFExpression &location, bool external, bool artificial, bool static_member) : UserID(uid), m_name(name), m_mangled(ConstString(mangled)), m_symfile_type_sp(symfile_type_sp), m_scope(scope), m_owner_scope(context), m_scope_range(scope_range), m_declaration(decl_ptr), m_location(location), m_external(external), m_artificial(artificial), m_static_member(static_member) {} //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- Variable::~Variable() {} lldb::LanguageType Variable::GetLanguage() const { SymbolContext variable_sc; m_owner_scope->CalculateSymbolContext(&variable_sc); if (variable_sc.comp_unit) return variable_sc.comp_unit->GetLanguage(); return lldb::eLanguageTypeUnknown; } ConstString Variable::GetName() const { ConstString name = m_mangled.GetName(GetLanguage()); if (name) return name; return m_name; } ConstString Variable::GetUnqualifiedName() const { return m_name; } bool Variable::NameMatches(const ConstString &name) const { if (m_name == name) return true; SymbolContext variable_sc; m_owner_scope->CalculateSymbolContext(&variable_sc); LanguageType language = eLanguageTypeUnknown; if (variable_sc.comp_unit) language = variable_sc.comp_unit->GetLanguage(); return m_mangled.NameMatches(name, language); } bool Variable::NameMatches(const RegularExpression ®ex) const { if (regex.Execute(m_name.AsCString())) return true; if (m_mangled) return m_mangled.NameMatches(regex, GetLanguage()); return false; } Type *Variable::GetType() { if (m_symfile_type_sp) return m_symfile_type_sp->GetType(); return nullptr; } void Variable::Dump(Stream *s, bool show_context) const { s->Printf("%p: ", static_cast(this)); s->Indent(); *s << "Variable" << (const UserID &)*this; if (m_name) *s << ", name = \"" << m_name << "\""; if (m_symfile_type_sp) { Type *type = m_symfile_type_sp->GetType(); if (type) { *s << ", type = {" << type->GetID() << "} " << (void *)type << " ("; type->DumpTypeName(s); s->PutChar(')'); } } if (m_scope != eValueTypeInvalid) { s->PutCString(", scope = "); switch (m_scope) { case eValueTypeVariableGlobal: s->PutCString(m_external ? "global" : "static"); break; case eValueTypeVariableArgument: s->PutCString("parameter"); break; case eValueTypeVariableLocal: s->PutCString("local"); break; case eValueTypeVariableThreadLocal: s->PutCString("thread local"); break; default: *s << "??? (" << m_scope << ')'; } } if (show_context && m_owner_scope != nullptr) { s->PutCString(", context = ( "); m_owner_scope->DumpSymbolContext(s); s->PutCString(" )"); } bool show_fullpaths = false; m_declaration.Dump(s, show_fullpaths); if (m_location.IsValid()) { s->PutCString(", location = "); lldb::addr_t loclist_base_addr = LLDB_INVALID_ADDRESS; if (m_location.IsLocationList()) { SymbolContext variable_sc; m_owner_scope->CalculateSymbolContext(&variable_sc); if (variable_sc.function) loclist_base_addr = variable_sc.function->GetAddressRange() .GetBaseAddress() .GetFileAddress(); } ABI *abi = nullptr; if (m_owner_scope) { ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule()); if (module_sp) abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture()).get(); } m_location.GetDescription(s, lldb::eDescriptionLevelBrief, loclist_base_addr, abi); } if (m_external) s->PutCString(", external"); if (m_artificial) s->PutCString(", artificial"); s->EOL(); } bool Variable::DumpDeclaration(Stream *s, bool show_fullpaths, bool show_module) { bool dumped_declaration_info = false; if (m_owner_scope) { SymbolContext sc; m_owner_scope->CalculateSymbolContext(&sc); sc.block = nullptr; sc.line_entry.Clear(); bool show_inlined_frames = false; const bool show_function_arguments = true; const bool show_function_name = true; dumped_declaration_info = sc.DumpStopContext( s, nullptr, Address(), show_fullpaths, show_module, show_inlined_frames, show_function_arguments, show_function_name); if (sc.function) s->PutChar(':'); } if (m_declaration.DumpStopContext(s, false)) dumped_declaration_info = true; return dumped_declaration_info; } size_t Variable::MemorySize() const { return sizeof(Variable); } CompilerDeclContext Variable::GetDeclContext() { Type *type = GetType(); if (type) return type->GetSymbolFile()->GetDeclContextContainingUID(GetID()); return CompilerDeclContext(); } CompilerDecl Variable::GetDecl() { Type *type = GetType(); return type ? type->GetSymbolFile()->GetDeclForUID(GetID()) : CompilerDecl(); } void Variable::CalculateSymbolContext(SymbolContext *sc) { if (m_owner_scope) { m_owner_scope->CalculateSymbolContext(sc); sc->variable = this; } else sc->Clear(false); } bool Variable::LocationIsValidForFrame(StackFrame *frame) { // Is the variable is described by a single location? if (!m_location.IsLocationList()) { // Yes it is, the location is valid. return true; } if (frame) { Function *function = frame->GetSymbolContext(eSymbolContextFunction).function; if (function) { TargetSP target_sp(frame->CalculateTarget()); addr_t loclist_base_load_addr = function->GetAddressRange().GetBaseAddress().GetLoadAddress( target_sp.get()); if (loclist_base_load_addr == LLDB_INVALID_ADDRESS) return false; // It is a location list. We just need to tell if the location list // contains the current address when converted to a load address return m_location.LocationListContainsAddress( loclist_base_load_addr, frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get())); } } return false; } bool Variable::LocationIsValidForAddress(const Address &address) { // Be sure to resolve the address to section offset prior to calling this // function. if (address.IsSectionOffset()) { SymbolContext sc; CalculateSymbolContext(&sc); if (sc.module_sp == address.GetModule()) { // Is the variable is described by a single location? if (!m_location.IsLocationList()) { // Yes it is, the location is valid. return true; } if (sc.function) { addr_t loclist_base_file_addr = sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) return false; // It is a location list. We just need to tell if the location list // contains the current address when converted to a load address return m_location.LocationListContainsAddress(loclist_base_file_addr, address.GetFileAddress()); } } } return false; } bool Variable::IsInScope(StackFrame *frame) { switch (m_scope) { case eValueTypeRegister: case eValueTypeRegisterSet: return frame != nullptr; case eValueTypeConstResult: case eValueTypeVariableGlobal: case eValueTypeVariableStatic: case eValueTypeVariableThreadLocal: return true; case eValueTypeVariableArgument: case eValueTypeVariableLocal: if (frame) { // We don't have a location list, we just need to see if the block that // this variable was defined in is currently Block *deepest_frame_block = frame->GetSymbolContext(eSymbolContextBlock).block; if (deepest_frame_block) { SymbolContext variable_sc; CalculateSymbolContext(&variable_sc); // Check for static or global variable defined at the compile unit // level that wasn't defined in a block if (variable_sc.block == nullptr) return true; // Check if the variable is valid in the current block if (variable_sc.block != deepest_frame_block && !variable_sc.block->Contains(deepest_frame_block)) return false; // If no scope range is specified then it means that the scope is the // same as the scope of the enclosing lexical block. if (m_scope_range.IsEmpty()) return true; addr_t file_address = frame->GetFrameCodeAddress().GetFileAddress(); return m_scope_range.FindEntryThatContains(file_address) != nullptr; } } break; default: break; } return false; } Status Variable::GetValuesForVariableExpressionPath( llvm::StringRef variable_expr_path, ExecutionContextScope *scope, GetVariableCallback callback, void *baton, VariableList &variable_list, ValueObjectList &valobj_list) { Status error; if (!callback || variable_expr_path.empty()) { error.SetErrorString("unknown error"); return error; } switch (variable_expr_path.front()) { case '*': error = Variable::GetValuesForVariableExpressionPath( variable_expr_path.drop_front(), scope, callback, baton, variable_list, valobj_list); if (error.Fail()) { error.SetErrorString("unknown error"); return error; } for (uint32_t i = 0; i < valobj_list.GetSize();) { Status tmp_error; ValueObjectSP valobj_sp( valobj_list.GetValueObjectAtIndex(i)->Dereference(tmp_error)); if (tmp_error.Fail()) { variable_list.RemoveVariableAtIndex(i); valobj_list.RemoveValueObjectAtIndex(i); } else { valobj_list.SetValueObjectAtIndex(i, valobj_sp); ++i; } } return error; case '&': { error = Variable::GetValuesForVariableExpressionPath( variable_expr_path.drop_front(), scope, callback, baton, variable_list, valobj_list); if (error.Success()) { for (uint32_t i = 0; i < valobj_list.GetSize();) { Status tmp_error; ValueObjectSP valobj_sp( valobj_list.GetValueObjectAtIndex(i)->AddressOf(tmp_error)); if (tmp_error.Fail()) { variable_list.RemoveVariableAtIndex(i); valobj_list.RemoveValueObjectAtIndex(i); } else { valobj_list.SetValueObjectAtIndex(i, valobj_sp); ++i; } } } else { error.SetErrorString("unknown error"); } return error; } break; default: { static RegularExpression g_regex( llvm::StringRef("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)")); RegularExpression::Match regex_match(1); std::string variable_name; variable_list.Clear(); if (!g_regex.Execute(variable_expr_path, ®ex_match)) { error.SetErrorStringWithFormat( "unable to extract a variable name from '%s'", variable_expr_path.str().c_str()); return error; } if (!regex_match.GetMatchAtIndex(variable_expr_path, 1, variable_name)) { error.SetErrorStringWithFormat( "unable to extract a variable name from '%s'", variable_expr_path.str().c_str()); return error; } if (!callback(baton, variable_name.c_str(), variable_list)) { error.SetErrorString("unknown error"); return error; } uint32_t i = 0; while (i < variable_list.GetSize()) { VariableSP var_sp(variable_list.GetVariableAtIndex(i)); ValueObjectSP valobj_sp; if (!var_sp) { variable_list.RemoveVariableAtIndex(i); continue; } ValueObjectSP variable_valobj_sp( ValueObjectVariable::Create(scope, var_sp)); if (!variable_valobj_sp) { variable_list.RemoveVariableAtIndex(i); continue; } llvm::StringRef variable_sub_expr_path = variable_expr_path.drop_front(variable_name.size()); if (!variable_sub_expr_path.empty()) { valobj_sp = variable_valobj_sp->GetValueForExpressionPath( variable_sub_expr_path); if (!valobj_sp) { error.SetErrorStringWithFormat( "invalid expression path '%s' for variable '%s'", variable_sub_expr_path.str().c_str(), var_sp->GetName().GetCString()); variable_list.RemoveVariableAtIndex(i); continue; } } else { // Just the name of a variable with no extras valobj_sp = variable_valobj_sp; } valobj_list.Append(valobj_sp); ++i; } if (variable_list.GetSize() > 0) { error.Clear(); return error; } } break; } error.SetErrorString("unknown error"); return error; } bool Variable::DumpLocationForAddress(Stream *s, const Address &address) { // Be sure to resolve the address to section offset prior to calling this // function. if (address.IsSectionOffset()) { SymbolContext sc; CalculateSymbolContext(&sc); if (sc.module_sp == address.GetModule()) { ABI *abi = nullptr; if (m_owner_scope) { ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule()); if (module_sp) abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture()).get(); } const addr_t file_addr = address.GetFileAddress(); if (sc.function) { if (sc.function->GetAddressRange().ContainsFileAddress(address)) { addr_t loclist_base_file_addr = sc.function->GetAddressRange().GetBaseAddress().GetFileAddress(); if (loclist_base_file_addr == LLDB_INVALID_ADDRESS) return false; return m_location.DumpLocationForAddress(s, eDescriptionLevelBrief, loclist_base_file_addr, file_addr, abi); } } return m_location.DumpLocationForAddress( s, eDescriptionLevelBrief, LLDB_INVALID_ADDRESS, file_addr, abi); } } return false; } static void PrivateAutoComplete( StackFrame *frame, llvm::StringRef partial_path, const llvm::Twine &prefix_path, // Anything that has been resolved already will be in here const CompilerType &compiler_type, StringList &matches, bool &word_complete); static void PrivateAutoCompleteMembers( StackFrame *frame, const std::string &partial_member_name, llvm::StringRef partial_path, const llvm::Twine &prefix_path, // Anything that has been resolved already will be in here const CompilerType &compiler_type, StringList &matches, bool &word_complete); static void PrivateAutoCompleteMembers( StackFrame *frame, const std::string &partial_member_name, llvm::StringRef partial_path, const llvm::Twine &prefix_path, // Anything that has been resolved already will be in here const CompilerType &compiler_type, StringList &matches, bool &word_complete) { // We are in a type parsing child members const uint32_t num_bases = compiler_type.GetNumDirectBaseClasses(); if (num_bases > 0) { for (uint32_t i = 0; i < num_bases; ++i) { CompilerType base_class_type = compiler_type.GetDirectBaseClassAtIndex(i, nullptr); PrivateAutoCompleteMembers( frame, partial_member_name, partial_path, prefix_path, base_class_type.GetCanonicalType(), matches, word_complete); } } const uint32_t num_vbases = compiler_type.GetNumVirtualBaseClasses(); if (num_vbases > 0) { for (uint32_t i = 0; i < num_vbases; ++i) { CompilerType vbase_class_type = compiler_type.GetVirtualBaseClassAtIndex(i, nullptr); PrivateAutoCompleteMembers( frame, partial_member_name, partial_path, prefix_path, vbase_class_type.GetCanonicalType(), matches, word_complete); } } // We are in a type parsing child members const uint32_t num_fields = compiler_type.GetNumFields(); if (num_fields > 0) { for (uint32_t i = 0; i < num_fields; ++i) { std::string member_name; CompilerType member_compiler_type = compiler_type.GetFieldAtIndex( i, member_name, nullptr, nullptr, nullptr); if (partial_member_name.empty() || member_name.find(partial_member_name) == 0) { if (member_name == partial_member_name) { PrivateAutoComplete( frame, partial_path, prefix_path + member_name, // Anything that has been resolved // already will be in here member_compiler_type.GetCanonicalType(), matches, word_complete); } else { matches.AppendString((prefix_path + member_name).str()); } } } } } static void PrivateAutoComplete( StackFrame *frame, llvm::StringRef partial_path, const llvm::Twine &prefix_path, // Anything that has been resolved already will be in here const CompilerType &compiler_type, StringList &matches, bool &word_complete) { // printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path = // '%s'\n", prefix_path.c_str(), partial_path.c_str()); std::string remaining_partial_path; const lldb::TypeClass type_class = compiler_type.GetTypeClass(); if (partial_path.empty()) { if (compiler_type.IsValid()) { switch (type_class) { default: case eTypeClassArray: case eTypeClassBlockPointer: case eTypeClassBuiltin: case eTypeClassComplexFloat: case eTypeClassComplexInteger: case eTypeClassEnumeration: case eTypeClassFunction: case eTypeClassMemberPointer: case eTypeClassReference: case eTypeClassTypedef: case eTypeClassVector: { matches.AppendString(prefix_path.str()); word_complete = matches.GetSize() == 1; } break; case eTypeClassClass: case eTypeClassStruct: case eTypeClassUnion: if (prefix_path.str().back() != '.') matches.AppendString((prefix_path + ".").str()); break; case eTypeClassObjCObject: case eTypeClassObjCInterface: break; case eTypeClassObjCObjectPointer: case eTypeClassPointer: { bool omit_empty_base_classes = true; if (compiler_type.GetNumChildren(omit_empty_base_classes) > 0) matches.AppendString((prefix_path + "->").str()); else { matches.AppendString(prefix_path.str()); word_complete = true; } } break; } } else { if (frame) { const bool get_file_globals = true; VariableList *variable_list = frame->GetVariableList(get_file_globals); if (variable_list) { const size_t num_variables = variable_list->GetSize(); for (size_t i = 0; i < num_variables; ++i) { Variable *variable = variable_list->GetVariableAtIndex(i).get(); matches.AppendString(variable->GetName().AsCString()); } } } } } else { const char ch = partial_path[0]; switch (ch) { case '*': if (prefix_path.str().empty()) { PrivateAutoComplete(frame, partial_path.substr(1), "*", compiler_type, matches, word_complete); } break; case '&': if (prefix_path.isTriviallyEmpty()) { PrivateAutoComplete(frame, partial_path.substr(1), std::string("&"), compiler_type, matches, word_complete); } break; case '-': - if (partial_path[1] == '>' && !prefix_path.str().empty()) { + if (partial_path.size() > 1 && partial_path[1] == '>' && + !prefix_path.str().empty()) { switch (type_class) { case lldb::eTypeClassPointer: { CompilerType pointee_type(compiler_type.GetPointeeType()); - if (partial_path[2]) { + if (partial_path.size() > 2 && partial_path[2]) { // If there is more after the "->", then search deeper PrivateAutoComplete( frame, partial_path.substr(2), prefix_path + "->", pointee_type.GetCanonicalType(), matches, word_complete); } else { // Nothing after the "->", so list all members PrivateAutoCompleteMembers( frame, std::string(), std::string(), prefix_path + "->", pointee_type.GetCanonicalType(), matches, word_complete); } } break; default: break; } } break; case '.': if (compiler_type.IsValid()) { switch (type_class) { case lldb::eTypeClassUnion: case lldb::eTypeClassStruct: case lldb::eTypeClassClass: - if (partial_path[1]) { + if (partial_path.size() > 1 && partial_path[1]) { // If there is more after the ".", then search deeper PrivateAutoComplete(frame, partial_path.substr(1), prefix_path + ".", compiler_type, matches, word_complete); } else { // Nothing after the ".", so list all members PrivateAutoCompleteMembers(frame, std::string(), partial_path, prefix_path + ".", compiler_type, matches, word_complete); } break; default: break; } } break; default: if (isalpha(ch) || ch == '_' || ch == '$') { const size_t partial_path_len = partial_path.size(); size_t pos = 1; while (pos < partial_path_len) { const char curr_ch = partial_path[pos]; if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$') { ++pos; continue; } break; } std::string token(partial_path, 0, pos); remaining_partial_path = partial_path.substr(pos); if (compiler_type.IsValid()) { PrivateAutoCompleteMembers(frame, token, remaining_partial_path, prefix_path, compiler_type, matches, word_complete); } else if (frame) { // We haven't found our variable yet const bool get_file_globals = true; VariableList *variable_list = frame->GetVariableList(get_file_globals); if (!variable_list) break; const size_t num_variables = variable_list->GetSize(); for (size_t i = 0; i < num_variables; ++i) { Variable *variable = variable_list->GetVariableAtIndex(i).get(); if (!variable) continue; const char *variable_name = variable->GetName().AsCString(); if (strstr(variable_name, token.c_str()) == variable_name) { if (strcmp(variable_name, token.c_str()) == 0) { Type *variable_type = variable->GetType(); if (variable_type) { CompilerType variable_compiler_type( variable_type->GetForwardCompilerType()); PrivateAutoComplete( frame, remaining_partial_path, prefix_path + token, // Anything that has been resolved // already will be in here variable_compiler_type.GetCanonicalType(), matches, word_complete); } else { matches.AppendString((prefix_path + variable_name).str()); } } else if (remaining_partial_path.empty()) { matches.AppendString((prefix_path + variable_name).str()); } } } } } break; } } } size_t Variable::AutoComplete(const ExecutionContext &exe_ctx, CompletionRequest &request) { CompilerType compiler_type; bool word_complete = false; + StringList matches; PrivateAutoComplete(exe_ctx.GetFramePtr(), request.GetCursorArgumentPrefix(), - "", compiler_type, request.GetMatches(), word_complete); + "", compiler_type, matches, word_complete); request.SetWordComplete(word_complete); + request.AddCompletions(matches); - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } Index: vendor/lldb/dist/source/Utility/ArchSpec.cpp =================================================================== --- vendor/lldb/dist/source/Utility/ArchSpec.cpp (revision 337146) +++ vendor/lldb/dist/source/Utility/ArchSpec.cpp (revision 337147) @@ -1,1498 +1,1500 @@ //===-- ArchSpec.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/NameMatches.h" #include "lldb/Utility/Stream.h" // for Stream #include "lldb/Utility/StringList.h" #include "lldb/lldb-defines.h" // for LLDB_INVALID_C... #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Twine.h" // for Twine #include "llvm/BinaryFormat/COFF.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/BinaryFormat/MachO.h" // for CPUType::CPU_T... #include "llvm/Support/Compiler.h" // for LLVM_FALLTHROUGH #include "llvm/Support/Host.h" using namespace lldb; using namespace lldb_private; static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_inverse, bool enforce_exact_match); namespace lldb_private { struct CoreDefinition { ByteOrder default_byte_order; uint32_t addr_byte_size; uint32_t min_opcode_byte_size; uint32_t max_opcode_byte_size; llvm::Triple::ArchType machine; ArchSpec::Core core; const char *const name; }; } // namespace lldb_private // This core information can be looked using the ArchSpec::Core as the index static const CoreDefinition g_core_definitions[] = { {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_generic, "arm"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv4, "armv4"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv4t, "armv4t"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv5, "armv5"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv5e, "armv5e"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv5t, "armv5t"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv6, "armv6"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv6m, "armv6m"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7, "armv7"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7f, "armv7f"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7s, "armv7s"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7k, "armv7k"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7m, "armv7m"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_armv7em, "armv7em"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::arm, ArchSpec::eCore_arm_xscale, "xscale"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumb, "thumb"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv4t, "thumbv4t"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv5, "thumbv5"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv5e, "thumbv5e"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv6, "thumbv6"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv6m, "thumbv6m"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7, "thumbv7"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7f, "thumbv7f"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7s, "thumbv7s"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7k, "thumbv7k"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7m, "thumbv7m"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb, ArchSpec::eCore_thumbv7em, "thumbv7em"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, ArchSpec::eCore_arm_arm64, "arm64"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, ArchSpec::eCore_arm_armv8, "armv8"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, ArchSpec::eCore_arm_aarch64, "aarch64"}, // mips32, mips32r2, mips32r3, mips32r5, mips32r6 {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32, "mips"}, {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r2, "mipsr2"}, {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r3, "mipsr3"}, {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r5, "mipsr5"}, {eByteOrderBig, 4, 2, 4, llvm::Triple::mips, ArchSpec::eCore_mips32r6, "mipsr6"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32el, "mipsel"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r2el, "mipsr2el"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r3el, "mipsr3el"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r5el, "mipsr5el"}, {eByteOrderLittle, 4, 2, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r6el, "mipsr6el"}, // mips64, mips64r2, mips64r3, mips64r5, mips64r6 {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64, "mips64"}, {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r2, "mips64r2"}, {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r3, "mips64r3"}, {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r5, "mips64r5"}, {eByteOrderBig, 8, 2, 4, llvm::Triple::mips64, ArchSpec::eCore_mips64r6, "mips64r6"}, {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64el, "mips64el"}, {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r2el, "mips64r2el"}, {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r3el, "mips64r3el"}, {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r5el, "mips64r5el"}, {eByteOrderLittle, 8, 2, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r6el, "mips64r6el"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_generic, "powerpc"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc601, "ppc601"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc602, "ppc602"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc603, "ppc603"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc603e, "ppc603e"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc603ev, "ppc603ev"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc604, "ppc604"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc604e, "ppc604e"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc620, "ppc620"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc750, "ppc750"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc7400, "ppc7400"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc7450, "ppc7450"}, {eByteOrderBig, 4, 4, 4, llvm::Triple::ppc, ArchSpec::eCore_ppc_ppc970, "ppc970"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::ppc64le, ArchSpec::eCore_ppc64le_generic, "powerpc64le"}, {eByteOrderBig, 8, 4, 4, llvm::Triple::ppc64, ArchSpec::eCore_ppc64_generic, "powerpc64"}, {eByteOrderBig, 8, 4, 4, llvm::Triple::ppc64, ArchSpec::eCore_ppc64_ppc970_64, "ppc970-64"}, {eByteOrderBig, 8, 2, 6, llvm::Triple::systemz, ArchSpec::eCore_s390x_generic, "s390x"}, {eByteOrderLittle, 4, 4, 4, llvm::Triple::sparc, ArchSpec::eCore_sparc_generic, "sparc"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::sparcv9, ArchSpec::eCore_sparc9_generic, "sparcv9"}, {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i386, "i386"}, {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i486, "i486"}, {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i486sx, "i486sx"}, {eByteOrderLittle, 4, 1, 15, llvm::Triple::x86, ArchSpec::eCore_x86_32_i686, "i686"}, {eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64, ArchSpec::eCore_x86_64_x86_64, "x86_64"}, {eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64, ArchSpec::eCore_x86_64_x86_64h, "x86_64h"}, {eByteOrderLittle, 4, 4, 4, llvm::Triple::hexagon, ArchSpec::eCore_hexagon_generic, "hexagon"}, {eByteOrderLittle, 4, 4, 4, llvm::Triple::hexagon, ArchSpec::eCore_hexagon_hexagonv4, "hexagonv4"}, {eByteOrderLittle, 4, 4, 4, llvm::Triple::hexagon, ArchSpec::eCore_hexagon_hexagonv5, "hexagonv5"}, {eByteOrderLittle, 4, 4, 4, llvm::Triple::UnknownArch, ArchSpec::eCore_uknownMach32, "unknown-mach-32"}, {eByteOrderLittle, 8, 4, 4, llvm::Triple::UnknownArch, ArchSpec::eCore_uknownMach64, "unknown-mach-64"}, {eByteOrderBig, 4, 1, 1, llvm::Triple::kalimba, ArchSpec::eCore_kalimba3, "kalimba3"}, {eByteOrderLittle, 4, 1, 1, llvm::Triple::kalimba, ArchSpec::eCore_kalimba4, "kalimba4"}, {eByteOrderLittle, 4, 1, 1, llvm::Triple::kalimba, ArchSpec::eCore_kalimba5, "kalimba5"}}; // Ensure that we have an entry in the g_core_definitions for each core. If you // comment out an entry above, you will need to comment out the corresponding // ArchSpec::Core enumeration. static_assert(sizeof(g_core_definitions) / sizeof(CoreDefinition) == ArchSpec::kNumCores, "make sure we have one core definition for each core"); struct ArchDefinitionEntry { ArchSpec::Core core; uint32_t cpu; uint32_t sub; uint32_t cpu_mask; uint32_t sub_mask; }; struct ArchDefinition { ArchitectureType type; size_t num_entries; const ArchDefinitionEntry *entries; const char *name; }; void ArchSpec::ListSupportedArchNames(StringList &list) { for (uint32_t i = 0; i < llvm::array_lengthof(g_core_definitions); ++i) list.AppendString(g_core_definitions[i].name); } size_t ArchSpec::AutoComplete(CompletionRequest &request) { if (!request.GetCursorArgumentPrefix().empty()) { for (uint32_t i = 0; i < llvm::array_lengthof(g_core_definitions); ++i) { if (NameMatches(g_core_definitions[i].name, NameMatch::StartsWith, request.GetCursorArgumentPrefix())) - request.GetMatches().AppendString(g_core_definitions[i].name); + request.AddCompletion(g_core_definitions[i].name); } } else { - ListSupportedArchNames(request.GetMatches()); + StringList matches; + ListSupportedArchNames(matches); + request.AddCompletions(matches); } - return request.GetMatches().GetSize(); + return request.GetNumberOfMatches(); } #define CPU_ANY (UINT32_MAX) //===----------------------------------------------------------------------===// // A table that gets searched linearly for matches. This table is used to // convert cpu type and subtypes to architecture names, and to convert // architecture names to cpu types and subtypes. The ordering is important and // allows the precedence to be set when the table is built. #define SUBTYPE_MASK 0x00FFFFFFu static const ArchDefinitionEntry g_macho_arch_entries[] = { {ArchSpec::eCore_arm_generic, llvm::MachO::CPU_TYPE_ARM, CPU_ANY, UINT32_MAX, UINT32_MAX}, {ArchSpec::eCore_arm_generic, llvm::MachO::CPU_TYPE_ARM, 0, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv4, llvm::MachO::CPU_TYPE_ARM, 5, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv4t, llvm::MachO::CPU_TYPE_ARM, 5, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv6, llvm::MachO::CPU_TYPE_ARM, 6, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv6m, llvm::MachO::CPU_TYPE_ARM, 14, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv5, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv5e, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv5t, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_xscale, llvm::MachO::CPU_TYPE_ARM, 8, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv7, llvm::MachO::CPU_TYPE_ARM, 9, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv7f, llvm::MachO::CPU_TYPE_ARM, 10, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv7s, llvm::MachO::CPU_TYPE_ARM, 11, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv7k, llvm::MachO::CPU_TYPE_ARM, 12, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv7m, llvm::MachO::CPU_TYPE_ARM, 15, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_armv7em, llvm::MachO::CPU_TYPE_ARM, 16, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, 1, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, 0, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, 13, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_arm_arm64, llvm::MachO::CPU_TYPE_ARM64, CPU_ANY, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumb, llvm::MachO::CPU_TYPE_ARM, 0, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv4t, llvm::MachO::CPU_TYPE_ARM, 5, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv5, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv5e, llvm::MachO::CPU_TYPE_ARM, 7, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv6, llvm::MachO::CPU_TYPE_ARM, 6, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv6m, llvm::MachO::CPU_TYPE_ARM, 14, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv7, llvm::MachO::CPU_TYPE_ARM, 9, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv7f, llvm::MachO::CPU_TYPE_ARM, 10, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv7s, llvm::MachO::CPU_TYPE_ARM, 11, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv7k, llvm::MachO::CPU_TYPE_ARM, 12, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv7m, llvm::MachO::CPU_TYPE_ARM, 15, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_thumbv7em, llvm::MachO::CPU_TYPE_ARM, 16, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_generic, llvm::MachO::CPU_TYPE_POWERPC, CPU_ANY, UINT32_MAX, UINT32_MAX}, {ArchSpec::eCore_ppc_generic, llvm::MachO::CPU_TYPE_POWERPC, 0, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc601, llvm::MachO::CPU_TYPE_POWERPC, 1, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc602, llvm::MachO::CPU_TYPE_POWERPC, 2, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc603, llvm::MachO::CPU_TYPE_POWERPC, 3, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc603e, llvm::MachO::CPU_TYPE_POWERPC, 4, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc603ev, llvm::MachO::CPU_TYPE_POWERPC, 5, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc604, llvm::MachO::CPU_TYPE_POWERPC, 6, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc604e, llvm::MachO::CPU_TYPE_POWERPC, 7, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc620, llvm::MachO::CPU_TYPE_POWERPC, 8, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc750, llvm::MachO::CPU_TYPE_POWERPC, 9, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc7400, llvm::MachO::CPU_TYPE_POWERPC, 10, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc7450, llvm::MachO::CPU_TYPE_POWERPC, 11, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc_ppc970, llvm::MachO::CPU_TYPE_POWERPC, 100, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc64_generic, llvm::MachO::CPU_TYPE_POWERPC64, 0, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc64le_generic, llvm::MachO::CPU_TYPE_POWERPC64, CPU_ANY, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_ppc64_ppc970_64, llvm::MachO::CPU_TYPE_POWERPC64, 100, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_32_i386, llvm::MachO::CPU_TYPE_I386, 3, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_32_i486, llvm::MachO::CPU_TYPE_I386, 4, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_32_i486sx, llvm::MachO::CPU_TYPE_I386, 0x84, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_32_i386, llvm::MachO::CPU_TYPE_I386, CPU_ANY, UINT32_MAX, UINT32_MAX}, {ArchSpec::eCore_x86_64_x86_64, llvm::MachO::CPU_TYPE_X86_64, 3, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_64_x86_64, llvm::MachO::CPU_TYPE_X86_64, 4, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_64_x86_64h, llvm::MachO::CPU_TYPE_X86_64, 8, UINT32_MAX, SUBTYPE_MASK}, {ArchSpec::eCore_x86_64_x86_64, llvm::MachO::CPU_TYPE_X86_64, CPU_ANY, UINT32_MAX, UINT32_MAX}, // Catch any unknown mach architectures so we can always use the object and // symbol mach-o files {ArchSpec::eCore_uknownMach32, 0, 0, 0xFF000000u, 0x00000000u}, {ArchSpec::eCore_uknownMach64, llvm::MachO::CPU_ARCH_ABI64, 0, 0xFF000000u, 0x00000000u}}; static const ArchDefinition g_macho_arch_def = { eArchTypeMachO, llvm::array_lengthof(g_macho_arch_entries), g_macho_arch_entries, "mach-o"}; //===----------------------------------------------------------------------===// // A table that gets searched linearly for matches. This table is used to // convert cpu type and subtypes to architecture names, and to convert // architecture names to cpu types and subtypes. The ordering is important and // allows the precedence to be set when the table is built. static const ArchDefinitionEntry g_elf_arch_entries[] = { {ArchSpec::eCore_sparc_generic, llvm::ELF::EM_SPARC, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // Sparc {ArchSpec::eCore_x86_32_i386, llvm::ELF::EM_386, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel 80386 {ArchSpec::eCore_x86_32_i486, llvm::ELF::EM_IAMCU, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel MCU // FIXME: is this correct? {ArchSpec::eCore_ppc_generic, llvm::ELF::EM_PPC, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC {ArchSpec::eCore_ppc64le_generic, llvm::ELF::EM_PPC64, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC64le {ArchSpec::eCore_ppc64_generic, llvm::ELF::EM_PPC64, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC64 {ArchSpec::eCore_arm_generic, llvm::ELF::EM_ARM, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM {ArchSpec::eCore_arm_aarch64, llvm::ELF::EM_AARCH64, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM64 {ArchSpec::eCore_s390x_generic, llvm::ELF::EM_S390, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // SystemZ {ArchSpec::eCore_sparc9_generic, llvm::ELF::EM_SPARCV9, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // SPARC V9 {ArchSpec::eCore_x86_64_x86_64, llvm::ELF::EM_X86_64, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // AMD64 {ArchSpec::eCore_mips32, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32 {ArchSpec::eCore_mips32r2, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r2, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r2 {ArchSpec::eCore_mips32r6, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r6, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r6 {ArchSpec::eCore_mips32el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32el {ArchSpec::eCore_mips32r2el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r2el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r2el {ArchSpec::eCore_mips32r6el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r6el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r6el {ArchSpec::eCore_mips64, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64 {ArchSpec::eCore_mips64r2, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r2, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r2 {ArchSpec::eCore_mips64r6, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r6, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r6 {ArchSpec::eCore_mips64el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64el {ArchSpec::eCore_mips64r2el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r2el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r2el {ArchSpec::eCore_mips64r6el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r6el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r6el {ArchSpec::eCore_hexagon_generic, llvm::ELF::EM_HEXAGON, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // HEXAGON {ArchSpec::eCore_kalimba3, llvm::ELF::EM_CSR_KALIMBA, llvm::Triple::KalimbaSubArch_v3, 0xFFFFFFFFu, 0xFFFFFFFFu}, // KALIMBA {ArchSpec::eCore_kalimba4, llvm::ELF::EM_CSR_KALIMBA, llvm::Triple::KalimbaSubArch_v4, 0xFFFFFFFFu, 0xFFFFFFFFu}, // KALIMBA {ArchSpec::eCore_kalimba5, llvm::ELF::EM_CSR_KALIMBA, llvm::Triple::KalimbaSubArch_v5, 0xFFFFFFFFu, 0xFFFFFFFFu} // KALIMBA }; static const ArchDefinition g_elf_arch_def = { eArchTypeELF, llvm::array_lengthof(g_elf_arch_entries), g_elf_arch_entries, "elf", }; static const ArchDefinitionEntry g_coff_arch_entries[] = { {ArchSpec::eCore_x86_32_i386, llvm::COFF::IMAGE_FILE_MACHINE_I386, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel 80x86 {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPC, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC (with FPU) {ArchSpec::eCore_arm_generic, llvm::COFF::IMAGE_FILE_MACHINE_ARM, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM {ArchSpec::eCore_arm_armv7, llvm::COFF::IMAGE_FILE_MACHINE_ARMNT, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARMv7 {ArchSpec::eCore_thumb, llvm::COFF::IMAGE_FILE_MACHINE_THUMB, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARMv7 {ArchSpec::eCore_x86_64_x86_64, llvm::COFF::IMAGE_FILE_MACHINE_AMD64, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu} // AMD64 }; static const ArchDefinition g_coff_arch_def = { eArchTypeCOFF, llvm::array_lengthof(g_coff_arch_entries), g_coff_arch_entries, "pe-coff", }; //===----------------------------------------------------------------------===// // Table of all ArchDefinitions static const ArchDefinition *g_arch_definitions[] = { &g_macho_arch_def, &g_elf_arch_def, &g_coff_arch_def}; static const size_t k_num_arch_definitions = llvm::array_lengthof(g_arch_definitions); //===----------------------------------------------------------------------===// // Static helper functions. // Get the architecture definition for a given object type. static const ArchDefinition *FindArchDefinition(ArchitectureType arch_type) { for (unsigned int i = 0; i < k_num_arch_definitions; ++i) { const ArchDefinition *def = g_arch_definitions[i]; if (def->type == arch_type) return def; } return nullptr; } // Get an architecture definition by name. static const CoreDefinition *FindCoreDefinition(llvm::StringRef name) { for (unsigned int i = 0; i < llvm::array_lengthof(g_core_definitions); ++i) { if (name.equals_lower(g_core_definitions[i].name)) return &g_core_definitions[i]; } return nullptr; } static inline const CoreDefinition *FindCoreDefinition(ArchSpec::Core core) { if (core < llvm::array_lengthof(g_core_definitions)) return &g_core_definitions[core]; return nullptr; } // Get a definition entry by cpu type and subtype. static const ArchDefinitionEntry * FindArchDefinitionEntry(const ArchDefinition *def, uint32_t cpu, uint32_t sub) { if (def == nullptr) return nullptr; const ArchDefinitionEntry *entries = def->entries; for (size_t i = 0; i < def->num_entries; ++i) { if (entries[i].cpu == (cpu & entries[i].cpu_mask)) if (entries[i].sub == (sub & entries[i].sub_mask)) return &entries[i]; } return nullptr; } static const ArchDefinitionEntry * FindArchDefinitionEntry(const ArchDefinition *def, ArchSpec::Core core) { if (def == nullptr) return nullptr; const ArchDefinitionEntry *entries = def->entries; for (size_t i = 0; i < def->num_entries; ++i) { if (entries[i].core == core) return &entries[i]; } return nullptr; } //===----------------------------------------------------------------------===// // Constructors and destructors. ArchSpec::ArchSpec() {} ArchSpec::ArchSpec(const char *triple_cstr) { if (triple_cstr) SetTriple(triple_cstr); } ArchSpec::ArchSpec(llvm::StringRef triple_str) { SetTriple(triple_str); } ArchSpec::ArchSpec(const llvm::Triple &triple) { SetTriple(triple); } ArchSpec::ArchSpec(ArchitectureType arch_type, uint32_t cpu, uint32_t subtype) { SetArchitecture(arch_type, cpu, subtype); } ArchSpec::~ArchSpec() = default; //===----------------------------------------------------------------------===// // Assignment and initialization. const ArchSpec &ArchSpec::operator=(const ArchSpec &rhs) { if (this != &rhs) { m_triple = rhs.m_triple; m_core = rhs.m_core; m_byte_order = rhs.m_byte_order; m_distribution_id = rhs.m_distribution_id; m_flags = rhs.m_flags; } return *this; } void ArchSpec::Clear() { m_triple = llvm::Triple(); m_core = kCore_invalid; m_byte_order = eByteOrderInvalid; m_distribution_id.Clear(); m_flags = 0; } //===----------------------------------------------------------------------===// // Predicates. const char *ArchSpec::GetArchitectureName() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) return core_def->name; return "unknown"; } bool ArchSpec::IsMIPS() const { const llvm::Triple::ArchType machine = GetMachine(); if (machine == llvm::Triple::mips || machine == llvm::Triple::mipsel || machine == llvm::Triple::mips64 || machine == llvm::Triple::mips64el) return true; return false; } std::string ArchSpec::GetTargetABI() const { std::string abi; if (IsMIPS()) { switch (GetFlags() & ArchSpec::eMIPSABI_mask) { case ArchSpec::eMIPSABI_N64: abi = "n64"; return abi; case ArchSpec::eMIPSABI_N32: abi = "n32"; return abi; case ArchSpec::eMIPSABI_O32: abi = "o32"; return abi; default: return abi; } } return abi; } void ArchSpec::SetFlags(std::string elf_abi) { uint32_t flag = GetFlags(); if (IsMIPS()) { if (elf_abi == "n64") flag |= ArchSpec::eMIPSABI_N64; else if (elf_abi == "n32") flag |= ArchSpec::eMIPSABI_N32; else if (elf_abi == "o32") flag |= ArchSpec::eMIPSABI_O32; } SetFlags(flag); } std::string ArchSpec::GetClangTargetCPU() const { std::string cpu; const llvm::Triple::ArchType machine = GetMachine(); if (machine == llvm::Triple::mips || machine == llvm::Triple::mipsel || machine == llvm::Triple::mips64 || machine == llvm::Triple::mips64el) { switch (m_core) { case ArchSpec::eCore_mips32: case ArchSpec::eCore_mips32el: cpu = "mips32"; break; case ArchSpec::eCore_mips32r2: case ArchSpec::eCore_mips32r2el: cpu = "mips32r2"; break; case ArchSpec::eCore_mips32r3: case ArchSpec::eCore_mips32r3el: cpu = "mips32r3"; break; case ArchSpec::eCore_mips32r5: case ArchSpec::eCore_mips32r5el: cpu = "mips32r5"; break; case ArchSpec::eCore_mips32r6: case ArchSpec::eCore_mips32r6el: cpu = "mips32r6"; break; case ArchSpec::eCore_mips64: case ArchSpec::eCore_mips64el: cpu = "mips64"; break; case ArchSpec::eCore_mips64r2: case ArchSpec::eCore_mips64r2el: cpu = "mips64r2"; break; case ArchSpec::eCore_mips64r3: case ArchSpec::eCore_mips64r3el: cpu = "mips64r3"; break; case ArchSpec::eCore_mips64r5: case ArchSpec::eCore_mips64r5el: cpu = "mips64r5"; break; case ArchSpec::eCore_mips64r6: case ArchSpec::eCore_mips64r6el: cpu = "mips64r6"; break; default: break; } } return cpu; } uint32_t ArchSpec::GetMachOCPUType() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) { const ArchDefinitionEntry *arch_def = FindArchDefinitionEntry(&g_macho_arch_def, core_def->core); if (arch_def) { return arch_def->cpu; } } return LLDB_INVALID_CPUTYPE; } uint32_t ArchSpec::GetMachOCPUSubType() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) { const ArchDefinitionEntry *arch_def = FindArchDefinitionEntry(&g_macho_arch_def, core_def->core); if (arch_def) { return arch_def->sub; } } return LLDB_INVALID_CPUTYPE; } uint32_t ArchSpec::GetDataByteSize() const { switch (m_core) { case eCore_kalimba3: return 4; case eCore_kalimba4: return 1; case eCore_kalimba5: return 4; default: return 1; } return 1; } uint32_t ArchSpec::GetCodeByteSize() const { switch (m_core) { case eCore_kalimba3: return 4; case eCore_kalimba4: return 1; case eCore_kalimba5: return 1; default: return 1; } return 1; } llvm::Triple::ArchType ArchSpec::GetMachine() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) return core_def->machine; return llvm::Triple::UnknownArch; } const ConstString &ArchSpec::GetDistributionId() const { return m_distribution_id; } void ArchSpec::SetDistributionId(const char *distribution_id) { m_distribution_id.SetCString(distribution_id); } uint32_t ArchSpec::GetAddressByteSize() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) { if (core_def->machine == llvm::Triple::mips64 || core_def->machine == llvm::Triple::mips64el) { // For N32/O32 applications Address size is 4 bytes. if (m_flags & (eMIPSABI_N32 | eMIPSABI_O32)) return 4; } return core_def->addr_byte_size; } return 0; } ByteOrder ArchSpec::GetDefaultEndian() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) return core_def->default_byte_order; return eByteOrderInvalid; } bool ArchSpec::CharIsSignedByDefault() const { switch (m_triple.getArch()) { default: return true; case llvm::Triple::aarch64: case llvm::Triple::aarch64_be: case llvm::Triple::arm: case llvm::Triple::armeb: case llvm::Triple::thumb: case llvm::Triple::thumbeb: return m_triple.isOSDarwin() || m_triple.isOSWindows(); case llvm::Triple::ppc: case llvm::Triple::ppc64: return m_triple.isOSDarwin(); case llvm::Triple::ppc64le: case llvm::Triple::systemz: case llvm::Triple::xcore: return false; } } lldb::ByteOrder ArchSpec::GetByteOrder() const { if (m_byte_order == eByteOrderInvalid) return GetDefaultEndian(); return m_byte_order; } //===----------------------------------------------------------------------===// // Mutators. bool ArchSpec::SetTriple(const llvm::Triple &triple) { m_triple = triple; UpdateCore(); return IsValid(); } bool lldb_private::ParseMachCPUDashSubtypeTriple(llvm::StringRef triple_str, ArchSpec &arch) { // Accept "12-10" or "12.10" as cpu type/subtype if (triple_str.empty()) return false; size_t pos = triple_str.find_first_of("-."); if (pos == llvm::StringRef::npos) return false; llvm::StringRef cpu_str = triple_str.substr(0, pos); llvm::StringRef remainder = triple_str.substr(pos + 1); if (cpu_str.empty() || remainder.empty()) return false; llvm::StringRef sub_str; llvm::StringRef vendor; llvm::StringRef os; std::tie(sub_str, remainder) = remainder.split('-'); std::tie(vendor, os) = remainder.split('-'); uint32_t cpu = 0; uint32_t sub = 0; if (cpu_str.getAsInteger(10, cpu) || sub_str.getAsInteger(10, sub)) return false; if (!arch.SetArchitecture(eArchTypeMachO, cpu, sub)) return false; if (!vendor.empty() && !os.empty()) { arch.GetTriple().setVendorName(vendor); arch.GetTriple().setOSName(os); } return true; } bool ArchSpec::SetTriple(llvm::StringRef triple) { if (triple.empty()) { Clear(); return false; } if (ParseMachCPUDashSubtypeTriple(triple, *this)) return true; SetTriple(llvm::Triple(llvm::Triple::normalize(triple))); return IsValid(); } bool ArchSpec::ContainsOnlyArch(const llvm::Triple &normalized_triple) { return !normalized_triple.getArchName().empty() && normalized_triple.getOSName().empty() && normalized_triple.getVendorName().empty() && normalized_triple.getEnvironmentName().empty(); } void ArchSpec::MergeFrom(const ArchSpec &other) { if (TripleVendorIsUnspecifiedUnknown() && !other.TripleVendorIsUnspecifiedUnknown()) GetTriple().setVendor(other.GetTriple().getVendor()); if (TripleOSIsUnspecifiedUnknown() && !other.TripleOSIsUnspecifiedUnknown()) GetTriple().setOS(other.GetTriple().getOS()); if (GetTriple().getArch() == llvm::Triple::UnknownArch) { GetTriple().setArch(other.GetTriple().getArch()); // MachO unknown64 isn't really invalid as the debugger can still obtain // information from the binary, e.g. line tables. As such, we don't update // the core here. if (other.GetCore() != eCore_uknownMach64) UpdateCore(); } if (GetTriple().getEnvironment() == llvm::Triple::UnknownEnvironment && !TripleVendorWasSpecified()) { if (other.TripleVendorWasSpecified()) GetTriple().setEnvironment(other.GetTriple().getEnvironment()); } // If this and other are both arm ArchSpecs and this ArchSpec is a generic // "some kind of arm" spec but the other ArchSpec is a specific arm core, // adopt the specific arm core. if (GetTriple().getArch() == llvm::Triple::arm && other.GetTriple().getArch() == llvm::Triple::arm && IsCompatibleMatch(other) && GetCore() == ArchSpec::eCore_arm_generic && other.GetCore() != ArchSpec::eCore_arm_generic) { m_core = other.GetCore(); CoreUpdated(true); } if (GetFlags() == 0) { SetFlags(other.GetFlags()); } } bool ArchSpec::SetArchitecture(ArchitectureType arch_type, uint32_t cpu, uint32_t sub, uint32_t os) { m_core = kCore_invalid; bool update_triple = true; const ArchDefinition *arch_def = FindArchDefinition(arch_type); if (arch_def) { const ArchDefinitionEntry *arch_def_entry = FindArchDefinitionEntry(arch_def, cpu, sub); if (arch_def_entry) { const CoreDefinition *core_def = FindCoreDefinition(arch_def_entry->core); if (core_def) { m_core = core_def->core; update_triple = false; // Always use the architecture name because it might be more // descriptive than the architecture enum ("armv7" -> // llvm::Triple::arm). m_triple.setArchName(llvm::StringRef(core_def->name)); if (arch_type == eArchTypeMachO) { m_triple.setVendor(llvm::Triple::Apple); // Don't set the OS. It could be simulator, macosx, ios, watchos, // tvos. We could get close with the cpu type - but we can't get it // right all of the time. Better to leave this unset so other // sections of code will set it when they have more information. // NB: don't call m_triple.setOS (llvm::Triple::UnknownOS). That sets // the OSName to // "unknown" and the ArchSpec::TripleVendorWasSpecified() method says // that any OSName setting means it was specified. } else if (arch_type == eArchTypeELF) { switch (os) { case llvm::ELF::ELFOSABI_AIX: m_triple.setOS(llvm::Triple::OSType::AIX); break; case llvm::ELF::ELFOSABI_FREEBSD: m_triple.setOS(llvm::Triple::OSType::FreeBSD); break; case llvm::ELF::ELFOSABI_GNU: m_triple.setOS(llvm::Triple::OSType::Linux); break; case llvm::ELF::ELFOSABI_NETBSD: m_triple.setOS(llvm::Triple::OSType::NetBSD); break; case llvm::ELF::ELFOSABI_OPENBSD: m_triple.setOS(llvm::Triple::OSType::OpenBSD); break; case llvm::ELF::ELFOSABI_SOLARIS: m_triple.setOS(llvm::Triple::OSType::Solaris); break; } } else if (arch_type == eArchTypeCOFF && os == llvm::Triple::Win32) { m_triple.setVendor(llvm::Triple::PC); m_triple.setOS(llvm::Triple::Win32); } else { m_triple.setVendor(llvm::Triple::UnknownVendor); m_triple.setOS(llvm::Triple::UnknownOS); } // Fall back onto setting the machine type if the arch by name // failed... if (m_triple.getArch() == llvm::Triple::UnknownArch) m_triple.setArch(core_def->machine); } } else { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET | LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_PLATFORM)); if (log) log->Printf("Unable to find a core definition for cpu 0x%" PRIx32 " sub %" PRId32, cpu, sub); } } CoreUpdated(update_triple); return IsValid(); } uint32_t ArchSpec::GetMinimumOpcodeByteSize() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) return core_def->min_opcode_byte_size; return 0; } uint32_t ArchSpec::GetMaximumOpcodeByteSize() const { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) return core_def->max_opcode_byte_size; return 0; } bool ArchSpec::IsExactMatch(const ArchSpec &rhs) const { return IsEqualTo(rhs, true); } bool ArchSpec::IsCompatibleMatch(const ArchSpec &rhs) const { return IsEqualTo(rhs, false); } static bool isCompatibleEnvironment(llvm::Triple::EnvironmentType lhs, llvm::Triple::EnvironmentType rhs) { if (lhs == rhs) return true; // If any of the environment is unknown then they are compatible if (lhs == llvm::Triple::UnknownEnvironment || rhs == llvm::Triple::UnknownEnvironment) return true; // If one of the environment is Android and the other one is EABI then they // are considered to be compatible. This is required as a workaround for // shared libraries compiled for Android without the NOTE section indicating // that they are using the Android ABI. if ((lhs == llvm::Triple::Android && rhs == llvm::Triple::EABI) || (rhs == llvm::Triple::Android && lhs == llvm::Triple::EABI) || (lhs == llvm::Triple::GNUEABI && rhs == llvm::Triple::EABI) || (rhs == llvm::Triple::GNUEABI && lhs == llvm::Triple::EABI) || (lhs == llvm::Triple::GNUEABIHF && rhs == llvm::Triple::EABIHF) || (rhs == llvm::Triple::GNUEABIHF && lhs == llvm::Triple::EABIHF)) return true; return false; } bool ArchSpec::IsEqualTo(const ArchSpec &rhs, bool exact_match) const { // explicitly ignoring m_distribution_id in this method. if (GetByteOrder() != rhs.GetByteOrder()) return false; const ArchSpec::Core lhs_core = GetCore(); const ArchSpec::Core rhs_core = rhs.GetCore(); const bool core_match = cores_match(lhs_core, rhs_core, true, exact_match); if (core_match) { const llvm::Triple &lhs_triple = GetTriple(); const llvm::Triple &rhs_triple = rhs.GetTriple(); const llvm::Triple::VendorType lhs_triple_vendor = lhs_triple.getVendor(); const llvm::Triple::VendorType rhs_triple_vendor = rhs_triple.getVendor(); if (lhs_triple_vendor != rhs_triple_vendor) { const bool rhs_vendor_specified = rhs.TripleVendorWasSpecified(); const bool lhs_vendor_specified = TripleVendorWasSpecified(); // Both architectures had the vendor specified, so if they aren't equal // then we return false if (rhs_vendor_specified && lhs_vendor_specified) return false; // Only fail if both vendor types are not unknown if (lhs_triple_vendor != llvm::Triple::UnknownVendor && rhs_triple_vendor != llvm::Triple::UnknownVendor) return false; } const llvm::Triple::OSType lhs_triple_os = lhs_triple.getOS(); const llvm::Triple::OSType rhs_triple_os = rhs_triple.getOS(); if (lhs_triple_os != rhs_triple_os) { const bool rhs_os_specified = rhs.TripleOSWasSpecified(); const bool lhs_os_specified = TripleOSWasSpecified(); // Both architectures had the OS specified, so if they aren't equal then // we return false if (rhs_os_specified && lhs_os_specified) return false; // Only fail if both os types are not unknown if (lhs_triple_os != llvm::Triple::UnknownOS && rhs_triple_os != llvm::Triple::UnknownOS) return false; } const llvm::Triple::EnvironmentType lhs_triple_env = lhs_triple.getEnvironment(); const llvm::Triple::EnvironmentType rhs_triple_env = rhs_triple.getEnvironment(); if (!isCompatibleEnvironment(lhs_triple_env, rhs_triple_env)) return false; return true; } return false; } void ArchSpec::UpdateCore() { llvm::StringRef arch_name(m_triple.getArchName()); const CoreDefinition *core_def = FindCoreDefinition(arch_name); if (core_def) { m_core = core_def->core; // Set the byte order to the default byte order for an architecture. This // can be modified if needed for cases when cores handle both big and // little endian m_byte_order = core_def->default_byte_order; } else { Clear(); } } //===----------------------------------------------------------------------===// // Helper methods. void ArchSpec::CoreUpdated(bool update_triple) { const CoreDefinition *core_def = FindCoreDefinition(m_core); if (core_def) { if (update_triple) m_triple = llvm::Triple(core_def->name, "unknown", "unknown"); m_byte_order = core_def->default_byte_order; } else { if (update_triple) m_triple = llvm::Triple(); m_byte_order = eByteOrderInvalid; } } //===----------------------------------------------------------------------===// // Operators. static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_inverse, bool enforce_exact_match) { if (core1 == core2) return true; switch (core1) { case ArchSpec::kCore_any: return true; case ArchSpec::eCore_arm_generic: if (enforce_exact_match) break; LLVM_FALLTHROUGH; case ArchSpec::kCore_arm_any: if (core2 >= ArchSpec::kCore_arm_first && core2 <= ArchSpec::kCore_arm_last) return true; if (core2 >= ArchSpec::kCore_thumb_first && core2 <= ArchSpec::kCore_thumb_last) return true; if (core2 == ArchSpec::kCore_arm_any) return true; break; case ArchSpec::kCore_x86_32_any: if ((core2 >= ArchSpec::kCore_x86_32_first && core2 <= ArchSpec::kCore_x86_32_last) || (core2 == ArchSpec::kCore_x86_32_any)) return true; break; case ArchSpec::kCore_x86_64_any: if ((core2 >= ArchSpec::kCore_x86_64_first && core2 <= ArchSpec::kCore_x86_64_last) || (core2 == ArchSpec::kCore_x86_64_any)) return true; break; case ArchSpec::kCore_ppc_any: if ((core2 >= ArchSpec::kCore_ppc_first && core2 <= ArchSpec::kCore_ppc_last) || (core2 == ArchSpec::kCore_ppc_any)) return true; break; case ArchSpec::kCore_ppc64_any: if ((core2 >= ArchSpec::kCore_ppc64_first && core2 <= ArchSpec::kCore_ppc64_last) || (core2 == ArchSpec::kCore_ppc64_any)) return true; break; case ArchSpec::eCore_arm_armv6m: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_generic) return true; try_inverse = false; if (core2 == ArchSpec::eCore_arm_armv7) return true; if (core2 == ArchSpec::eCore_arm_armv6m) return true; } break; case ArchSpec::kCore_hexagon_any: if ((core2 >= ArchSpec::kCore_hexagon_first && core2 <= ArchSpec::kCore_hexagon_last) || (core2 == ArchSpec::kCore_hexagon_any)) return true; break; // v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization // Cortex-M0 - ARMv6-M - armv6m Cortex-M3 - ARMv7-M - armv7m Cortex-M4 - // ARMv7E-M - armv7em case ArchSpec::eCore_arm_armv7em: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_generic) return true; if (core2 == ArchSpec::eCore_arm_armv7m) return true; if (core2 == ArchSpec::eCore_arm_armv6m) return true; if (core2 == ArchSpec::eCore_arm_armv7) return true; try_inverse = true; } break; // v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization // Cortex-M0 - ARMv6-M - armv6m Cortex-M3 - ARMv7-M - armv7m Cortex-M4 - // ARMv7E-M - armv7em case ArchSpec::eCore_arm_armv7m: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_generic) return true; if (core2 == ArchSpec::eCore_arm_armv6m) return true; if (core2 == ArchSpec::eCore_arm_armv7) return true; if (core2 == ArchSpec::eCore_arm_armv7em) return true; try_inverse = true; } break; case ArchSpec::eCore_arm_armv7f: case ArchSpec::eCore_arm_armv7k: case ArchSpec::eCore_arm_armv7s: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_generic) return true; if (core2 == ArchSpec::eCore_arm_armv7) return true; try_inverse = false; } break; case ArchSpec::eCore_x86_64_x86_64h: if (!enforce_exact_match) { try_inverse = false; if (core2 == ArchSpec::eCore_x86_64_x86_64) return true; } break; case ArchSpec::eCore_arm_armv8: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_arm64) return true; if (core2 == ArchSpec::eCore_arm_aarch64) return true; try_inverse = false; } break; case ArchSpec::eCore_arm_aarch64: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_arm64) return true; if (core2 == ArchSpec::eCore_arm_armv8) return true; try_inverse = false; } break; case ArchSpec::eCore_arm_arm64: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_arm_aarch64) return true; if (core2 == ArchSpec::eCore_arm_armv8) return true; try_inverse = false; } break; case ArchSpec::eCore_mips32: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32_first && core2 <= ArchSpec::kCore_mips32_last) return true; try_inverse = false; } break; case ArchSpec::eCore_mips32el: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32el_first && core2 <= ArchSpec::kCore_mips32el_last) return true; try_inverse = true; } break; case ArchSpec::eCore_mips64: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32_first && core2 <= ArchSpec::kCore_mips32_last) return true; if (core2 >= ArchSpec::kCore_mips64_first && core2 <= ArchSpec::kCore_mips64_last) return true; try_inverse = false; } break; case ArchSpec::eCore_mips64el: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32el_first && core2 <= ArchSpec::kCore_mips32el_last) return true; if (core2 >= ArchSpec::kCore_mips64el_first && core2 <= ArchSpec::kCore_mips64el_last) return true; try_inverse = false; } break; case ArchSpec::eCore_mips64r2: case ArchSpec::eCore_mips64r3: case ArchSpec::eCore_mips64r5: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32_first && core2 <= (core1 - 10)) return true; if (core2 >= ArchSpec::kCore_mips64_first && core2 <= (core1 - 1)) return true; try_inverse = false; } break; case ArchSpec::eCore_mips64r2el: case ArchSpec::eCore_mips64r3el: case ArchSpec::eCore_mips64r5el: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32el_first && core2 <= (core1 - 10)) return true; if (core2 >= ArchSpec::kCore_mips64el_first && core2 <= (core1 - 1)) return true; try_inverse = false; } break; case ArchSpec::eCore_mips32r2: case ArchSpec::eCore_mips32r3: case ArchSpec::eCore_mips32r5: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32_first && core2 <= core1) return true; } break; case ArchSpec::eCore_mips32r2el: case ArchSpec::eCore_mips32r3el: case ArchSpec::eCore_mips32r5el: if (!enforce_exact_match) { if (core2 >= ArchSpec::kCore_mips32el_first && core2 <= core1) return true; } break; case ArchSpec::eCore_mips32r6: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_mips32 || core2 == ArchSpec::eCore_mips32r6) return true; } break; case ArchSpec::eCore_mips32r6el: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_mips32el || core2 == ArchSpec::eCore_mips32r6el) return true; } break; case ArchSpec::eCore_mips64r6: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_mips32 || core2 == ArchSpec::eCore_mips32r6) return true; if (core2 == ArchSpec::eCore_mips64 || core2 == ArchSpec::eCore_mips64r6) return true; } break; case ArchSpec::eCore_mips64r6el: if (!enforce_exact_match) { if (core2 == ArchSpec::eCore_mips32el || core2 == ArchSpec::eCore_mips32r6el) return true; if (core2 == ArchSpec::eCore_mips64el || core2 == ArchSpec::eCore_mips64r6el) return true; } break; default: break; } if (try_inverse) return cores_match(core2, core1, false, enforce_exact_match); return false; } bool lldb_private::operator<(const ArchSpec &lhs, const ArchSpec &rhs) { const ArchSpec::Core lhs_core = lhs.GetCore(); const ArchSpec::Core rhs_core = rhs.GetCore(); return lhs_core < rhs_core; } bool lldb_private::operator==(const ArchSpec &lhs, const ArchSpec &rhs) { return lhs.GetCore() == rhs.GetCore(); } bool ArchSpec::IsFullySpecifiedTriple() const { const auto &user_specified_triple = GetTriple(); bool user_triple_fully_specified = false; if ((user_specified_triple.getOS() != llvm::Triple::UnknownOS) || TripleOSWasSpecified()) { if ((user_specified_triple.getVendor() != llvm::Triple::UnknownVendor) || TripleVendorWasSpecified()) { const unsigned unspecified = 0; if (user_specified_triple.getOSMajorVersion() != unspecified) { user_triple_fully_specified = true; } } } return user_triple_fully_specified; } void ArchSpec::PiecewiseTripleCompare( const ArchSpec &other, bool &arch_different, bool &vendor_different, bool &os_different, bool &os_version_different, bool &env_different) const { const llvm::Triple &me(GetTriple()); const llvm::Triple &them(other.GetTriple()); arch_different = (me.getArch() != them.getArch()); vendor_different = (me.getVendor() != them.getVendor()); os_different = (me.getOS() != them.getOS()); os_version_different = (me.getOSMajorVersion() != them.getOSMajorVersion()); env_different = (me.getEnvironment() != them.getEnvironment()); } bool ArchSpec::IsAlwaysThumbInstructions() const { std::string Status; if (GetTriple().getArch() == llvm::Triple::arm || GetTriple().getArch() == llvm::Triple::thumb) { // v. https://en.wikipedia.org/wiki/ARM_Cortex-M // // Cortex-M0 through Cortex-M7 are ARM processor cores which can only // execute thumb instructions. We map the cores to arch names like this: // // Cortex-M0, Cortex-M0+, Cortex-M1: armv6m Cortex-M3: armv7m Cortex-M4, // Cortex-M7: armv7em if (GetCore() == ArchSpec::Core::eCore_arm_armv7m || GetCore() == ArchSpec::Core::eCore_arm_armv7em || GetCore() == ArchSpec::Core::eCore_arm_armv6m) { return true; } } return false; } void ArchSpec::DumpTriple(Stream &s) const { const llvm::Triple &triple = GetTriple(); llvm::StringRef arch_str = triple.getArchName(); llvm::StringRef vendor_str = triple.getVendorName(); llvm::StringRef os_str = triple.getOSName(); llvm::StringRef environ_str = triple.getEnvironmentName(); s.Printf("%s-%s-%s", arch_str.empty() ? "*" : arch_str.str().c_str(), vendor_str.empty() ? "*" : vendor_str.str().c_str(), os_str.empty() ? "*" : os_str.str().c_str()); if (!environ_str.empty()) s.Printf("-%s", environ_str.str().c_str()); } Index: vendor/lldb/dist/source/Utility/CompletionRequest.cpp =================================================================== --- vendor/lldb/dist/source/Utility/CompletionRequest.cpp (revision 337146) +++ vendor/lldb/dist/source/Utility/CompletionRequest.cpp (revision 337147) @@ -1,59 +1,60 @@ //===-- CompletionRequest.cpp -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Utility/CompletionRequest.h" using namespace lldb; using namespace lldb_private; CompletionRequest::CompletionRequest(llvm::StringRef command_line, unsigned raw_cursor_pos, int match_start_point, int max_return_elements, StringList &matches) : m_command(command_line), m_raw_cursor_pos(raw_cursor_pos), m_match_start_point(match_start_point), m_max_return_elements(max_return_elements), m_matches(&matches) { + matches.Clear(); // We parse the argument up to the cursor, so the last argument in // parsed_line is the one containing the cursor, and the cursor is after the // last character. m_parsed_line = Args(command_line); m_partial_parsed_line = Args(command_line.substr(0, raw_cursor_pos)); m_cursor_index = m_partial_parsed_line.GetArgumentCount() - 1; if (m_cursor_index == -1) m_cursor_char_position = 0; else m_cursor_char_position = strlen(m_partial_parsed_line.GetArgumentAtIndex(m_cursor_index)); matches.Clear(); const char *cursor = command_line.data() + raw_cursor_pos; if (raw_cursor_pos > 0 && cursor[-1] == ' ') { // We are just after a space. If we are in an argument, then we will // continue parsing, but if we are between arguments, then we have to // complete whatever the next element would be. We can distinguish the two // cases because if we are in an argument (e.g. because the space is // protected by a quote) then the space will also be in the parsed // argument... const char *current_elem = m_partial_parsed_line.GetArgumentAtIndex(m_cursor_index); if (m_cursor_char_position == 0 || current_elem[m_cursor_char_position - 1] != ' ') { m_parsed_line.InsertArgumentAtIndex(m_cursor_index + 1, llvm::StringRef(), '\0'); m_cursor_index++; m_cursor_char_position = 0; } } } Index: vendor/lldb/dist/source/Utility/Stream.cpp =================================================================== --- vendor/lldb/dist/source/Utility/Stream.cpp (revision 337146) +++ vendor/lldb/dist/source/Utility/Stream.cpp (revision 337147) @@ -1,573 +1,528 @@ //===-- Stream.cpp ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Utility/Stream.h" #include "lldb/Utility/Endian.h" #include "lldb/Utility/VASPrintf.h" #include "llvm/ADT/SmallString.h" // for SmallString #include #include #include using namespace lldb; using namespace lldb_private; Stream::Stream(uint32_t flags, uint32_t addr_size, ByteOrder byte_order) : m_flags(flags), m_addr_size(addr_size), m_byte_order(byte_order), m_indent_level(0) {} Stream::Stream() : m_flags(0), m_addr_size(4), m_byte_order(endian::InlHostByteOrder()), m_indent_level(0) {} //------------------------------------------------------------------ // Destructor //------------------------------------------------------------------ Stream::~Stream() {} ByteOrder Stream::SetByteOrder(ByteOrder byte_order) { ByteOrder old_byte_order = m_byte_order; m_byte_order = byte_order; return old_byte_order; } //------------------------------------------------------------------ // Put an offset "uval" out to the stream using the printf format in "format". //------------------------------------------------------------------ void Stream::Offset(uint32_t uval, const char *format) { Printf(format, uval); } //------------------------------------------------------------------ // Put an SLEB128 "uval" out to the stream using the printf format in "format". //------------------------------------------------------------------ size_t Stream::PutSLEB128(int64_t sval) { size_t bytes_written = 0; if (m_flags.Test(eBinary)) { bool more = true; while (more) { uint8_t byte = sval & 0x7fu; sval >>= 7; /* sign bit of byte is 2nd high order bit (0x40) */ if ((sval == 0 && !(byte & 0x40)) || (sval == -1 && (byte & 0x40))) more = false; else // more bytes to come byte |= 0x80u; bytes_written += Write(&byte, 1); } } else { bytes_written = Printf("0x%" PRIi64, sval); } return bytes_written; } //------------------------------------------------------------------ // Put an ULEB128 "uval" out to the stream using the printf format in "format". //------------------------------------------------------------------ size_t Stream::PutULEB128(uint64_t uval) { size_t bytes_written = 0; if (m_flags.Test(eBinary)) { do { uint8_t byte = uval & 0x7fu; uval >>= 7; if (uval != 0) { // more bytes to come byte |= 0x80u; } bytes_written += Write(&byte, 1); } while (uval != 0); } else { bytes_written = Printf("0x%" PRIx64, uval); } return bytes_written; } //------------------------------------------------------------------ // Print a raw NULL terminated C string to the stream. //------------------------------------------------------------------ size_t Stream::PutCString(llvm::StringRef str) { size_t bytes_written = 0; bytes_written = Write(str.data(), str.size()); // when in binary mode, emit the NULL terminator if (m_flags.Test(eBinary)) bytes_written += PutChar('\0'); return bytes_written; } //------------------------------------------------------------------ // Print a double quoted NULL terminated C string to the stream using the // printf format in "format". //------------------------------------------------------------------ void Stream::QuotedCString(const char *cstr, const char *format) { Printf(format, cstr); } //------------------------------------------------------------------ // Put an address "addr" out to the stream with optional prefix and suffix // strings. //------------------------------------------------------------------ void Stream::Address(uint64_t addr, uint32_t addr_size, const char *prefix, const char *suffix) { if (prefix == NULL) prefix = ""; if (suffix == NULL) suffix = ""; // int addr_width = m_addr_size << 1; // Printf ("%s0x%0*" PRIx64 "%s", prefix, addr_width, addr, suffix); Printf("%s0x%0*" PRIx64 "%s", prefix, addr_size * 2, (uint64_t)addr, suffix); } //------------------------------------------------------------------ // Put an address range out to the stream with optional prefix and suffix // strings. //------------------------------------------------------------------ void Stream::AddressRange(uint64_t lo_addr, uint64_t hi_addr, uint32_t addr_size, const char *prefix, const char *suffix) { if (prefix && prefix[0]) PutCString(prefix); Address(lo_addr, addr_size, "["); Address(hi_addr, addr_size, "-", ")"); if (suffix && suffix[0]) PutCString(suffix); } size_t Stream::PutChar(char ch) { return Write(&ch, 1); } //------------------------------------------------------------------ // Print some formatted output to the stream. //------------------------------------------------------------------ size_t Stream::Printf(const char *format, ...) { va_list args; va_start(args, format); size_t result = PrintfVarArg(format, args); va_end(args); return result; } //------------------------------------------------------------------ // Print some formatted output to the stream. //------------------------------------------------------------------ size_t Stream::PrintfVarArg(const char *format, va_list args) { llvm::SmallString<1024> buf; VASprintf(buf, format, args); // Include the NULL termination byte for binary output size_t length = buf.size(); if (m_flags.Test(eBinary)) ++length; return Write(buf.c_str(), length); } //------------------------------------------------------------------ // Print and End of Line character to the stream //------------------------------------------------------------------ size_t Stream::EOL() { return PutChar('\n'); } //------------------------------------------------------------------ // Indent the current line using the current indentation level and print an // optional string following the indentation spaces. //------------------------------------------------------------------ size_t Stream::Indent(const char *s) { return Printf("%*.*s%s", m_indent_level, m_indent_level, "", s ? s : ""); } size_t Stream::Indent(llvm::StringRef str) { return Printf("%*.*s%s", m_indent_level, m_indent_level, "", str.str().c_str()); } //------------------------------------------------------------------ // Stream a character "ch" out to this stream. //------------------------------------------------------------------ Stream &Stream::operator<<(char ch) { PutChar(ch); return *this; } //------------------------------------------------------------------ // Stream the NULL terminated C string out to this stream. //------------------------------------------------------------------ Stream &Stream::operator<<(const char *s) { Printf("%s", s); return *this; } Stream &Stream::operator<<(llvm::StringRef str) { Write(str.data(), str.size()); return *this; } //------------------------------------------------------------------ // Stream the pointer value out to this stream. //------------------------------------------------------------------ Stream &Stream::operator<<(const void *p) { Printf("0x%.*tx", (int)sizeof(const void *) * 2, (ptrdiff_t)p); return *this; } //------------------------------------------------------------------ // Stream a uint8_t "uval" out to this stream. //------------------------------------------------------------------ Stream &Stream::operator<<(uint8_t uval) { PutHex8(uval); return *this; } //------------------------------------------------------------------ // Stream a uint16_t "uval" out to this stream. //------------------------------------------------------------------ Stream &Stream::operator<<(uint16_t uval) { PutHex16(uval, m_byte_order); return *this; } //------------------------------------------------------------------ // Stream a uint32_t "uval" out to this stream. //------------------------------------------------------------------ Stream &Stream::operator<<(uint32_t uval) { PutHex32(uval, m_byte_order); return *this; } //------------------------------------------------------------------ // Stream a uint64_t "uval" out to this stream. //------------------------------------------------------------------ Stream &Stream::operator<<(uint64_t uval) { PutHex64(uval, m_byte_order); return *this; } //------------------------------------------------------------------ // Stream a int8_t "sval" out to this stream. //------------------------------------------------------------------ Stream &Stream::operator<<(int8_t sval) { Printf("%i", (int)sval); return *this; } //------------------------------------------------------------------ // Stream a int16_t "sval" out to this stream. //------------------------------------------------------------------ Stream &Stream::operator<<(int16_t sval) { Printf("%i", (int)sval); return *this; } //------------------------------------------------------------------ // Stream a int32_t "sval" out to this stream. //------------------------------------------------------------------ Stream &Stream::operator<<(int32_t sval) { Printf("%i", (int)sval); return *this; } //------------------------------------------------------------------ // Stream a int64_t "sval" out to this stream. //------------------------------------------------------------------ Stream &Stream::operator<<(int64_t sval) { Printf("%" PRIi64, sval); return *this; } //------------------------------------------------------------------ // Get the current indentation level //------------------------------------------------------------------ int Stream::GetIndentLevel() const { return m_indent_level; } //------------------------------------------------------------------ // Set the current indentation level //------------------------------------------------------------------ void Stream::SetIndentLevel(int indent_level) { m_indent_level = indent_level; } //------------------------------------------------------------------ // Increment the current indentation level //------------------------------------------------------------------ void Stream::IndentMore(int amount) { m_indent_level += amount; } //------------------------------------------------------------------ // Decrement the current indentation level //------------------------------------------------------------------ void Stream::IndentLess(int amount) { if (m_indent_level >= amount) m_indent_level -= amount; else m_indent_level = 0; } //------------------------------------------------------------------ // Get the address size in bytes //------------------------------------------------------------------ uint32_t Stream::GetAddressByteSize() const { return m_addr_size; } //------------------------------------------------------------------ // Set the address size in bytes //------------------------------------------------------------------ void Stream::SetAddressByteSize(uint32_t addr_size) { m_addr_size = addr_size; } //------------------------------------------------------------------ // The flags get accessor //------------------------------------------------------------------ Flags &Stream::GetFlags() { return m_flags; } //------------------------------------------------------------------ // The flags const get accessor //------------------------------------------------------------------ const Flags &Stream::GetFlags() const { return m_flags; } //------------------------------------------------------------------ // The byte order get accessor //------------------------------------------------------------------ lldb::ByteOrder Stream::GetByteOrder() const { return m_byte_order; } size_t Stream::PrintfAsRawHex8(const char *format, ...) { va_list args; va_start(args, format); llvm::SmallString<1024> buf; VASprintf(buf, format, args); size_t length = 0; for (char C : buf) length += _PutHex8(C, false); va_end(args); return length; } size_t Stream::PutNHex8(size_t n, uint8_t uvalue) { size_t bytes_written = 0; for (size_t i = 0; i < n; ++i) bytes_written += _PutHex8(uvalue, false); return bytes_written; } size_t Stream::_PutHex8(uint8_t uvalue, bool add_prefix) { size_t bytes_written = 0; if (m_flags.Test(eBinary)) { bytes_written = Write(&uvalue, 1); } else { if (add_prefix) PutCString("0x"); static char g_hex_to_ascii_hex_char[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; char nibble_chars[2]; nibble_chars[0] = g_hex_to_ascii_hex_char[(uvalue >> 4) & 0xf]; nibble_chars[1] = g_hex_to_ascii_hex_char[(uvalue >> 0) & 0xf]; bytes_written = Write(nibble_chars, sizeof(nibble_chars)); } return bytes_written; } size_t Stream::PutHex8(uint8_t uvalue) { return _PutHex8(uvalue, false); } size_t Stream::PutHex16(uint16_t uvalue, ByteOrder byte_order) { if (byte_order == eByteOrderInvalid) byte_order = m_byte_order; size_t bytes_written = 0; if (byte_order == eByteOrderLittle) { for (size_t byte = 0; byte < sizeof(uvalue); ++byte) bytes_written += _PutHex8((uint8_t)(uvalue >> (byte * 8)), false); } else { for (size_t byte = sizeof(uvalue) - 1; byte < sizeof(uvalue); --byte) bytes_written += _PutHex8((uint8_t)(uvalue >> (byte * 8)), false); } return bytes_written; } size_t Stream::PutHex32(uint32_t uvalue, ByteOrder byte_order) { if (byte_order == eByteOrderInvalid) byte_order = m_byte_order; size_t bytes_written = 0; if (byte_order == eByteOrderLittle) { for (size_t byte = 0; byte < sizeof(uvalue); ++byte) bytes_written += _PutHex8((uint8_t)(uvalue >> (byte * 8)), false); } else { for (size_t byte = sizeof(uvalue) - 1; byte < sizeof(uvalue); --byte) bytes_written += _PutHex8((uint8_t)(uvalue >> (byte * 8)), false); } return bytes_written; } size_t Stream::PutHex64(uint64_t uvalue, ByteOrder byte_order) { if (byte_order == eByteOrderInvalid) byte_order = m_byte_order; size_t bytes_written = 0; if (byte_order == eByteOrderLittle) { for (size_t byte = 0; byte < sizeof(uvalue); ++byte) bytes_written += _PutHex8((uint8_t)(uvalue >> (byte * 8)), false); } else { for (size_t byte = sizeof(uvalue) - 1; byte < sizeof(uvalue); --byte) bytes_written += _PutHex8((uint8_t)(uvalue >> (byte * 8)), false); } return bytes_written; } size_t Stream::PutMaxHex64(uint64_t uvalue, size_t byte_size, lldb::ByteOrder byte_order) { switch (byte_size) { case 1: return PutHex8((uint8_t)uvalue); case 2: return PutHex16((uint16_t)uvalue); case 4: return PutHex32((uint32_t)uvalue); case 8: return PutHex64(uvalue); } return 0; } size_t Stream::PutPointer(void *ptr) { return PutRawBytes(&ptr, sizeof(ptr), endian::InlHostByteOrder(), endian::InlHostByteOrder()); } size_t Stream::PutFloat(float f, ByteOrder byte_order) { if (byte_order == eByteOrderInvalid) byte_order = m_byte_order; return PutRawBytes(&f, sizeof(f), endian::InlHostByteOrder(), byte_order); } size_t Stream::PutDouble(double d, ByteOrder byte_order) { if (byte_order == eByteOrderInvalid) byte_order = m_byte_order; return PutRawBytes(&d, sizeof(d), endian::InlHostByteOrder(), byte_order); } size_t Stream::PutLongDouble(long double ld, ByteOrder byte_order) { if (byte_order == eByteOrderInvalid) byte_order = m_byte_order; return PutRawBytes(&ld, sizeof(ld), endian::InlHostByteOrder(), byte_order); } size_t Stream::PutRawBytes(const void *s, size_t src_len, ByteOrder src_byte_order, ByteOrder dst_byte_order) { if (src_byte_order == eByteOrderInvalid) src_byte_order = m_byte_order; if (dst_byte_order == eByteOrderInvalid) dst_byte_order = m_byte_order; size_t bytes_written = 0; const uint8_t *src = (const uint8_t *)s; bool binary_was_set = m_flags.Test(eBinary); if (!binary_was_set) m_flags.Set(eBinary); if (src_byte_order == dst_byte_order) { for (size_t i = 0; i < src_len; ++i) bytes_written += _PutHex8(src[i], false); } else { for (size_t i = src_len - 1; i < src_len; --i) bytes_written += _PutHex8(src[i], false); } if (!binary_was_set) m_flags.Clear(eBinary); return bytes_written; } size_t Stream::PutBytesAsRawHex8(const void *s, size_t src_len, ByteOrder src_byte_order, ByteOrder dst_byte_order) { if (src_byte_order == eByteOrderInvalid) src_byte_order = m_byte_order; if (dst_byte_order == eByteOrderInvalid) dst_byte_order = m_byte_order; size_t bytes_written = 0; const uint8_t *src = (const uint8_t *)s; bool binary_is_set = m_flags.Test(eBinary); m_flags.Clear(eBinary); if (src_byte_order == dst_byte_order) { for (size_t i = 0; i < src_len; ++i) bytes_written += _PutHex8(src[i], false); } else { for (size_t i = src_len - 1; i < src_len; --i) bytes_written += _PutHex8(src[i], false); } if (binary_is_set) m_flags.Set(eBinary); return bytes_written; } size_t Stream::PutCStringAsRawHex8(const char *s) { size_t bytes_written = 0; bool binary_is_set = m_flags.Test(eBinary); m_flags.Clear(eBinary); do { bytes_written += _PutHex8(*s, false); ++s; } while (*s); if (binary_is_set) m_flags.Set(eBinary); return bytes_written; } - -void Stream::UnitTest(Stream *s) { - s->PutHex8(0x12); - - s->PutChar(' '); - s->PutHex16(0x3456, endian::InlHostByteOrder()); - s->PutChar(' '); - s->PutHex16(0x3456, eByteOrderBig); - s->PutChar(' '); - s->PutHex16(0x3456, eByteOrderLittle); - - s->PutChar(' '); - s->PutHex32(0x789abcde, endian::InlHostByteOrder()); - s->PutChar(' '); - s->PutHex32(0x789abcde, eByteOrderBig); - s->PutChar(' '); - s->PutHex32(0x789abcde, eByteOrderLittle); - - s->PutChar(' '); - s->PutHex64(0x1122334455667788ull, endian::InlHostByteOrder()); - s->PutChar(' '); - s->PutHex64(0x1122334455667788ull, eByteOrderBig); - s->PutChar(' '); - s->PutHex64(0x1122334455667788ull, eByteOrderLittle); - - const char *hola = "Hello World!!!"; - s->PutChar(' '); - s->PutCString(hola); - - s->PutChar(' '); - s->Write(hola, 5); - - s->PutChar(' '); - s->PutCStringAsRawHex8(hola); - - s->PutChar(' '); - s->PutCStringAsRawHex8("01234"); - - s->PutChar(' '); - s->Printf("pid=%i", 12733); - - s->PutChar(' '); - s->PrintfAsRawHex8("pid=%i", 12733); - s->PutChar('\n'); -} Index: vendor/lldb/dist/unittests/Utility/CMakeLists.txt =================================================================== --- vendor/lldb/dist/unittests/Utility/CMakeLists.txt (revision 337146) +++ vendor/lldb/dist/unittests/Utility/CMakeLists.txt (revision 337147) @@ -1,36 +1,37 @@ add_lldb_unittest(UtilityTests AnsiTerminalTest.cpp ArgsTest.cpp OptionsWithRawTest.cpp ArchSpecTest.cpp CleanUpTest.cpp ConstStringTest.cpp CompletionRequestTest.cpp EnvironmentTest.cpp FileSpecTest.cpp FlagsTest.cpp JSONTest.cpp LogTest.cpp NameMatchesTest.cpp StatusTest.cpp StreamTeeTest.cpp + StreamTest.cpp StringExtractorTest.cpp StructuredDataTest.cpp TildeExpressionResolverTest.cpp TimeoutTest.cpp TimerTest.cpp UriParserTest.cpp UUIDTest.cpp VASprintfTest.cpp VMRangeTest.cpp LINK_LIBS lldbUtility lldbUtilityHelpers LINK_COMPONENTS Support ) add_unittest_inputs(UtilityTests StructuredData-basic.json ) Index: vendor/lldb/dist/unittests/Utility/CompletionRequestTest.cpp =================================================================== --- vendor/lldb/dist/unittests/Utility/CompletionRequestTest.cpp (revision 337146) +++ vendor/lldb/dist/unittests/Utility/CompletionRequestTest.cpp (revision 337147) @@ -1,40 +1,103 @@ //===-- CompletionRequestTest.cpp -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "gtest/gtest.h" #include "lldb/Utility/CompletionRequest.h" using namespace lldb_private; TEST(CompletionRequest, Constructor) { std::string command = "a bad c"; const unsigned cursor_pos = 3; const int arg_index = 1; const int arg_cursor_pos = 1; const int match_start = 2345; const int match_max_return = 12345; StringList matches; CompletionRequest request(command, cursor_pos, match_start, match_max_return, matches); EXPECT_STREQ(request.GetRawLine().str().c_str(), command.c_str()); EXPECT_EQ(request.GetRawCursorPos(), cursor_pos); EXPECT_EQ(request.GetCursorIndex(), arg_index); EXPECT_EQ(request.GetCursorCharPosition(), arg_cursor_pos); EXPECT_EQ(request.GetMatchStartPoint(), match_start); EXPECT_EQ(request.GetMaxReturnElements(), match_max_return); EXPECT_EQ(request.GetWordComplete(), false); EXPECT_EQ(request.GetPartialParsedLine().GetArgumentCount(), 2u); EXPECT_STREQ(request.GetPartialParsedLine().GetArgumentAtIndex(1), "b"); +} - // This is the generated matches should be equal to our passed string list. - EXPECT_EQ(&request.GetMatches(), &matches); +TEST(CompletionRequest, DuplicateFiltering) { + std::string command = "a bad c"; + const unsigned cursor_pos = 3; + StringList matches; + + CompletionRequest request(command, cursor_pos, 0, 0, matches); + + EXPECT_EQ(0U, request.GetNumberOfMatches()); + + // Add foo twice + request.AddCompletion("foo"); + EXPECT_EQ(1U, request.GetNumberOfMatches()); + EXPECT_EQ(1U, matches.GetSize()); + EXPECT_STREQ("foo", matches.GetStringAtIndex(0)); + + request.AddCompletion("foo"); + EXPECT_EQ(1U, request.GetNumberOfMatches()); + EXPECT_EQ(1U, matches.GetSize()); + EXPECT_STREQ("foo", matches.GetStringAtIndex(0)); + + // Add bar twice + request.AddCompletion("bar"); + EXPECT_EQ(2U, request.GetNumberOfMatches()); + EXPECT_EQ(2U, matches.GetSize()); + EXPECT_STREQ("foo", matches.GetStringAtIndex(0)); + EXPECT_STREQ("bar", matches.GetStringAtIndex(1)); + + request.AddCompletion("bar"); + EXPECT_EQ(2U, request.GetNumberOfMatches()); + EXPECT_EQ(2U, matches.GetSize()); + EXPECT_STREQ("foo", matches.GetStringAtIndex(0)); + EXPECT_STREQ("bar", matches.GetStringAtIndex(1)); + + // Add foo again. + request.AddCompletion("foo"); + EXPECT_EQ(2U, request.GetNumberOfMatches()); + EXPECT_EQ(2U, matches.GetSize()); + EXPECT_STREQ("foo", matches.GetStringAtIndex(0)); + EXPECT_STREQ("bar", matches.GetStringAtIndex(1)); + + // Add something with an existing prefix + request.AddCompletion("foobar"); + EXPECT_EQ(3U, request.GetNumberOfMatches()); + EXPECT_EQ(3U, matches.GetSize()); + EXPECT_STREQ("foo", matches.GetStringAtIndex(0)); + EXPECT_STREQ("bar", matches.GetStringAtIndex(1)); + EXPECT_STREQ("foobar", matches.GetStringAtIndex(2)); +} + +TEST(CompletionRequest, TestCompletionOwnership) { + std::string command = "a bad c"; + const unsigned cursor_pos = 3; + StringList matches; + + CompletionRequest request(command, cursor_pos, 0, 0, matches); + + std::string Temporary = "bar"; + request.AddCompletion(Temporary); + // Manipulate our completion. The request should have taken a copy, so that + // shouldn't influence anything. + Temporary[0] = 'f'; + + EXPECT_EQ(1U, request.GetNumberOfMatches()); + EXPECT_STREQ("bar", matches.GetStringAtIndex(0)); } Index: vendor/lldb/dist/unittests/Utility/StreamTest.cpp =================================================================== --- vendor/lldb/dist/unittests/Utility/StreamTest.cpp (nonexistent) +++ vendor/lldb/dist/unittests/Utility/StreamTest.cpp (revision 337147) @@ -0,0 +1,474 @@ +//===-- StreamTest.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/StreamString.h" +#include "gtest/gtest.h" + +using namespace lldb_private; + +namespace { +struct StreamTest : ::testing::Test { + // Note: Stream is an abstract class, so we use StreamString to test it. To + // make it easier to change this later, only methods in this class explicitly + // refer to the StringStream class. + StreamString s; + // We return here a std::string because that way gtest can print better + // assertion messages. + std::string TakeValue() { + std::string result = s.GetString().str(); + s.Clear(); + return result; + } +}; +} + +namespace { +// A StreamTest where we expect the Stream output to be binary. +struct BinaryStreamTest : StreamTest { + void SetUp() override { + s.GetFlags().Set(Stream::eBinary); + } +}; +} + +TEST_F(StreamTest, ChangingByteOrder) { + s.SetByteOrder(lldb::eByteOrderPDP); + EXPECT_EQ(lldb::eByteOrderPDP, s.GetByteOrder()); +} + +TEST_F(StreamTest, PutChar) { + s.PutChar('a'); + EXPECT_EQ("a", TakeValue()); + + s.PutChar('1'); + EXPECT_EQ("1", TakeValue()); +} + +TEST_F(StreamTest, PutCharWhitespace) { + s.PutChar(' '); + EXPECT_EQ(" ", TakeValue()); + + s.PutChar('\n'); + EXPECT_EQ("\n", TakeValue()); + + s.PutChar('\r'); + EXPECT_EQ("\r", TakeValue()); + + s.PutChar('\t'); + EXPECT_EQ("\t", TakeValue()); +} + +TEST_F(StreamTest, PutCString) { + s.PutCString(""); + EXPECT_EQ("", TakeValue()); + + s.PutCString("foobar"); + EXPECT_EQ("foobar", TakeValue()); + + s.PutCString(" "); + EXPECT_EQ(" ", TakeValue()); +} + +TEST_F(StreamTest, PutCStringWithStringRef) { + s.PutCString(llvm::StringRef("")); + EXPECT_EQ("", TakeValue()); + + s.PutCString(llvm::StringRef("foobar")); + EXPECT_EQ("foobar", TakeValue()); + + s.PutCString(llvm::StringRef(" ")); + EXPECT_EQ(" ", TakeValue()); +} + +TEST_F(StreamTest, QuotedCString) { + s.QuotedCString("foo"); + EXPECT_EQ(R"("foo")", TakeValue()); + + s.QuotedCString("ba r"); + EXPECT_EQ(R"("ba r")", TakeValue()); + + s.QuotedCString(" "); + EXPECT_EQ(R"(" ")", TakeValue()); +} + +TEST_F(StreamTest, PutCharNull) { + s.PutChar('\0'); + EXPECT_EQ(std::string("\0", 1), TakeValue()); + + s.PutChar('a'); + EXPECT_EQ(std::string("a", 1), TakeValue()); +} + +TEST_F(StreamTest, PutCStringAsRawHex8) { + s.PutCStringAsRawHex8("foobar"); + EXPECT_EQ("666f6f626172", TakeValue()); + + s.PutCStringAsRawHex8(" "); + EXPECT_EQ("20", TakeValue()); +} + +TEST_F(StreamTest, PutHex8) { + s.PutHex8((uint8_t)55); + EXPECT_EQ("37", TakeValue()); + s.PutHex8(std::numeric_limits::max()); + EXPECT_EQ("ff", TakeValue()); + s.PutHex8((uint8_t)0); + EXPECT_EQ("00", TakeValue()); +} + +TEST_F(StreamTest, PutNHex8) { + s.PutNHex8(0, (uint8_t)55); + EXPECT_EQ("", TakeValue()); + s.PutNHex8(1, (uint8_t)55); + EXPECT_EQ("37", TakeValue()); + s.PutNHex8(2, (uint8_t)55); + EXPECT_EQ("3737", TakeValue()); + s.PutNHex8(1, (uint8_t)56); + EXPECT_EQ("38", TakeValue()); +} + +TEST_F(StreamTest, PutHex16ByteOrderLittle) { + s.PutHex16(0x1234U, lldb::eByteOrderLittle); + EXPECT_EQ("3412", TakeValue()); + s.PutHex16(std::numeric_limits::max(), lldb::eByteOrderLittle); + EXPECT_EQ("ffff", TakeValue()); + s.PutHex16(0U, lldb::eByteOrderLittle); + EXPECT_EQ("0000", TakeValue()); +} + +TEST_F(StreamTest, PutHex16ByteOrderBig) { + s.PutHex16(0x1234U, lldb::eByteOrderBig); + EXPECT_EQ("1234", TakeValue()); + s.PutHex16(std::numeric_limits::max(), lldb::eByteOrderBig); + EXPECT_EQ("ffff", TakeValue()); + s.PutHex16(0U, lldb::eByteOrderBig); + EXPECT_EQ("0000", TakeValue()); +} + +TEST_F(StreamTest, PutHex32ByteOrderLittle) { + s.PutHex32(0x12345678U, lldb::eByteOrderLittle); + EXPECT_EQ("78563412", TakeValue()); + s.PutHex32(std::numeric_limits::max(), lldb::eByteOrderLittle); + EXPECT_EQ("ffffffff", TakeValue()); + s.PutHex32(0U, lldb::eByteOrderLittle); + EXPECT_EQ("00000000", TakeValue()); +} + +TEST_F(StreamTest, PutHex32ByteOrderBig) { + s.PutHex32(0x12345678U, lldb::eByteOrderBig); + EXPECT_EQ("12345678", TakeValue()); + s.PutHex32(std::numeric_limits::max(), lldb::eByteOrderBig); + EXPECT_EQ("ffffffff", TakeValue()); + s.PutHex32(0U, lldb::eByteOrderBig); + EXPECT_EQ("00000000", TakeValue()); +} + +TEST_F(StreamTest, PutHex64ByteOrderLittle) { + s.PutHex64(0x1234567890ABCDEFU, lldb::eByteOrderLittle); + EXPECT_EQ("efcdab9078563412", TakeValue()); + s.PutHex64(std::numeric_limits::max(), lldb::eByteOrderLittle); + EXPECT_EQ("ffffffffffffffff", TakeValue()); + s.PutHex64(0U, lldb::eByteOrderLittle); + EXPECT_EQ("0000000000000000", TakeValue()); +} + +TEST_F(StreamTest, PutHex64ByteOrderBig) { + s.PutHex64(0x1234567890ABCDEFU, lldb::eByteOrderBig); + EXPECT_EQ("1234567890abcdef", TakeValue()); + s.PutHex64(std::numeric_limits::max(), lldb::eByteOrderBig); + EXPECT_EQ("ffffffffffffffff", TakeValue()); + s.PutHex64(0U, lldb::eByteOrderBig); + EXPECT_EQ("0000000000000000", TakeValue()); +} + +//------------------------------------------------------------------------------ +// Shift operator tests. +//------------------------------------------------------------------------------ + +TEST_F(StreamTest, ShiftOperatorChars) { + s << 'a' << 'b'; + EXPECT_EQ("ab", TakeValue()); +} + +TEST_F(StreamTest, ShiftOperatorStrings) { + s << "cstring\n"; + s << llvm::StringRef("llvm::StringRef\n"); + EXPECT_EQ("cstring\nllvm::StringRef\n", TakeValue()); +} + +TEST_F(StreamTest, ShiftOperatorInts) { + s << std::numeric_limits::max() << " "; + s << std::numeric_limits::max() << " "; + s << std::numeric_limits::max() << " "; + s << std::numeric_limits::max(); + EXPECT_EQ("127 32767 2147483647 9223372036854775807", TakeValue()); +} + +TEST_F(StreamTest, ShiftOperatorUInts) { + s << std::numeric_limits::max() << " "; + s << std::numeric_limits::max() << " "; + s << std::numeric_limits::max() << " "; + s << std::numeric_limits::max(); + EXPECT_EQ("ff ffff ffffffff ffffffffffffffff", TakeValue()); +} + +TEST_F(StreamTest, ShiftOperatorPtr) { + // This test is a bit tricky because pretty much everything related to + // pointer printing seems to lead to UB or IB. So let's make the most basic + // test that just checks that we print *something*. This way we at least know + // that pointer printing doesn't do really bad things (e.g. crashing, reading + // OOB/uninitialized memory which the sanitizers would spot). + + // Shift our own pointer to the output. + int i = 3; + int *ptr = &i; + s << ptr; + + EXPECT_TRUE(!TakeValue().empty()); +} + +TEST_F(StreamTest, PutPtr) { + // See the ShiftOperatorPtr test for the rationale. + int i = 3; + int *ptr = &i; + s.PutPointer(ptr); + + EXPECT_TRUE(!TakeValue().empty()); +} + +// Alias to make it more clear that 'invalid' means for the Stream interface +// that it should use the host byte order. +const static auto hostByteOrder = lldb::eByteOrderInvalid; + +//------------------------------------------------------------------------------ +// PutRawBytes/PutBytesAsRawHex tests. +//------------------------------------------------------------------------------ + +TEST_F(StreamTest, PutBytesAsRawHex8ToBigEndian) { + uint32_t value = 0x12345678; + s.PutBytesAsRawHex8(static_cast(&value), sizeof(value), + hostByteOrder, lldb::eByteOrderBig); + EXPECT_EQ("78563412", TakeValue()); +} + +TEST_F(StreamTest, PutRawBytesToBigEndian) { + uint32_t value = 0x12345678; + s.PutRawBytes(static_cast(&value), sizeof(value), + hostByteOrder, lldb::eByteOrderBig); + EXPECT_EQ("\x78\x56\x34\x12", TakeValue()); +} + +TEST_F(StreamTest, PutBytesAsRawHex8ToLittleEndian) { + uint32_t value = 0x12345678; + s.PutBytesAsRawHex8(static_cast(&value), sizeof(value), + hostByteOrder, lldb::eByteOrderLittle); + EXPECT_EQ("12345678", TakeValue()); +} + +TEST_F(StreamTest, PutRawBytesToLittleEndian) { + uint32_t value = 0x12345678; + s.PutRawBytes(static_cast(&value), sizeof(value), + hostByteOrder, lldb::eByteOrderLittle); + EXPECT_EQ("\x12\x34\x56\x78", TakeValue()); +} + +TEST_F(StreamTest, PutBytesAsRawHex8ToMixedEndian) { + uint32_t value = 0x12345678; + s.PutBytesAsRawHex8(static_cast(&value), sizeof(value), + hostByteOrder, lldb::eByteOrderPDP); + + // FIXME: PDP byte order is not actually implemented but Stream just silently + // prints the value in some random byte order... +#if 0 + EXPECT_EQ("34127856", TakeValue()); +#endif +} + +TEST_F(StreamTest, PutRawBytesToMixedEndian) { + uint32_t value = 0x12345678; + s.PutRawBytes(static_cast(&value), sizeof(value), + lldb::eByteOrderInvalid, lldb::eByteOrderPDP); + + // FIXME: PDP byte order is not actually implemented but Stream just silently + // prints the value in some random byte order... +#if 0 + EXPECT_EQ("\x34\x12\x78\x56", TakeValue()); +#endif +} + +//------------------------------------------------------------------------------ +// ULEB128 support for binary streams. +//------------------------------------------------------------------------------ + +TEST_F(BinaryStreamTest, PutULEB128OneByte) { + auto bytes = s.PutULEB128(0x74ULL); + EXPECT_EQ("\x74", TakeValue()); + EXPECT_EQ(1U, bytes); +} + +TEST_F(BinaryStreamTest, PutULEB128TwoBytes) { + auto bytes = s.PutULEB128(0x1985ULL); + EXPECT_EQ("\x85\x33", TakeValue()); + EXPECT_EQ(2U, bytes); +} + +TEST_F(BinaryStreamTest, PutULEB128ThreeBytes) { + auto bytes = s.PutULEB128(0x5023ULL); + EXPECT_EQ("\xA3\xA0\x1", TakeValue()); + EXPECT_EQ(3U, bytes); +} + +TEST_F(BinaryStreamTest, PutULEB128FourBytes) { + auto bytes = s.PutULEB128(0xA48032ULL); + EXPECT_EQ("\xB2\x80\x92\x5", TakeValue()); + EXPECT_EQ(4U, bytes); +} + +TEST_F(BinaryStreamTest, PutULEB128FiveBytes) { + auto bytes = s.PutULEB128(0x12345678ULL); + EXPECT_EQ("\xF8\xAC\xD1\x91\x1", TakeValue()); + EXPECT_EQ(5U, bytes); +} + +TEST_F(BinaryStreamTest, PutULEB128SixBytes) { + auto bytes = s.PutULEB128(0xABFE3FAFDFULL); + EXPECT_EQ("\xDF\xDF\xFE\xF1\xBF\x15", TakeValue()); + EXPECT_EQ(6U, bytes); +} + +TEST_F(BinaryStreamTest, PutULEB128SevenBytes) { + auto bytes = s.PutULEB128(0xDABFE3FAFDFULL); + EXPECT_EQ("\xDF\xDF\xFE\xF1\xBF\xB5\x3", TakeValue()); + EXPECT_EQ(7U, bytes); +} + +TEST_F(BinaryStreamTest, PutULEB128EightBytes) { + auto bytes = s.PutULEB128(0x7CDABFE3FAFDFULL); + EXPECT_EQ("\xDF\xDF\xFE\xF1\xBF\xB5\xF3\x3", TakeValue()); + EXPECT_EQ(8U, bytes); +} + +TEST_F(BinaryStreamTest, PutULEB128NineBytes) { + auto bytes = s.PutULEB128(0x327CDABFE3FAFDFULL); + EXPECT_EQ("\xDF\xDF\xFE\xF1\xBF\xB5\xF3\x93\x3", TakeValue()); + EXPECT_EQ(9U, bytes); +} + +TEST_F(BinaryStreamTest, PutULEB128MaxValue) { + auto bytes = s.PutULEB128(std::numeric_limits::max()); + EXPECT_EQ("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x1", TakeValue()); + EXPECT_EQ(10U, bytes); +} + +TEST_F(BinaryStreamTest, PutULEB128Zero) { + auto bytes = s.PutULEB128(0x0U); + EXPECT_EQ(std::string("\0", 1), TakeValue()); + EXPECT_EQ(1U, bytes); +} + +TEST_F(BinaryStreamTest, PutULEB128One) { + auto bytes = s.PutULEB128(0x1U); + EXPECT_EQ("\x1", TakeValue()); + EXPECT_EQ(1U, bytes); +} + +//------------------------------------------------------------------------------ +// SLEB128 support for binary streams. +//------------------------------------------------------------------------------ + +TEST_F(BinaryStreamTest, PutSLEB128OneByte) { + auto bytes = s.PutSLEB128(0x74LL); + EXPECT_EQ(std::string("\xF4\0", 2), TakeValue()); + EXPECT_EQ(2U, bytes); +} + +TEST_F(BinaryStreamTest, PutSLEB128TwoBytes) { + auto bytes = s.PutSLEB128(0x1985LL); + EXPECT_EQ("\x85\x33", TakeValue()); + EXPECT_EQ(2U, bytes); +} + +TEST_F(BinaryStreamTest, PutSLEB128ThreeBytes) { + auto bytes = s.PutSLEB128(0x5023LL); + EXPECT_EQ("\xA3\xA0\x1", TakeValue()); + EXPECT_EQ(3U, bytes); +} + +TEST_F(BinaryStreamTest, PutSLEB128FourBytes) { + auto bytes = s.PutSLEB128(0xA48032LL); + EXPECT_EQ("\xB2\x80\x92\x5", TakeValue()); + EXPECT_EQ(4U, bytes); +} + +TEST_F(BinaryStreamTest, PutSLEB128FiveBytes) { + auto bytes = s.PutSLEB128(0x12345678LL); + EXPECT_EQ("\xF8\xAC\xD1\x91\x1", TakeValue()); + EXPECT_EQ(5U, bytes); +} + +TEST_F(BinaryStreamTest, PutSLEB128SixBytes) { + auto bytes = s.PutSLEB128(0xABFE3FAFDFLL); + EXPECT_EQ("\xDF\xDF\xFE\xF1\xBF\x15", TakeValue()); + EXPECT_EQ(6U, bytes); +} + +TEST_F(BinaryStreamTest, PutSLEB128SevenBytes) { + auto bytes = s.PutSLEB128(0xDABFE3FAFDFLL); + EXPECT_EQ("\xDF\xDF\xFE\xF1\xBF\xB5\x3", TakeValue()); + EXPECT_EQ(7U, bytes); +} + +TEST_F(BinaryStreamTest, PutSLEB128EightBytes) { + auto bytes = s.PutSLEB128(0x7CDABFE3FAFDFLL); + EXPECT_EQ("\xDF\xDF\xFE\xF1\xBF\xB5\xF3\x3", TakeValue()); + EXPECT_EQ(8U, bytes); +} + +TEST_F(BinaryStreamTest, PutSLEB128NineBytes) { + auto bytes = s.PutSLEB128(0x327CDABFE3FAFDFLL); + EXPECT_EQ("\xDF\xDF\xFE\xF1\xBF\xB5\xF3\x93\x3", TakeValue()); + EXPECT_EQ(9U, bytes); +} + +TEST_F(BinaryStreamTest, PutSLEB128MaxValue) { + auto bytes = s.PutSLEB128(std::numeric_limits::max()); + EXPECT_EQ(std::string("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0", 10), TakeValue()); + EXPECT_EQ(10U, bytes); +} + +TEST_F(BinaryStreamTest, PutSLEB128Zero) { + auto bytes = s.PutSLEB128(0x0); + EXPECT_EQ(std::string("\0", 1), TakeValue()); + EXPECT_EQ(1U, bytes); +} + +TEST_F(BinaryStreamTest, PutSLEB128One) { + auto bytes = s.PutSLEB128(0x1); + EXPECT_EQ(std::string("\x1", 1), TakeValue()); + EXPECT_EQ(1U, bytes); +} + +//------------------------------------------------------------------------------ +// SLEB128/ULEB128 support for non-binary streams. +//------------------------------------------------------------------------------ + +// The logic for this is very simple, so it should be enough to test some basic +// use cases. + +TEST_F(StreamTest, PutULEB128) { + auto bytes = s.PutULEB128(0x74ULL); + EXPECT_EQ("0x74", TakeValue()); + EXPECT_EQ(4U, bytes); +} + +TEST_F(StreamTest, PutSLEB128) { + auto bytes = s.PutSLEB128(0x1985LL); + EXPECT_EQ("0x6533", TakeValue()); + EXPECT_EQ(6U, bytes); +} Property changes on: vendor/lldb/dist/unittests/Utility/StreamTest.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property