Index: projects/clang700-import/contrib/llvm/tools/lldb/include/lldb/Utility/CompletionRequest.h =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/include/lldb/Utility/CompletionRequest.h (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/include/lldb/Utility/CompletionRequest.h (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/include/lldb/Utility/Stream.h =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/include/lldb/Utility/Stream.h (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/include/lldb/Utility/Stream.h (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandCompletions.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandCompletions.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandCompletions.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectCommands.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectCommands.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectCommands.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectFrame.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectFrame.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectFrame.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectMultiword.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectMultiword.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectMultiword.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlatform.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlatform.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlatform.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlugin.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlugin.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectPlugin.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectProcess.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectProcess.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectProcess.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectSettings.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectSettings.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectSettings.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Commands/CommandObjectTarget.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Core/FormatEntity.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Core/FormatEntity.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Core/FormatEntity.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Core/IOHandler.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Core/IOHandler.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Core/IOHandler.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/CommandInterpreter.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/CommandInterpreter.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/CommandInterpreter.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/CommandObject.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/CommandObject.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/CommandObject.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/CommandObjectRegexCommand.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/CommandObjectRegexCommand.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/CommandObjectRegexCommand.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValue.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValue.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValue.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueArch.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueArch.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueArch.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueBoolean.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueBoolean.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueBoolean.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueEnumeration.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueEnumeration.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueEnumeration.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueFileSpec.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueFileSpec.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueFileSpec.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueUUID.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueUUID.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/OptionValueUUID.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/Options.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/Options.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Interpreter/Options.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Symbol/Variable.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Symbol/Variable.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Symbol/Variable.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Utility/ArchSpec.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Utility/ArchSpec.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Utility/ArchSpec.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Utility/CompletionRequest.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Utility/CompletionRequest.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Utility/CompletionRequest.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb/source/Utility/Stream.cpp =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb/source/Utility/Stream.cpp (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb/source/Utility/Stream.cpp (revision 337152) @@ -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: projects/clang700-import/contrib/llvm/tools/lldb =================================================================== --- projects/clang700-import/contrib/llvm/tools/lldb (revision 337151) +++ projects/clang700-import/contrib/llvm/tools/lldb (revision 337152) Property changes on: projects/clang700-import/contrib/llvm/tools/lldb ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /vendor/lldb/dist:r336984-337151