Index: vendor/lldb/dist/include/lldb/Core/MappedHash.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/MappedHash.h (revision 311541) +++ vendor/lldb/dist/include/lldb/Core/MappedHash.h (revision 311542) @@ -1,484 +1,483 @@ //===-- MappedHash.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_MappedHash_h_ #define liblldb_MappedHash_h_ // C Includes #include #include // C++ Includes #include #include #include #include // Other libraries and framework includes // Project includes #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Stream.h" class MappedHash { public: enum HashFunctionType { eHashFunctionDJB = 0u // Daniel J Bernstein hash function that is also used // by the ELF GNU_HASH sections }; static uint32_t HashStringUsingDJB(const char *s) { uint32_t h = 5381; for (unsigned char c = *s; c; c = *++s) h = ((h << 5) + h) + c; return h; } static uint32_t HashString(uint32_t hash_function, const char *s) { if (!s) return 0; switch (hash_function) { case MappedHash::eHashFunctionDJB: return HashStringUsingDJB(s); default: break; } - assert(!"Invalid hash function index"); - return 0; + llvm_unreachable("Invalid hash function index"); } static const uint32_t HASH_MAGIC = 0x48415348u; static const uint32_t HASH_CIGAM = 0x48534148u; template struct Header { typedef T HeaderData; uint32_t magic; // HASH_MAGIC or HASH_CIGAM magic value to allow endian detection uint16_t version; // Version number uint16_t hash_function; // The hash function enumeration that was used uint32_t bucket_count; // The number of buckets in this hash table uint32_t hashes_count; // The total number of unique hash values and hash // data offsets in this table uint32_t header_data_len; // The size in bytes of the "header_data" template // member below HeaderData header_data; // Header() : magic(HASH_MAGIC), version(1), hash_function(eHashFunctionDJB), bucket_count(0), hashes_count(0), header_data_len(sizeof(T)), header_data() {} virtual ~Header() = default; size_t GetByteSize() const { return sizeof(magic) + sizeof(version) + sizeof(hash_function) + sizeof(bucket_count) + sizeof(hashes_count) + sizeof(header_data_len) + header_data_len; } virtual size_t GetByteSize(const HeaderData &header_data) = 0; void SetHeaderDataByteSize(uint32_t header_data_byte_size) { header_data_len = header_data_byte_size; } void Dump(lldb_private::Stream &s) { s.Printf("header.magic = 0x%8.8x\n", magic); s.Printf("header.version = 0x%4.4x\n", version); s.Printf("header.hash_function = 0x%4.4x\n", hash_function); s.Printf("header.bucket_count = 0x%8.8x %u\n", bucket_count, bucket_count); s.Printf("header.hashes_count = 0x%8.8x %u\n", hashes_count, hashes_count); s.Printf("header.header_data_len = 0x%8.8x %u\n", header_data_len, header_data_len); } virtual lldb::offset_t Read(lldb_private::DataExtractor &data, lldb::offset_t offset) { if (data.ValidOffsetForDataOfSize( offset, sizeof(magic) + sizeof(version) + sizeof(hash_function) + sizeof(bucket_count) + sizeof(hashes_count) + sizeof(header_data_len))) { magic = data.GetU32(&offset); if (magic != HASH_MAGIC) { if (magic == HASH_CIGAM) { switch (data.GetByteOrder()) { case lldb::eByteOrderBig: data.SetByteOrder(lldb::eByteOrderLittle); break; case lldb::eByteOrderLittle: data.SetByteOrder(lldb::eByteOrderBig); break; default: return LLDB_INVALID_OFFSET; } } else { // Magic bytes didn't match version = 0; return LLDB_INVALID_OFFSET; } } version = data.GetU16(&offset); if (version != 1) { // Unsupported version return LLDB_INVALID_OFFSET; } hash_function = data.GetU16(&offset); if (hash_function == 4) hash_function = 0; // Deal with pre-release version of this table... bucket_count = data.GetU32(&offset); hashes_count = data.GetU32(&offset); header_data_len = data.GetU32(&offset); return offset; } return LLDB_INVALID_OFFSET; } // // // Returns a buffer that contains a serialized version of this // table // // that must be freed with free(). // virtual void * // Write (int fd); }; template class ExportTable { public: typedef __HeaderDataType HeaderDataType; typedef Header HeaderType; typedef __KeyType KeyType; typedef __ValueType ValueType; struct Entry { uint32_t hash; KeyType key; ValueType value; }; typedef std::vector ValueArrayType; typedef std::map HashData; // Map a name hash to one or more name infos typedef std::map HashToHashData; virtual KeyType GetKeyForStringType(const char *cstr) const = 0; virtual size_t GetByteSize(const HashData &key_to_key_values) = 0; virtual bool WriteHashData(const HashData &hash_data, lldb_private::Stream &ostrm) = 0; // void AddEntry(const char *cstr, const ValueType &value) { Entry entry; entry.hash = MappedHash::HashString(eHashFunctionDJB, cstr); entry.key = GetKeyForStringType(cstr); entry.value = value; m_entries.push_back(entry); } void Save(const HeaderDataType &header_data, lldb_private::Stream &ostrm) { if (m_entries.empty()) return; const uint32_t num_entries = m_entries.size(); uint32_t i = 0; HeaderType header; header.magic = HASH_MAGIC; header.version = 1; header.hash_function = eHashFunctionDJB; header.bucket_count = 0; header.hashes_count = 0; header.prologue_length = header_data.GetByteSize(); // We need to figure out the number of unique hashes first before we can // calculate the number of buckets we want to use. typedef std::vector hash_coll; hash_coll unique_hashes; unique_hashes.resize(num_entries); for (i = 0; i < num_entries; ++i) unique_hashes[i] = m_entries[i].hash; std::sort(unique_hashes.begin(), unique_hashes.end()); hash_coll::iterator pos = std::unique(unique_hashes.begin(), unique_hashes.end()); const size_t num_unique_hashes = std::distance(unique_hashes.begin(), pos); if (num_unique_hashes > 1024) header.bucket_count = num_unique_hashes / 4; else if (num_unique_hashes > 16) header.bucket_count = num_unique_hashes / 2; else header.bucket_count = num_unique_hashes; if (header.bucket_count == 0) header.bucket_count = 1; std::vector hash_buckets; std::vector hash_indexes(header.bucket_count, 0); std::vector hash_values; std::vector hash_offsets; hash_buckets.resize(header.bucket_count); uint32_t bucket_entry_empties = 0; // StreamString hash_file_data(Stream::eBinary, // dwarf->GetObjectFile()->GetAddressByteSize(), // dwarf->GetObjectFile()->GetByteSize()); // Push all of the hashes into their buckets and create all bucket // entries all populated with data. for (i = 0; i < num_entries; ++i) { const uint32_t hash = m_entries[i].hash; const uint32_t bucket_idx = hash % header.bucket_count; const uint32_t strp_offset = m_entries[i].str_offset; const uint32_t die_offset = m_entries[i].die_offset; hash_buckets[bucket_idx][hash][strp_offset].push_back(die_offset); } // Now for each bucket we write the bucket value which is the // number of hashes and the hash index encoded into a single // 32 bit unsigned integer. for (i = 0; i < header.bucket_count; ++i) { HashToHashData &bucket_entry = hash_buckets[i]; if (bucket_entry.empty()) { // Empty bucket ++bucket_entry_empties; hash_indexes[i] = UINT32_MAX; } else { const uint32_t hash_value_index = hash_values.size(); uint32_t hash_count = 0; typename HashToHashData::const_iterator pos, end = bucket_entry.end(); for (pos = bucket_entry.begin(); pos != end; ++pos) { hash_values.push_back(pos->first); hash_offsets.push_back(GetByteSize(pos->second)); ++hash_count; } hash_indexes[i] = hash_value_index; } } header.hashes_count = hash_values.size(); // Write the header out now that we have the hash_count header.Write(ostrm); // Now for each bucket we write the start index of the hashes // for the current bucket, or UINT32_MAX if the bucket is empty for (i = 0; i < header.bucket_count; ++i) { ostrm.PutHex32(hash_indexes[i]); } // Now we need to write out all of the hash values for (i = 0; i < header.hashes_count; ++i) { ostrm.PutHex32(hash_values[i]); } // Now we need to write out all of the hash data offsets, // there is an offset for each hash in the hashes array // that was written out above for (i = 0; i < header.hashes_count; ++i) { ostrm.PutHex32(hash_offsets[i]); } // Now we write the data for each hash and verify we got the offset // correct above... for (i = 0; i < header.bucket_count; ++i) { HashToHashData &bucket_entry = hash_buckets[i]; typename HashToHashData::const_iterator pos, end = bucket_entry.end(); for (pos = bucket_entry.begin(); pos != end; ++pos) { if (!bucket_entry.empty()) { WriteHashData(pos->second); } } } } protected: typedef std::vector collection; collection m_entries; }; // A class for reading and using a saved hash table from a block of data // in memory template class MemoryTable { public: typedef __HeaderType HeaderType; typedef __KeyType KeyType; typedef __HashData HashData; enum Result { eResultKeyMatch = 0u, // The entry was found, key matched and "pair" was // filled in successfully eResultKeyMismatch = 1u, // Bucket hash data collision, but key didn't match eResultEndOfHashData = 2u, // The chain of items for this hash data in // this bucket is terminated, search no more eResultError = 3u // Error parsing the hash data, abort }; struct Pair { KeyType key; HashData value; }; MemoryTable(lldb_private::DataExtractor &data) : m_header(), m_hash_indexes(nullptr), m_hash_values(nullptr), m_hash_offsets(nullptr) { lldb::offset_t offset = m_header.Read(data, 0); if (offset != LLDB_INVALID_OFFSET && IsValid()) { m_hash_indexes = (const uint32_t *)data.GetData( &offset, m_header.bucket_count * sizeof(uint32_t)); m_hash_values = (const uint32_t *)data.GetData( &offset, m_header.hashes_count * sizeof(uint32_t)); m_hash_offsets = (const uint32_t *)data.GetData( &offset, m_header.hashes_count * sizeof(uint32_t)); } } virtual ~MemoryTable() = default; bool IsValid() const { return m_header.version == 1 && m_header.hash_function == eHashFunctionDJB && m_header.bucket_count > 0 && m_header.hashes_count > 0; } uint32_t GetHashIndex(uint32_t bucket_idx) const { if (m_hash_indexes && bucket_idx < m_header.bucket_count) return m_hash_indexes[bucket_idx]; return UINT32_MAX; } uint32_t GetHashValue(uint32_t hash_idx) const { if (m_hash_values && hash_idx < m_header.hashes_count) return m_hash_values[hash_idx]; return UINT32_MAX; } uint32_t GetHashDataOffset(uint32_t hash_idx) const { if (m_hash_offsets && hash_idx < m_header.hashes_count) return m_hash_offsets[hash_idx]; return UINT32_MAX; } bool Find(const char *name, Pair &pair) const { if (!name || !name[0]) return false; if (IsValid()) { const uint32_t bucket_count = m_header.bucket_count; const uint32_t hash_count = m_header.hashes_count; const uint32_t hash_value = MappedHash::HashString(m_header.hash_function, name); const uint32_t bucket_idx = hash_value % bucket_count; uint32_t hash_idx = GetHashIndex(bucket_idx); if (hash_idx < hash_count) { for (; hash_idx < hash_count; ++hash_idx) { const uint32_t curr_hash_value = GetHashValue(hash_idx); if (curr_hash_value == hash_value) { lldb::offset_t hash_data_offset = GetHashDataOffset(hash_idx); while (hash_data_offset != UINT32_MAX) { const lldb::offset_t prev_hash_data_offset = hash_data_offset; Result hash_result = GetHashDataForName(name, &hash_data_offset, pair); // Check the result of getting our hash data switch (hash_result) { case eResultKeyMatch: return true; case eResultKeyMismatch: if (prev_hash_data_offset == hash_data_offset) return false; break; case eResultEndOfHashData: // The last HashData for this key has been reached, stop // searching return false; case eResultError: // Error parsing the hash data, abort return false; } } } if ((curr_hash_value % bucket_count) != bucket_idx) break; } } } return false; } // This method must be implemented in any subclasses. // The KeyType is user specified and must somehow result in a string // value. For example, the KeyType might be a string offset in a string // table and subclasses can store their string table as a member of the // subclass and return a valie "const char *" given a "key". The value // could also be a C string pointer, in which case just returning "key" // will suffice. virtual const char *GetStringForKeyType(KeyType key) const = 0; virtual bool ReadHashData(uint32_t hash_data_offset, HashData &hash_data) const = 0; // This method must be implemented in any subclasses and it must try to // read one "Pair" at the offset pointed to by the "hash_data_offset_ptr" // parameter. This offset should be updated as bytes are consumed and // a value "Result" enum should be returned. If the "name" matches the // full name for the "pair.key" (which must be filled in by this call), // then the HashData in the pair ("pair.value") should be extracted and // filled in and "eResultKeyMatch" should be returned. If "name" doesn't // match this string for the key, then "eResultKeyMismatch" should be // returned and all data for the current HashData must be consumed or // skipped and the "hash_data_offset_ptr" offset needs to be updated to // point to the next HashData. If the end of the HashData objects for // a given hash value have been reached, then "eResultEndOfHashData" // should be returned. If anything else goes wrong during parsing, // return "eResultError" and the corresponding "Find()" function will // be canceled and return false. virtual Result GetHashDataForName(const char *name, lldb::offset_t *hash_data_offset_ptr, Pair &pair) const = 0; const HeaderType &GetHeader() { return m_header; } void ForEach( std::function const &callback) const { const size_t num_hash_offsets = m_header.hashes_count; for (size_t i = 0; i < num_hash_offsets; ++i) { uint32_t hash_data_offset = GetHashDataOffset(i); if (hash_data_offset != UINT32_MAX) { HashData hash_data; if (ReadHashData(hash_data_offset, hash_data)) { // If the callback returns false, then we are done and should stop if (callback(hash_data) == false) return; } } } } protected: // Implementation agnostic information HeaderType m_header; const uint32_t *m_hash_indexes; const uint32_t *m_hash_values; const uint32_t *m_hash_offsets; }; }; #endif // liblldb_MappedHash_h_ Index: vendor/lldb/dist/include/lldb/Host/Editline.h =================================================================== --- vendor/lldb/dist/include/lldb/Host/Editline.h (revision 311541) +++ vendor/lldb/dist/include/lldb/Host/Editline.h (revision 311542) @@ -1,365 +1,367 @@ //===-- Editline.h ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // TODO: wire up window size changes // If we ever get a private copy of libedit, there are a number of defects that // would be nice to fix; // a) Sometimes text just disappears while editing. In an 80-column editor // paste the following text, without // the quotes: // "This is a test of the input system missing Hello, World! Do you // disappear when it gets to a particular length?" // Now press ^A to move to the start and type 3 characters, and you'll see a // good amount of the text will // disappear. It's still in the buffer, just invisible. // b) The prompt printing logic for dealing with ANSI formatting characters is // broken, which is why we're // working around it here. // c) When resizing the terminal window, if the cursor moves between rows // libedit will get confused. // d) The incremental search uses escape to cancel input, so it's confused by // ANSI sequences starting with escape. // e) Emoji support is fairly terrible, presumably it doesn't understand // composed characters? #ifndef liblldb_Editline_h_ #define liblldb_Editline_h_ #if defined(__cplusplus) #include #include #include // components needed to handle wide characters ( , codecvt_utf8, // libedit built with '--enable-widec' ) // are available on some platforms. The wchar_t versions of libedit functions // will only be // used in cases where this is true. This is a compile time dependecy, for now // selected per target Platform #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) #define LLDB_EDITLINE_USE_WCHAR 1 #include #else #define LLDB_EDITLINE_USE_WCHAR 0 #endif #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/lldb-private.h" #if defined(_WIN32) #include "lldb/Host/windows/editlinewin.h" #elif !defined(__ANDROID__) #include #endif #include #include #include #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/Predicate.h" namespace lldb_private { namespace line_editor { // type alias's to help manage 8 bit and wide character versions of libedit #if LLDB_EDITLINE_USE_WCHAR using EditLineStringType = std::wstring; using EditLineStringStreamType = std::wstringstream; using EditLineCharType = wchar_t; #else using EditLineStringType = std::string; using EditLineStringStreamType = std::stringstream; using EditLineCharType = char; #endif typedef int (*EditlineGetCharCallbackType)(::EditLine *editline, EditLineCharType *c); typedef unsigned char (*EditlineCommandCallbackType)(::EditLine *editline, int ch); typedef const char *(*EditlinePromptCallbackType)(::EditLine *editline); class EditlineHistory; typedef std::shared_ptr EditlineHistorySP; typedef bool (*IsInputCompleteCallbackType)(Editline *editline, StringList &lines, void *baton); typedef int (*FixIndentationCallbackType)(Editline *editline, const StringList &lines, int cursor_position, void *baton); typedef int (*CompleteCallbackType)(const char *current_line, const char *cursor, const char *last_char, int skip_first_n_matches, int max_matches, StringList &matches, void *baton); /// Status used to decide when and how to start editing another line in /// multi-line sessions enum class EditorStatus { /// The default state proceeds to edit the current line Editing, /// Editing complete, returns the complete set of edited lines Complete, /// End of input reported EndOfInput, /// Editing interrupted Interrupted }; /// Established locations that can be easily moved among with MoveCursor enum class CursorLocation { /// The start of the first line in a multi-line edit session BlockStart, /// The start of the current line in a multi-line edit session EditingPrompt, /// The location of the cursor on the current line in a multi-line edit /// session EditingCursor, /// The location immediately after the last character in a multi-line edit /// session BlockEnd }; } using namespace line_editor; /// Instances of Editline provide an abstraction over libedit's EditLine /// facility. Both /// single- and multi-line editing are supported. class Editline { public: Editline(const char *editor_name, FILE *input_file, FILE *output_file, FILE *error_file, bool color_prompts); ~Editline(); /// Uses the user data storage of EditLine to retrieve an associated instance /// of Editline. static Editline *InstanceFor(::EditLine *editline); /// Sets a string to be used as a prompt, or combined with a line number to /// form a prompt. void SetPrompt(const char *prompt); /// Sets an alternate string to be used as a prompt for the second line and /// beyond in multi-line /// editing scenarios. void SetContinuationPrompt(const char *continuation_prompt); /// Required to update the width of the terminal registered for I/O. It is /// critical that this /// be correct at all times. void TerminalSizeChanged(); /// Returns the prompt established by SetPrompt() const char *GetPrompt(); /// Returns the index of the line currently being edited uint32_t GetCurrentLine(); /// Interrupt the current edit as if ^C was pressed bool Interrupt(); /// Cancel this edit and oblitarate all trace of it bool Cancel(); /// Register a callback for the tab key void SetAutoCompleteCallback(CompleteCallbackType callback, void *baton); /// Register a callback for testing whether multi-line input is complete void SetIsInputCompleteCallback(IsInputCompleteCallbackType callback, void *baton); /// Register a callback for determining the appropriate indentation for a line /// when creating a newline. An optional set of insertable characters can /// also /// trigger the callback. bool SetFixIndentationCallback(FixIndentationCallbackType callback, void *baton, const char *indent_chars); /// Prompts for and reads a single line of user input. bool GetLine(std::string &line, bool &interrupted); /// Prompts for and reads a multi-line batch of user input. bool GetLines(int first_line_number, StringList &lines, bool &interrupted); void PrintAsync(Stream *stream, const char *s, size_t len); private: /// Sets the lowest line number for multi-line editing sessions. A value of /// zero suppresses /// line number printing in the prompt. void SetBaseLineNumber(int line_number); /// Returns the complete prompt by combining the prompt or continuation prompt /// with line numbers /// as appropriate. The line index is a zero-based index into the current /// multi-line session. std::string PromptForIndex(int line_index); /// Sets the current line index between line edits to allow free movement /// between lines. Updates /// the prompt to match. void SetCurrentLine(int line_index); /// Determines the width of the prompt in characters. The width is guaranteed /// to be the same for /// all lines of the current multi-line session. int GetPromptWidth(); /// Returns true if the underlying EditLine session's keybindings are /// Emacs-based, or false if /// they are VI-based. bool IsEmacs(); /// Returns true if the current EditLine buffer contains nothing but spaces, /// or is empty. bool IsOnlySpaces(); /// Helper method used by MoveCursor to determine relative line position. int GetLineIndexForLocation(CursorLocation location, int cursor_row); /// Move the cursor from one well-established location to another using /// relative line positioning /// and absolute column positioning. void MoveCursor(CursorLocation from, CursorLocation to); /// Clear from cursor position to bottom of screen and print input lines /// including prompts, optionally /// starting from a specific line. Lines are drawn with an extra space at the /// end to reserve room for /// the rightmost cursor position. void DisplayInput(int firstIndex = 0); /// Counts the number of rows a given line of content will end up occupying, /// taking into account both /// the preceding prompt and a single trailing space occupied by a cursor when /// at the end of the line. int CountRowsForLine(const EditLineStringType &content); /// Save the line currently being edited void SaveEditedLine(); /// Convert the current input lines into a UTF8 StringList StringList GetInputAsStringList(int line_count = UINT32_MAX); /// Replaces the current multi-line session with the next entry from history. /// When the parameter is /// true it will take the next earlier entry from history, when it is false it /// takes the next most /// recent. unsigned char RecallHistory(bool earlier); /// Character reading implementation for EditLine that supports our multi-line /// editing trickery. int GetCharacter(EditLineCharType *c); /// Prompt implementation for EditLine. const char *Prompt(); /// Line break command used when meta+return is pressed in multi-line mode. unsigned char BreakLineCommand(int ch); /// Command used when return is pressed in multi-line mode. unsigned char EndOrAddLineCommand(int ch); /// Delete command used when delete is pressed in multi-line mode. unsigned char DeleteNextCharCommand(int ch); /// Delete command used when backspace is pressed in multi-line mode. unsigned char DeletePreviousCharCommand(int ch); /// Line navigation command used when ^P or up arrow are pressed in multi-line /// mode. unsigned char PreviousLineCommand(int ch); /// Line navigation command used when ^N or down arrow are pressed in /// multi-line mode. unsigned char NextLineCommand(int ch); /// History navigation command used when Alt + up arrow is pressed in /// multi-line mode. unsigned char PreviousHistoryCommand(int ch); /// History navigation command used when Alt + down arrow is pressed in /// multi-line mode. unsigned char NextHistoryCommand(int ch); /// Buffer start command used when Esc < is typed in multi-line emacs mode. unsigned char BufferStartCommand(int ch); /// Buffer end command used when Esc > is typed in multi-line emacs mode. unsigned char BufferEndCommand(int ch); /// Context-sensitive tab insertion or code completion command used when the /// tab key is typed. unsigned char TabCommand(int ch); /// Respond to normal character insertion by fixing line indentation unsigned char FixIndentationCommand(int ch); /// Revert line command used when moving between lines. unsigned char RevertLineCommand(int ch); /// Ensures that the current EditLine instance is properly configured for /// single or multi-line editing. void ConfigureEditor(bool multiline); + bool CompleteCharacter(char ch, EditLineCharType &out); + private: #if LLDB_EDITLINE_USE_WCHAR std::wstring_convert> m_utf8conv; #endif ::EditLine *m_editline = nullptr; EditlineHistorySP m_history_sp; bool m_in_history = false; std::vector m_live_history_lines; bool m_multiline_enabled = false; std::vector m_input_lines; EditorStatus m_editor_status; bool m_color_prompts = true; int m_terminal_width = 0; int m_base_line_number = 0; unsigned m_current_line_index = 0; int m_current_line_rows = -1; int m_revert_cursor_index = 0; int m_line_number_digits = 3; std::string m_set_prompt; std::string m_set_continuation_prompt; std::string m_current_prompt; bool m_needs_prompt_repaint = false; std::string m_editor_name; FILE *m_input_file; FILE *m_output_file; FILE *m_error_file; ConnectionFileDescriptor m_input_connection; IsInputCompleteCallbackType m_is_input_complete_callback = nullptr; void *m_is_input_complete_callback_baton = nullptr; FixIndentationCallbackType m_fix_indentation_callback = nullptr; void *m_fix_indentation_callback_baton = nullptr; const char *m_fix_indentation_callback_chars = nullptr; CompleteCallbackType m_completion_callback = nullptr; void *m_completion_callback_baton = nullptr; std::mutex m_output_mutex; }; } #endif // #if defined(__cplusplus) #endif // liblldb_Editline_h_ Index: vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiEnvironmentCd.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiEnvironmentCd.py (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiEnvironmentCd.py (revision 311542) @@ -0,0 +1,37 @@ +""" +Test lldb-mi -environment-cd command. +""" + +from __future__ import print_function + + +import lldbmi_testcase +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class MiEnvironmentCdTestCase(lldbmi_testcase.MiTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @skipIfDarwin # Disabled while I investigate the failure on buildbot. + def test_lldbmi_environment_cd(self): + """Test that 'lldb-mi --interpreter' changes working directory for inferior.""" + + self.spawnLldbMi(args=None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # cd to a different directory + self.runCmd("-environment-cd /tmp") + self.expect("\^done") + + # Run to the end + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("@\"cwd: /tmp\\r\\n\"", exactly=True) Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiEnvironmentCd.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiGdbSetShow.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiGdbSetShow.py (revision 311541) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/TestMiGdbSetShow.py (revision 311542) @@ -1,210 +1,251 @@ """ Test lldb-mi -gdb-set and -gdb-show commands. """ from __future__ import print_function import unittest2 import lldbmi_testcase from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class MiGdbSetShowTestCase(lldbmi_testcase.MiTestCaseBase): mydir = TestBase.compute_mydir(__file__) @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr22274: need a pexpect replacement for windows") @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_gdb_set_target_async_default(self): """Test that 'lldb-mi --interpreter' switches to async mode by default.""" self.spawnLldbMi(args=None) # Switch to sync mode self.runCmd("-gdb-set target-async off") self.expect("\^done") self.runCmd("-gdb-show target-async") self.expect("\^done,value=\"off\"") # Test that -gdb-set switches to async by default self.runCmd("-gdb-set target-async") self.expect("\^done") self.runCmd("-gdb-show target-async") self.expect("\^done,value=\"on\"") @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr22274: need a pexpect replacement for windows") @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @expectedFlakeyLinux("llvm.org/pr26028") # Fails in ~1% of cases def test_lldbmi_gdb_set_target_async_on(self): """Test that 'lldb-mi --interpreter' can execute commands in async mode.""" self.spawnLldbMi(args=None) # Switch to sync mode self.runCmd("-gdb-set target-async off") self.expect("\^done") self.runCmd("-gdb-show target-async") self.expect("\^done,value=\"off\"") # Test that -gdb-set can switch to async mode self.runCmd("-gdb-set target-async on") self.expect("\^done") self.runCmd("-gdb-show target-async") self.expect("\^done,value=\"on\"") # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Test that program is executed in async mode self.runCmd("-exec-run") self.expect("\*running") self.expect("@\"argc=1") @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr22274: need a pexpect replacement for windows") @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @expectedFailureAll( oslist=["linux"], bugnumber="Failing in ~11/600 dosep runs (build 3120-3122)") def test_lldbmi_gdb_set_target_async_off(self): """Test that 'lldb-mi --interpreter' can execute commands in sync mode.""" self.spawnLldbMi(args=None) # Test that -gdb-set can switch to sync mode self.runCmd("-gdb-set target-async off") self.expect("\^done") self.runCmd("-gdb-show target-async") self.expect("\^done,value=\"off\"") # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Test that program is executed in async mode self.runCmd("-exec-run") unexpected = ["\*running"] # "\*running" is async notification it = self.expect(unexpected + ["@\"argc=1\\\\r\\\\n"]) if it < len(unexpected): self.fail("unexpected found: %s" % unexpected[it]) @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr22274: need a pexpect replacement for windows") @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_gdb_show_target_async(self): """Test that 'lldb-mi --interpreter' in async mode by default.""" self.spawnLldbMi(args=None) # Test that default target-async value is "on" self.runCmd("-gdb-show target-async") self.expect("\^done,value=\"on\"") @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr22274: need a pexpect replacement for windows") @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_gdb_show_language(self): """Test that 'lldb-mi --interpreter' can get current language.""" self.spawnLldbMi(args=None) # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Run to main self.runCmd("-break-insert -f main") self.expect("\^done,bkpt={number=\"1\"") self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Test that -gdb-show language gets current language self.runCmd("-gdb-show language") self.expect("\^done,value=\"c\+\+\"") @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr22274: need a pexpect replacement for windows") @unittest2.expectedFailure("-gdb-set ignores unknown properties") def test_lldbmi_gdb_set_unknown(self): """Test that 'lldb-mi --interpreter' fails when setting an unknown property.""" self.spawnLldbMi(args=None) # Test that -gdb-set fails if property is unknown self.runCmd("-gdb-set unknown some_value") self.expect("\^error") @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr22274: need a pexpect replacement for windows") @unittest2.expectedFailure("-gdb-show ignores unknown properties") def test_lldbmi_gdb_show_unknown(self): """Test that 'lldb-mi --interpreter' fails when showing an unknown property.""" self.spawnLldbMi(args=None) # Test that -gdb-show fails if property is unknown self.runCmd("-gdb-show unknown") self.expect("\^error") @expectedFailureAll( oslist=["windows"], bugnumber="llvm.org/pr22274: need a pexpect replacement for windows") @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @skipIfLinux # llvm.org/pr22841: lldb-mi tests fail on all Linux buildbots def test_lldbmi_gdb_set_ouptut_radix(self): """Test that 'lldb-mi --interpreter' works for -gdb-set output-radix.""" self.spawnLldbMi(args=None) # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Run to BP_printf line = line_number('main.cpp', '// BP_printf') self.runCmd("-break-insert main.cpp:%d" % line) self.expect("\^done,bkpt={number=\"1\"") self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Setup variable self.runCmd("-var-create var_a * a") self.expect( "\^done,name=\"var_a\",numchild=\"0\",value=\"10\",type=\"int\",thread-id=\"1\",has_more=\"0\"") # Test default output self.runCmd("-var-evaluate-expression var_a") self.expect("\^done,value=\"10\"") # Test hex output self.runCmd("-gdb-set output-radix 16") self.expect("\^done") self.runCmd("-var-evaluate-expression var_a") self.expect("\^done,value=\"0xa\"") # Test octal output self.runCmd("-gdb-set output-radix 8") self.expect("\^done") self.runCmd("-var-evaluate-expression var_a") self.expect("\^done,value=\"012\"") # Test decimal output self.runCmd("-gdb-set output-radix 10") self.expect("\^done") self.runCmd("-var-evaluate-expression var_a") self.expect("\^done,value=\"10\"") + + @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows + @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races + @expectedFailureAll( + bugnumber="llvm.org/pr31485: data-disassemble doesn't follow flavor settings") + def test_lldbmi_gdb_set_disassembly_flavor(self): + """Test that 'lldb-mi --interpreter' works for -gdb-set disassembly-flavor.""" + + self.spawnLldbMi(args=None) + + # Load executable + self.runCmd("-file-exec-and-symbols %s" % self.myexe) + self.expect("\^done") + + # Run to BP_printf + line = line_number('main.cpp', '// BP_printf') + self.runCmd("-break-insert main.cpp:%d" % line) + self.expect("\^done,bkpt={number=\"1\"") + self.runCmd("-exec-run") + self.expect("\^running") + self.expect("\*stopped,reason=\"breakpoint-hit\".+addr=\"(0x[0-9a-f]+)\"") + + # Get starting and ending address from $pc + pc = int(self.child.match.group(1), base=16) + s_addr, e_addr = pc, pc + 1 + + # Test default output (att) + self.runCmd("-data-disassemble -s %d -e %d -- 0" % (s_addr, e_addr)) + self.expect("movl ") + + # Test intel style + self.runCmd("-gdb-set disassembly-flavor intel") + self.expect("\^done") + self.runCmd("-data-disassemble -s %d -e %d -- 0" % (s_addr, e_addr)) + self.expect("mov ") + + # Test AT&T style + self.runCmd("-gdb-set disassembly-flavor intel") + self.expect("\^done") + self.runCmd("-data-disassemble -s %d -e %d -- 0" % (s_addr, e_addr)) + self.expect("movl ") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/control/TestMiExec.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/control/TestMiExec.py (revision 311541) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/control/TestMiExec.py (revision 311542) @@ -1,484 +1,485 @@ """ Test lldb-mi -exec-xxx commands. """ from __future__ import print_function import lldbmi_testcase from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class MiExecTestCase(lldbmi_testcase.MiTestCaseBase): mydir = TestBase.compute_mydir(__file__) @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @expectedFailureAll( oslist=["linux"], bugnumber="llvm.org/pr25000: lldb-mi does not receive broadcasted notification from Core/Process about process stopped") def test_lldbmi_exec_run(self): """Test that 'lldb-mi --interpreter' can stop at entry.""" self.spawnLldbMi(args=None) # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Test that program is stopped at entry self.runCmd("-exec-run --start") self.expect("\^running") self.expect( "\*stopped,reason=\"signal-received\",signal-name=\"SIGSTOP\",signal-meaning=\"Stop\",.*?thread-id=\"1\",stopped-threads=\"all\"") # Test that lldb-mi is ready to execute next commands self.expect(self.child_prompt, exactly=True) @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_exec_abort(self): """Test that 'lldb-mi --interpreter' works for -exec-abort.""" self.spawnLldbMi(args=None) # Test that -exec-abort fails on invalid process self.runCmd("-exec-abort") self.expect( "\^error,msg=\"Command 'exec-abort'\. Invalid process during debug session\"") # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Set arguments self.runCmd("-exec-arguments arg1") self.expect("\^done") # Run to main self.runCmd("-break-insert -f main") self.expect("\^done,bkpt={number=\"1\"") self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Test that arguments were passed self.runCmd("-data-evaluate-expression argc") self.expect("\^done,value=\"2\"") # Test that program may be aborted self.runCmd("-exec-abort") self.expect("\^done") self.expect("\*stopped,reason=\"exited-normally\"") # Test that program can be run again self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Test that arguments were passed again self.runCmd("-data-evaluate-expression argc") self.expect("\^done,value=\"2\"") # Test that program may be aborted again self.runCmd("-exec-abort") self.expect("\^done") self.expect("\*stopped,reason=\"exited-normally\"") @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_exec_arguments_set(self): """Test that 'lldb-mi --interpreter' can pass args using -exec-arguments.""" self.spawnLldbMi(args=None) # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Set arguments self.runCmd( "-exec-arguments --arg1 \"2nd arg\" third_arg fourth=\"4th arg\"") self.expect("\^done") # Run to main self.runCmd("-break-insert -f main") self.expect("\^done,bkpt={number=\"1\"") self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Check argc and argv to see if arg passed + # Note that exactly=True is needed to avoid extra escaping for re self.runCmd("-data-evaluate-expression argc") self.expect("\^done,value=\"5\"") #self.runCmd("-data-evaluate-expression argv[1]") # self.expect("\^done,value=\"--arg1\"") self.runCmd("-interpreter-exec command \"print argv[1]\"") - self.expect("\"--arg1\"") + self.expect("\\\"--arg1\\\"", exactly=True) #self.runCmd("-data-evaluate-expression argv[2]") #self.expect("\^done,value=\"2nd arg\"") self.runCmd("-interpreter-exec command \"print argv[2]\"") - self.expect("\"2nd arg\"") + self.expect("\\\"2nd arg\\\"", exactly=True) #self.runCmd("-data-evaluate-expression argv[3]") # self.expect("\^done,value=\"third_arg\"") self.runCmd("-interpreter-exec command \"print argv[3]\"") - self.expect("\"third_arg\"") + self.expect("\\\"third_arg\\\"", exactly=True) #self.runCmd("-data-evaluate-expression argv[4]") #self.expect("\^done,value=\"fourth=\\\\\\\"4th arg\\\\\\\"\"") self.runCmd("-interpreter-exec command \"print argv[4]\"") - self.expect("\"fourth=\\\\\\\"4th arg\\\\\\\"\"") + self.expect("\\\"fourth=\\\\\\\"4th arg\\\\\\\"\\\"", exactly=True) @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_exec_arguments_reset(self): """Test that 'lldb-mi --interpreter' can reset previously set args using -exec-arguments.""" self.spawnLldbMi(args=None) # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Set arguments self.runCmd("-exec-arguments arg1") self.expect("\^done") self.runCmd("-exec-arguments") self.expect("\^done") # Run to main self.runCmd("-break-insert -f main") self.expect("\^done,bkpt={number=\"1\"") self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Check argc to see if arg passed self.runCmd("-data-evaluate-expression argc") self.expect("\^done,value=\"1\"") @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_exec_next(self): """Test that 'lldb-mi --interpreter' works for stepping.""" self.spawnLldbMi(args=None) # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Run to main self.runCmd("-break-insert -f main") self.expect("\^done,bkpt={number=\"1\"") self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Warning: the following is sensitive to the lines in the source # Test -exec-next self.runCmd("-exec-next --thread 1 --frame 0") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"29\"") # Test that --thread is optional self.runCmd("-exec-next --frame 0") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"30\"") # Test that --frame is optional self.runCmd("-exec-next --thread 1") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"31\"") # Test that both --thread and --frame are optional self.runCmd("-exec-next") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"32\"") # Test that an invalid --thread is handled self.runCmd("-exec-next --thread 0") self.expect("\^error,message=\"error: Thread index 0 is out of range") self.runCmd("-exec-next --thread 10") self.expect("\^error,message=\"error: Thread index 10 is out of range") # Test that an invalid --frame is handled # FIXME: no error is returned self.runCmd("-exec-next --frame 10") #self.expect("\^error: Frame index 10 is out of range") @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_exec_next_instruction(self): """Test that 'lldb-mi --interpreter' works for instruction stepping.""" self.spawnLldbMi(args=None) # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Run to main self.runCmd("-break-insert -f main") self.expect("\^done,bkpt={number=\"1\"") self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Warning: the following is sensitive to the lines in the # source and optimizations # Test -exec-next-instruction self.runCmd("-exec-next-instruction --thread 1 --frame 0") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"28\"") # Test that --thread is optional self.runCmd("-exec-next-instruction --frame 0") self.expect("\^running") # Depending on compiler, it can stop at different line self.expect( "\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"(28|29)\"") # Test that --frame is optional self.runCmd("-exec-next-instruction --thread 1") self.expect("\^running") # Depending on compiler, it can stop at different line self.expect( "\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"(28|29|30)\"") # Test that both --thread and --frame are optional self.runCmd("-exec-next-instruction") self.expect("\^running") # Depending on compiler, it can stop at different line self.expect( "\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"(29|30|31)\"") # Test that an invalid --thread is handled self.runCmd("-exec-next-instruction --thread 0") self.expect("\^error,message=\"error: Thread index 0 is out of range") self.runCmd("-exec-next-instruction --thread 10") self.expect("\^error,message=\"error: Thread index 10 is out of range") # Test that an invalid --frame is handled # FIXME: no error is returned self.runCmd("-exec-next-instruction --frame 10") #self.expect("\^error: Frame index 10 is out of range") @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_exec_step(self): """Test that 'lldb-mi --interpreter' works for stepping into.""" self.spawnLldbMi(args=None) # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Run to main self.runCmd("-break-insert -f main") self.expect("\^done,bkpt={number=\"1\"") self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Warning: the following is sensitive to the lines in the source # Test that -exec-step steps into (or not) printf depending on debug info # Note that message is different in Darwin and Linux: # Darwin: "*stopped,reason=\"end-stepping-range\",frame={addr=\"0x[0-9a-f]+\",func=\"main\",args=[{name=\"argc\",value=\"1\"},{name=\"argv\",value="0x[0-9a-f]+\"}],file=\"main.cpp\",fullname=\".+main.cpp\",line=\"\d\"},thread-id=\"1\",stopped-threads=\"all\" # Linux: # "*stopped,reason=\"end-stepping-range\",frame={addr="0x[0-9a-f]+\",func=\"__printf\",args=[{name=\"format\",value=\"0x[0-9a-f]+\"}],file=\"printf.c\",fullname=\".+printf.c\",line="\d+"},thread-id=\"1\",stopped-threads=\"all\" self.runCmd("-exec-step --thread 1 --frame 0") self.expect("\^running") it = self.expect(["\*stopped,reason=\"end-stepping-range\".+?func=\"main\"", "\*stopped,reason=\"end-stepping-range\".+?func=\"(?!main).+?\""]) # Exit from printf if needed if it == 1: self.runCmd("-exec-finish") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?func=\"main\"") # Test that -exec-step steps into g_MyFunction and back out # (and that --thread is optional) self.runCmd("-exec-step --frame 0") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?func=\"g_MyFunction.*?\"") # Use -exec-finish here to make sure that control reaches the caller. # -exec-step can keep us in the g_MyFunction for gcc self.runCmd("-exec-finish --frame 0") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"30\"") # Test that -exec-step steps into s_MyFunction # (and that --frame is optional) self.runCmd("-exec-step --thread 1") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?func=\".*?s_MyFunction.*?\"") # Test that -exec-step steps into g_MyFunction from inside # s_MyFunction (and that both --thread and --frame are optional) self.runCmd("-exec-step") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?func=\"g_MyFunction.*?\"") # Test that an invalid --thread is handled self.runCmd("-exec-step --thread 0") self.expect("\^error,message=\"error: Thread index 0 is out of range") self.runCmd("-exec-step --thread 10") self.expect("\^error,message=\"error: Thread index 10 is out of range") # Test that an invalid --frame is handled # FIXME: no error is returned self.runCmd("-exec-step --frame 10") #self.expect("\^error: Frame index 10 is out of range") @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_exec_step_instruction(self): """Test that 'lldb-mi --interpreter' works for instruction stepping into.""" self.spawnLldbMi(args=None) # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Warning: the following is sensitive to the lines in the # source and optimizations # Run to main self.runCmd("-break-insert -f main") self.expect("\^done,bkpt={number=\"1\"") self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Test that -exec-next steps over printf self.runCmd("-exec-next --thread 1 --frame 0") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?main\.cpp\",line=\"29\"") # Test that -exec-step-instruction steps over non branching # instruction self.runCmd("-exec-step-instruction --thread 1 --frame 0") self.expect("\^running") self.expect("\*stopped,reason=\"end-stepping-range\".+?main\.cpp\"") # Test that -exec-step-instruction steps into g_MyFunction # instruction (and that --thread is optional) self.runCmd("-exec-step-instruction --frame 0") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?func=\"g_MyFunction.*?\"") # Test that -exec-step-instruction steps over non branching # (and that --frame is optional) self.runCmd("-exec-step-instruction --thread 1") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?func=\"g_MyFunction.*?\"") # Test that -exec-step-instruction steps into g_MyFunction # (and that both --thread and --frame are optional) self.runCmd("-exec-step-instruction") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?func=\"g_MyFunction.*?\"") # Test that an invalid --thread is handled self.runCmd("-exec-step-instruction --thread 0") self.expect("\^error,message=\"error: Thread index 0 is out of range") self.runCmd("-exec-step-instruction --thread 10") self.expect("\^error,message=\"error: Thread index 10 is out of range") # Test that an invalid --frame is handled # FIXME: no error is returned self.runCmd("-exec-step-instruction --frame 10") #self.expect("\^error: Frame index 10 is out of range") @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_exec_finish(self): """Test that 'lldb-mi --interpreter' works for -exec-finish.""" self.spawnLldbMi(args=None) # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Set BP at g_MyFunction and run to BP self.runCmd("-break-insert -f g_MyFunction") self.expect("\^done,bkpt={number=\"1\"") self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Test that -exec-finish returns from g_MyFunction self.runCmd("-exec-finish --thread 1 --frame 0") self.expect("\^running") self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"main\"") # Run to BP inside s_MyFunction call self.runCmd("-break-insert s_MyFunction") self.expect("\^done,bkpt={number=\"2\"") self.runCmd("-exec-continue") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Test that -exec-finish hits BP at g_MyFunction call inside # s_MyFunction (and that --thread is optional) self.runCmd("-exec-finish --frame 0") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Test that -exec-finish returns from g_MyFunction call inside # s_MyFunction (and that --frame is optional) self.runCmd("-exec-finish --thread 1") self.expect("\^running") self.expect( "\*stopped,reason=\"end-stepping-range\".+?func=\".*?s_MyFunction.*?\"") # Test that -exec-finish returns from s_MyFunction # (and that both --thread and --frame are optional) self.runCmd("-exec-finish") self.expect("\^running") self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"main\"") # Test that an invalid --thread is handled self.runCmd("-exec-finish --thread 0") self.expect("\^error,message=\"error: Thread index 0 is out of range") self.runCmd("-exec-finish --thread 10") self.expect("\^error,message=\"error: Thread index 10 is out of range") # Test that an invalid --frame is handled # FIXME: no error is returned #self.runCmd("-exec-finish --frame 10") #self.expect("\^error: Frame index 10 is out of range") # Set BP at printf and run to BP self.runCmd("-break-insert -f printf") self.expect("\^done,bkpt={number=\"3\"") self.runCmd("-exec-continue") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Test that -exec-finish returns from printf self.runCmd("-exec-finish --thread 1 --frame 0") self.expect("\^running") self.expect("\*stopped,reason=\"end-stepping-range\".+?func=\"main\"") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/main.cpp =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/main.cpp (revision 311541) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/main.cpp (revision 311542) @@ -1,19 +1,33 @@ //===-- main.cpp ------------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include +#ifdef _WIN32 + #include + #define getcwd _getcwd // suppress "deprecation" warning +#else + #include +#endif + int main(int argc, char const *argv[]) { - int a = 10; + int a = 10; + char buf[512]; + char *ans = getcwd(buf, sizeof(buf)); + if (ans) { + printf("cwd: %s\n", ans); + } + printf("argc=%d\n", argc); // BP_printf + return 0; } Index: vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/syntax/TestMiSyntax.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/syntax/TestMiSyntax.py (revision 311541) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-mi/syntax/TestMiSyntax.py (revision 311542) @@ -1,188 +1,189 @@ """ Test that the lldb-mi driver understands MI command syntax. """ from __future__ import print_function import lldbmi_testcase from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil from functools import reduce class MiSyntaxTestCase(lldbmi_testcase.MiTestCaseBase): mydir = TestBase.compute_mydir(__file__) @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_tokens(self): """Test that 'lldb-mi --interpreter' prints command tokens.""" self.spawnLldbMi(args=None) # Load executable self.runCmd("000-file-exec-and-symbols %s" % self.myexe) self.expect("000\^done") # Run to main self.runCmd("100000001-break-insert -f main") self.expect("100000001\^done,bkpt={number=\"1\"") self.runCmd("2-exec-run") self.expect("2\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") # Exit self.runCmd("0000000000000000000003-exec-continue") self.expect("0000000000000000000003\^running") self.expect("\*stopped,reason=\"exited-normally\"") @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races def test_lldbmi_specialchars(self): """Test that 'lldb-mi --interpreter' handles complicated strings.""" # Create an alias for myexe complicated_myexe = "C--mpl-x file's`s @#$%^&*()_+-={}[]| name" os.symlink(self.myexe, complicated_myexe) self.addTearDownHook(lambda: os.unlink(complicated_myexe)) self.spawnLldbMi(args="\"%s\"" % complicated_myexe) # Test that the executable was loaded self.expect( "-file-exec-and-symbols \"%s\"" % complicated_myexe, exactly=True) self.expect("\^done") # Check that it was loaded correctly self.runCmd("-break-insert -f main") self.expect("\^done,bkpt={number=\"1\"") self.runCmd("-exec-run") self.expect("\^running") self.expect("\*stopped,reason=\"breakpoint-hit\"") @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @expectedFailureAll( oslist=["linux"], bugnumber="Failing in ~6/600 dosep runs (build 3120-3122)") def test_lldbmi_process_output(self): """Test that 'lldb-mi --interpreter' wraps process output correctly.""" self.spawnLldbMi(args=None) # Load executable self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.expect("\^done") # Run self.runCmd("-exec-run") self.expect("\^running") # Test that a process output is wrapped correctly self.expect("\@\"'\\\\r\\\\n\"") self.expect("\@\"` - it's \\\\\\\\n\\\\x12\\\\\"\\\\\\\\\\\\\"") @skipIfWindows # llvm.org/pr24452: Get lldb-mi tests working on Windows @skipIfFreeBSD # llvm.org/pr22411: Failure presumably due to known thread races @expectedFailureAll(oslist=["macosx"], bugnumber="rdar://28805064") def test_lldbmi_output_grammar(self): """Test that 'lldb-mi --interpreter' uses standard output syntax.""" self.spawnLldbMi(args=None) self.child.setecho(False) # Run all commands simultaneously self.runCmd("-unknown-command") + self.runCmd("-interpreter-exec command help") self.runCmd("-file-exec-and-symbols %s" % self.myexe) self.runCmd("-break-insert -f main") self.runCmd("-gdb-set target-async off") self.runCmd("-exec-run") self.runCmd("-gdb-set target-async on") self.runCmd("-exec-continue") self.runCmd("-gdb-exit") # Test that the program's output matches to the following pattern: # ( async-record | stream-record )* [ result-record ] "(gdb)" nl async_record = "^[0-9]*(\*|\+|=).+?\n" # 1 stream_record = "^(~|@|&).+?\n" # 2 result_record = "^[0-9]*\^.+?\n" # 3 prompt = "^\(gdb\)\r\n" # 4 command = "^\r\n" # 5 (it looks like empty line for pexpect) error = "^.+?\n" # 6 import pexpect # 7 (EOF) all_patterns = [ async_record, stream_record, result_record, prompt, command, error, pexpect.EOF] # Routines to get a bit-mask for the specified list of patterns def get_bit(pattern): return all_patterns.index(pattern) def get_mask(pattern): return 1 << get_bit(pattern) def or_op(x, y): return x | y def get_state(*args): return reduce(or_op, map(get_mask, args)) next_state = get_state(command) while True: it = self.expect(all_patterns) matched_pattern = all_patterns[it] # Check that state is acceptable if not (next_state & get_mask(matched_pattern)): self.fail( "error: inconsistent pattern '%s' for state %#x (matched string: %s)" % (repr(matched_pattern), next_state, self.child.after)) elif matched_pattern == async_record or matched_pattern == stream_record: next_state = get_state( async_record, stream_record, result_record, prompt) elif matched_pattern == result_record: # FIXME lldb-mi prints async-records out of turn # ``` # ^done # (gdb) # ^running # =thread-group-started,id="i1",pid="13875" # (gdb) # ``` # Therefore to pass that test I changed the grammar's rule: # next_state = get_state(prompt) # to: next_state = get_state(async_record, prompt) elif matched_pattern == prompt: # FIXME lldb-mi prints the prompt out of turn # ``` # ^done # (gdb) # ^running # (gdb) # (gdb) # ``` # Therefore to pass that test I changed the grammar's rule: # next_state = get_state(async_record, stream_record, result_record, command, pexpect.EOF) # to: next_state = get_state( async_record, stream_record, result_record, prompt, command, pexpect.EOF) elif matched_pattern == command: next_state = get_state( async_record, stream_record, result_record) elif matched_pattern == pexpect.EOF: break else: self.fail("error: pexpect returned an unknown state") Index: vendor/lldb/dist/source/Core/DataEncoder.cpp =================================================================== --- vendor/lldb/dist/source/Core/DataEncoder.cpp (revision 311541) +++ vendor/lldb/dist/source/Core/DataEncoder.cpp (revision 311542) @@ -1,287 +1,286 @@ //===-- DataEncoder.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/DataEncoder.h" // C Includes // C++ Includes #include #include // Other libraries and framework includes #include "llvm/Support/MathExtras.h" // Project includes #include "lldb/Core/DataBuffer.h" #include "lldb/Host/Endian.h" using namespace lldb; using namespace lldb_private; static inline void WriteInt16(unsigned char *ptr, unsigned offset, uint16_t value) { *(uint16_t *)(ptr + offset) = value; } static inline void WriteInt32(unsigned char *ptr, unsigned offset, uint32_t value) { *(uint32_t *)(ptr + offset) = value; } static inline void WriteInt64(unsigned char *ptr, unsigned offset, uint64_t value) { *(uint64_t *)(ptr + offset) = value; } static inline void WriteSwappedInt16(unsigned char *ptr, unsigned offset, uint16_t value) { *(uint16_t *)(ptr + offset) = llvm::ByteSwap_16(value); } static inline void WriteSwappedInt32(unsigned char *ptr, unsigned offset, uint32_t value) { *(uint32_t *)(ptr + offset) = llvm::ByteSwap_32(value); } static inline void WriteSwappedInt64(unsigned char *ptr, unsigned offset, uint64_t value) { *(uint64_t *)(ptr + offset) = llvm::ByteSwap_64(value); } //---------------------------------------------------------------------- // Default constructor. //---------------------------------------------------------------------- DataEncoder::DataEncoder() : m_start(nullptr), m_end(nullptr), m_byte_order(endian::InlHostByteOrder()), m_addr_size(sizeof(void *)), m_data_sp() {} //---------------------------------------------------------------------- // This constructor allows us to use data that is owned by someone else. // The data must stay around as long as this object is valid. //---------------------------------------------------------------------- DataEncoder::DataEncoder(void *data, uint32_t length, ByteOrder endian, uint8_t addr_size) : m_start((uint8_t *)data), m_end((uint8_t *)data + length), m_byte_order(endian), m_addr_size(addr_size), m_data_sp() {} //---------------------------------------------------------------------- // Make a shared pointer reference to the shared data in "data_sp" and // set the endian swapping setting to "swap", and the address size to // "addr_size". The shared data reference will ensure the data lives // as long as any DataEncoder objects exist that have a reference to // this data. //---------------------------------------------------------------------- DataEncoder::DataEncoder(const DataBufferSP &data_sp, ByteOrder endian, uint8_t addr_size) : m_start(nullptr), m_end(nullptr), m_byte_order(endian), m_addr_size(addr_size), m_data_sp() { SetData(data_sp); } DataEncoder::~DataEncoder() = default; //------------------------------------------------------------------ // Clears the object contents back to a default invalid state, and // release any references to shared data that this object may // contain. //------------------------------------------------------------------ void DataEncoder::Clear() { m_start = nullptr; m_end = nullptr; m_byte_order = endian::InlHostByteOrder(); m_addr_size = sizeof(void *); m_data_sp.reset(); } //------------------------------------------------------------------ // If this object contains shared data, this function returns the // offset into that shared data. Else zero is returned. //------------------------------------------------------------------ size_t DataEncoder::GetSharedDataOffset() const { if (m_start != nullptr) { const DataBuffer *data = m_data_sp.get(); if (data != nullptr) { const uint8_t *data_bytes = data->GetBytes(); if (data_bytes != nullptr) { assert(m_start >= data_bytes); return m_start - data_bytes; } } } return 0; } //---------------------------------------------------------------------- // Set the data with which this object will extract from to data // starting at BYTES and set the length of the data to LENGTH bytes // long. The data is externally owned must be around at least as // long as this object points to the data. No copy of the data is // made, this object just refers to this data and can extract from // it. If this object refers to any shared data upon entry, the // reference to that data will be released. Is SWAP is set to true, // any data extracted will be endian swapped. //---------------------------------------------------------------------- uint32_t DataEncoder::SetData(void *bytes, uint32_t length, ByteOrder endian) { m_byte_order = endian; m_data_sp.reset(); if (bytes == nullptr || length == 0) { m_start = nullptr; m_end = nullptr; } else { m_start = (uint8_t *)bytes; m_end = m_start + length; } return GetByteSize(); } //---------------------------------------------------------------------- // Assign the data for this object to be a subrange of the shared // data in "data_sp" starting "data_offset" bytes into "data_sp" // and ending "data_length" bytes later. If "data_offset" is not // a valid offset into "data_sp", then this object will contain no // bytes. If "data_offset" is within "data_sp" yet "data_length" is // too large, the length will be capped at the number of bytes // remaining in "data_sp". A ref counted pointer to the data in // "data_sp" will be made in this object IF the number of bytes this // object refers to in greater than zero (if at least one byte was // available starting at "data_offset") to ensure the data stays // around as long as it is needed. The address size and endian swap // settings will remain unchanged from their current settings. //---------------------------------------------------------------------- uint32_t DataEncoder::SetData(const DataBufferSP &data_sp, uint32_t data_offset, uint32_t data_length) { m_start = m_end = nullptr; if (data_length > 0) { m_data_sp = data_sp; if (data_sp) { const size_t data_size = data_sp->GetByteSize(); if (data_offset < data_size) { m_start = data_sp->GetBytes() + data_offset; const size_t bytes_left = data_size - data_offset; // Cap the length of we asked for too many if (data_length <= bytes_left) m_end = m_start + data_length; // We got all the bytes we wanted else m_end = m_start + bytes_left; // Not all the bytes requested were // available in the shared data } } } uint32_t new_size = GetByteSize(); // Don't hold a shared pointer to the data buffer if we don't share // any valid bytes in the shared buffer. if (new_size == 0) m_data_sp.reset(); return new_size; } //---------------------------------------------------------------------- // Extract a single unsigned char from the binary data and update // the offset pointed to by "offset_ptr". // // RETURNS the byte that was extracted, or zero on failure. //---------------------------------------------------------------------- uint32_t DataEncoder::PutU8(uint32_t offset, uint8_t value) { if (ValidOffset(offset)) { m_start[offset] = value; return offset + 1; } return UINT32_MAX; } uint32_t DataEncoder::PutU16(uint32_t offset, uint16_t value) { if (ValidOffsetForDataOfSize(offset, sizeof(value))) { if (m_byte_order != endian::InlHostByteOrder()) WriteSwappedInt16(m_start, offset, value); else WriteInt16(m_start, offset, value); return offset + sizeof(value); } return UINT32_MAX; } uint32_t DataEncoder::PutU32(uint32_t offset, uint32_t value) { if (ValidOffsetForDataOfSize(offset, sizeof(value))) { if (m_byte_order != endian::InlHostByteOrder()) WriteSwappedInt32(m_start, offset, value); else WriteInt32(m_start, offset, value); return offset + sizeof(value); } return UINT32_MAX; } uint32_t DataEncoder::PutU64(uint32_t offset, uint64_t value) { if (ValidOffsetForDataOfSize(offset, sizeof(value))) { if (m_byte_order != endian::InlHostByteOrder()) WriteSwappedInt64(m_start, offset, value); else WriteInt64(m_start, offset, value); return offset + sizeof(value); } return UINT32_MAX; } //---------------------------------------------------------------------- // Extract a single integer value from the data and update the offset // pointed to by "offset_ptr". The size of the extracted integer // is specified by the "byte_size" argument. "byte_size" should have // a value >= 1 and <= 8 since the return value is only 64 bits // wide. Any "byte_size" values less than 1 or greater than 8 will // result in nothing being extracted, and zero being returned. // // RETURNS the integer value that was extracted, or zero on failure. //---------------------------------------------------------------------- uint32_t DataEncoder::PutMaxU64(uint32_t offset, uint32_t byte_size, uint64_t value) { switch (byte_size) { case 1: return PutU8(offset, value); case 2: return PutU16(offset, value); case 4: return PutU32(offset, value); case 8: return PutU64(offset, value); default: - assert(!"GetMax64 unhandled case!"); - break; + llvm_unreachable("GetMax64 unhandled case!"); } return UINT32_MAX; } uint32_t DataEncoder::PutData(uint32_t offset, const void *src, uint32_t src_len) { if (src == nullptr || src_len == 0) return offset; if (ValidOffsetForDataOfSize(offset, src_len)) { memcpy(m_start + offset, src, src_len); return offset + src_len; } return UINT32_MAX; } uint32_t DataEncoder::PutAddress(uint32_t offset, lldb::addr_t addr) { return PutMaxU64(offset, GetAddressByteSize(), addr); } uint32_t DataEncoder::PutCString(uint32_t offset, const char *cstr) { if (cstr != nullptr) return PutData(offset, cstr, strlen(cstr) + 1); return UINT32_MAX; } Index: vendor/lldb/dist/source/Core/ValueObjectMemory.cpp =================================================================== --- vendor/lldb/dist/source/Core/ValueObjectMemory.cpp (revision 311541) +++ vendor/lldb/dist/source/Core/ValueObjectMemory.cpp (revision 311542) @@ -1,232 +1,231 @@ //===-- ValueObjectMemory.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/ValueObjectMemory.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/Module.h" #include "lldb/Core/Value.h" #include "lldb/Core/ValueObject.h" #include "lldb/Core/ValueObjectList.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/Variable.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" using namespace lldb; using namespace lldb_private; ValueObjectSP ValueObjectMemory::Create(ExecutionContextScope *exe_scope, llvm::StringRef name, const Address &address, lldb::TypeSP &type_sp) { return (new ValueObjectMemory(exe_scope, name, address, type_sp))->GetSP(); } ValueObjectSP ValueObjectMemory::Create(ExecutionContextScope *exe_scope, llvm::StringRef name, const Address &address, const CompilerType &ast_type) { return (new ValueObjectMemory(exe_scope, name, address, ast_type))->GetSP(); } ValueObjectMemory::ValueObjectMemory(ExecutionContextScope *exe_scope, llvm::StringRef name, const Address &address, lldb::TypeSP &type_sp) : ValueObject(exe_scope), m_address(address), m_type_sp(type_sp), m_compiler_type() { // Do not attempt to construct one of these objects with no variable! assert(m_type_sp.get() != NULL); SetName(ConstString(name)); m_value.SetContext(Value::eContextTypeLLDBType, m_type_sp.get()); TargetSP target_sp(GetTargetSP()); lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get()); if (load_address != LLDB_INVALID_ADDRESS) { m_value.SetValueType(Value::eValueTypeLoadAddress); m_value.GetScalar() = load_address; } else { lldb::addr_t file_address = m_address.GetFileAddress(); if (file_address != LLDB_INVALID_ADDRESS) { m_value.SetValueType(Value::eValueTypeFileAddress); m_value.GetScalar() = file_address; } else { m_value.GetScalar() = m_address.GetOffset(); m_value.SetValueType(Value::eValueTypeScalar); } } } ValueObjectMemory::ValueObjectMemory(ExecutionContextScope *exe_scope, llvm::StringRef name, const Address &address, const CompilerType &ast_type) : ValueObject(exe_scope), m_address(address), m_type_sp(), m_compiler_type(ast_type) { // Do not attempt to construct one of these objects with no variable! assert(m_compiler_type.GetTypeSystem()); assert(m_compiler_type.GetOpaqueQualType()); TargetSP target_sp(GetTargetSP()); SetName(ConstString(name)); // m_value.SetContext(Value::eContextTypeClangType, // m_compiler_type.GetOpaqueQualType()); m_value.SetCompilerType(m_compiler_type); lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get()); if (load_address != LLDB_INVALID_ADDRESS) { m_value.SetValueType(Value::eValueTypeLoadAddress); m_value.GetScalar() = load_address; } else { lldb::addr_t file_address = m_address.GetFileAddress(); if (file_address != LLDB_INVALID_ADDRESS) { m_value.SetValueType(Value::eValueTypeFileAddress); m_value.GetScalar() = file_address; } else { m_value.GetScalar() = m_address.GetOffset(); m_value.SetValueType(Value::eValueTypeScalar); } } } ValueObjectMemory::~ValueObjectMemory() {} CompilerType ValueObjectMemory::GetCompilerTypeImpl() { if (m_type_sp) return m_type_sp->GetForwardCompilerType(); return m_compiler_type; } ConstString ValueObjectMemory::GetTypeName() { if (m_type_sp) return m_type_sp->GetName(); return m_compiler_type.GetConstTypeName(); } ConstString ValueObjectMemory::GetDisplayTypeName() { if (m_type_sp) return m_type_sp->GetForwardCompilerType().GetDisplayTypeName(); return m_compiler_type.GetDisplayTypeName(); } size_t ValueObjectMemory::CalculateNumChildren(uint32_t max) { if (m_type_sp) { auto child_count = m_type_sp->GetNumChildren(true); return child_count <= max ? child_count : max; } const bool omit_empty_base_classes = true; auto child_count = m_compiler_type.GetNumChildren(omit_empty_base_classes); return child_count <= max ? child_count : max; } uint64_t ValueObjectMemory::GetByteSize() { if (m_type_sp) return m_type_sp->GetByteSize(); return m_compiler_type.GetByteSize(nullptr); } lldb::ValueType ValueObjectMemory::GetValueType() const { // RETHINK: Should this be inherited from somewhere? return lldb::eValueTypeVariableGlobal; } bool ValueObjectMemory::UpdateValue() { SetValueIsValid(false); m_error.Clear(); ExecutionContext exe_ctx(GetExecutionContextRef()); Target *target = exe_ctx.GetTargetPtr(); if (target) { m_data.SetByteOrder(target->GetArchitecture().GetByteOrder()); m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); } Value old_value(m_value); if (m_address.IsValid()) { Value::ValueType value_type = m_value.GetValueType(); switch (value_type) { default: - assert(!"Unhandled expression result value kind..."); - break; + llvm_unreachable("Unhandled expression result value kind..."); case Value::eValueTypeScalar: // The variable value is in the Scalar value inside the m_value. // We can point our m_data right to it. m_error = m_value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get()); break; case Value::eValueTypeFileAddress: case Value::eValueTypeLoadAddress: case Value::eValueTypeHostAddress: // The DWARF expression result was an address in the inferior // process. If this variable is an aggregate type, we just need // the address as the main value as all child variable objects // will rely upon this location and add an offset and then read // their own values as needed. If this variable is a simple // type, we read all data for it into m_data. // Make sure this type has a value before we try and read it // If we have a file address, convert it to a load address if we can. if (value_type == Value::eValueTypeFileAddress && exe_ctx.GetProcessPtr()) { lldb::addr_t load_addr = m_address.GetLoadAddress(target); if (load_addr != LLDB_INVALID_ADDRESS) { m_value.SetValueType(Value::eValueTypeLoadAddress); m_value.GetScalar() = load_addr; } } if (!CanProvideValue()) { // this value object represents an aggregate type whose // children have values, but this object does not. So we // say we are changed if our location has changed. SetValueDidChange(value_type != old_value.GetValueType() || m_value.GetScalar() != old_value.GetScalar()); } else { // Copy the Value and set the context to use our Variable // so it can extract read its value into m_data appropriately Value value(m_value); if (m_type_sp) value.SetContext(Value::eContextTypeLLDBType, m_type_sp.get()); else { // value.SetContext(Value::eContextTypeClangType, // m_compiler_type.GetOpaqueQualType()); value.SetCompilerType(m_compiler_type); } m_error = value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get()); } break; } SetValueIsValid(m_error.Success()); } return m_error.Success(); } bool ValueObjectMemory::IsInScope() { // FIXME: Maybe try to read the memory address, and if that works, then // we are in scope? return true; } lldb::ModuleSP ValueObjectMemory::GetModule() { return m_address.GetModule(); } Index: vendor/lldb/dist/source/Expression/IRInterpreter.cpp =================================================================== --- vendor/lldb/dist/source/Expression/IRInterpreter.cpp (revision 311541) +++ vendor/lldb/dist/source/Expression/IRInterpreter.cpp (revision 311542) @@ -1,1707 +1,1705 @@ //===-- IRInterpreter.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/Expression/IRInterpreter.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/ValueObject.h" #include "lldb/Expression/DiagnosticManager.h" #include "lldb/Expression/IRExecutionUnit.h" #include "lldb/Expression/IRMemoryMap.h" #include "lldb/Host/Endian.h" #include "lldb/Target/ABI.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanCallFunctionUsingABI.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; static std::string PrintValue(const Value *value, bool truncate = false) { std::string s; raw_string_ostream rso(s); value->print(rso); rso.flush(); if (truncate) s.resize(s.length() - 1); size_t offset; while ((offset = s.find('\n')) != s.npos) s.erase(offset, 1); while (s[0] == ' ' || s[0] == '\t') s.erase(0, 1); return s; } static std::string PrintType(const Type *type, bool truncate = false) { std::string s; raw_string_ostream rso(s); type->print(rso); rso.flush(); if (truncate) s.resize(s.length() - 1); return s; } static bool CanIgnoreCall(const CallInst *call) { const llvm::Function *called_function = call->getCalledFunction(); if (!called_function) return false; if (called_function->isIntrinsic()) { switch (called_function->getIntrinsicID()) { default: break; case llvm::Intrinsic::dbg_declare: case llvm::Intrinsic::dbg_value: return true; } } return false; } class InterpreterStackFrame { public: typedef std::map ValueMap; ValueMap m_values; DataLayout &m_target_data; lldb_private::IRExecutionUnit &m_execution_unit; const BasicBlock *m_bb; const BasicBlock *m_prev_bb; BasicBlock::const_iterator m_ii; BasicBlock::const_iterator m_ie; lldb::addr_t m_frame_process_address; size_t m_frame_size; lldb::addr_t m_stack_pointer; lldb::ByteOrder m_byte_order; size_t m_addr_byte_size; InterpreterStackFrame(DataLayout &target_data, lldb_private::IRExecutionUnit &execution_unit, lldb::addr_t stack_frame_bottom, lldb::addr_t stack_frame_top) : m_target_data(target_data), m_execution_unit(execution_unit), m_bb(nullptr), m_prev_bb(nullptr) { m_byte_order = (target_data.isLittleEndian() ? lldb::eByteOrderLittle : lldb::eByteOrderBig); m_addr_byte_size = (target_data.getPointerSize(0)); m_frame_process_address = stack_frame_bottom; m_frame_size = stack_frame_top - stack_frame_bottom; m_stack_pointer = stack_frame_top; } ~InterpreterStackFrame() {} void Jump(const BasicBlock *bb) { m_prev_bb = m_bb; m_bb = bb; m_ii = m_bb->begin(); m_ie = m_bb->end(); } std::string SummarizeValue(const Value *value) { lldb_private::StreamString ss; ss.Printf("%s", PrintValue(value).c_str()); ValueMap::iterator i = m_values.find(value); if (i != m_values.end()) { lldb::addr_t addr = i->second; ss.Printf(" 0x%llx", (unsigned long long)addr); } return ss.GetString(); } bool AssignToMatchType(lldb_private::Scalar &scalar, uint64_t u64value, Type *type) { size_t type_size = m_target_data.getTypeStoreSize(type); switch (type_size) { case 1: scalar = (uint8_t)u64value; break; case 2: scalar = (uint16_t)u64value; break; case 4: scalar = (uint32_t)u64value; break; case 8: scalar = (uint64_t)u64value; break; default: return false; } return true; } bool EvaluateValue(lldb_private::Scalar &scalar, const Value *value, Module &module) { const Constant *constant = dyn_cast(value); if (constant) { APInt value_apint; if (!ResolveConstantValue(value_apint, constant)) return false; return AssignToMatchType(scalar, value_apint.getLimitedValue(), value->getType()); } else { lldb::addr_t process_address = ResolveValue(value, module); size_t value_size = m_target_data.getTypeStoreSize(value->getType()); lldb_private::DataExtractor value_extractor; lldb_private::Error extract_error; m_execution_unit.GetMemoryData(value_extractor, process_address, value_size, extract_error); if (!extract_error.Success()) return false; lldb::offset_t offset = 0; if (value_size == 1 || value_size == 2 || value_size == 4 || value_size == 8) { uint64_t u64value = value_extractor.GetMaxU64(&offset, value_size); return AssignToMatchType(scalar, u64value, value->getType()); } } return false; } bool AssignValue(const Value *value, lldb_private::Scalar &scalar, Module &module) { lldb::addr_t process_address = ResolveValue(value, module); if (process_address == LLDB_INVALID_ADDRESS) return false; lldb_private::Scalar cast_scalar; if (!AssignToMatchType(cast_scalar, scalar.ULongLong(), value->getType())) return false; size_t value_byte_size = m_target_data.getTypeStoreSize(value->getType()); lldb_private::DataBufferHeap buf(value_byte_size, 0); lldb_private::Error get_data_error; if (!cast_scalar.GetAsMemoryData(buf.GetBytes(), buf.GetByteSize(), m_byte_order, get_data_error)) return false; lldb_private::Error write_error; m_execution_unit.WriteMemory(process_address, buf.GetBytes(), buf.GetByteSize(), write_error); return write_error.Success(); } bool ResolveConstantValue(APInt &value, const Constant *constant) { switch (constant->getValueID()) { default: break; case Value::FunctionVal: if (const Function *constant_func = dyn_cast(constant)) { lldb_private::ConstString name(constant_func->getName()); lldb::addr_t addr = m_execution_unit.FindSymbol(name); if (addr == LLDB_INVALID_ADDRESS) return false; value = APInt(m_target_data.getPointerSizeInBits(), addr); return true; } break; case Value::ConstantIntVal: if (const ConstantInt *constant_int = dyn_cast(constant)) { value = constant_int->getValue(); return true; } break; case Value::ConstantFPVal: if (const ConstantFP *constant_fp = dyn_cast(constant)) { value = constant_fp->getValueAPF().bitcastToAPInt(); return true; } break; case Value::ConstantExprVal: if (const ConstantExpr *constant_expr = dyn_cast(constant)) { switch (constant_expr->getOpcode()) { default: return false; case Instruction::IntToPtr: case Instruction::PtrToInt: case Instruction::BitCast: return ResolveConstantValue(value, constant_expr->getOperand(0)); case Instruction::GetElementPtr: { ConstantExpr::const_op_iterator op_cursor = constant_expr->op_begin(); ConstantExpr::const_op_iterator op_end = constant_expr->op_end(); Constant *base = dyn_cast(*op_cursor); if (!base) return false; if (!ResolveConstantValue(value, base)) return false; op_cursor++; if (op_cursor == op_end) return true; // no offset to apply! SmallVector indices(op_cursor, op_end); Type *src_elem_ty = cast(constant_expr)->getSourceElementType(); uint64_t offset = m_target_data.getIndexedOffsetInType(src_elem_ty, indices); const bool is_signed = true; value += APInt(value.getBitWidth(), offset, is_signed); return true; } } } break; case Value::ConstantPointerNullVal: if (isa(constant)) { value = APInt(m_target_data.getPointerSizeInBits(), 0); return true; } break; } return false; } bool MakeArgument(const Argument *value, uint64_t address) { lldb::addr_t data_address = Malloc(value->getType()); if (data_address == LLDB_INVALID_ADDRESS) return false; lldb_private::Error write_error; m_execution_unit.WritePointerToMemory(data_address, address, write_error); if (!write_error.Success()) { lldb_private::Error free_error; m_execution_unit.Free(data_address, free_error); return false; } m_values[value] = data_address; lldb_private::Log *log( lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (log) { log->Printf("Made an allocation for argument %s", PrintValue(value).c_str()); log->Printf(" Data region : %llx", (unsigned long long)address); log->Printf(" Ref region : %llx", (unsigned long long)data_address); } return true; } bool ResolveConstant(lldb::addr_t process_address, const Constant *constant) { APInt resolved_value; if (!ResolveConstantValue(resolved_value, constant)) return false; size_t constant_size = m_target_data.getTypeStoreSize(constant->getType()); lldb_private::DataBufferHeap buf(constant_size, 0); lldb_private::Error get_data_error; lldb_private::Scalar resolved_scalar( resolved_value.zextOrTrunc(llvm::NextPowerOf2(constant_size) * 8)); if (!resolved_scalar.GetAsMemoryData(buf.GetBytes(), buf.GetByteSize(), m_byte_order, get_data_error)) return false; lldb_private::Error write_error; m_execution_unit.WriteMemory(process_address, buf.GetBytes(), buf.GetByteSize(), write_error); return write_error.Success(); } lldb::addr_t Malloc(size_t size, uint8_t byte_alignment) { lldb::addr_t ret = m_stack_pointer; ret -= size; ret -= (ret % byte_alignment); if (ret < m_frame_process_address) return LLDB_INVALID_ADDRESS; m_stack_pointer = ret; return ret; } lldb::addr_t MallocPointer() { return Malloc(m_target_data.getPointerSize(), m_target_data.getPointerPrefAlignment()); } lldb::addr_t Malloc(llvm::Type *type) { lldb_private::Error alloc_error; return Malloc(m_target_data.getTypeAllocSize(type), m_target_data.getPrefTypeAlignment(type)); } std::string PrintData(lldb::addr_t addr, llvm::Type *type) { size_t length = m_target_data.getTypeStoreSize(type); lldb_private::DataBufferHeap buf(length, 0); lldb_private::Error read_error; m_execution_unit.ReadMemory(buf.GetBytes(), addr, length, read_error); if (!read_error.Success()) return std::string(""); lldb_private::StreamString ss; for (size_t i = 0; i < length; i++) { if ((!(i & 0xf)) && i) ss.Printf("%02hhx - ", buf.GetBytes()[i]); else ss.Printf("%02hhx ", buf.GetBytes()[i]); } return ss.GetString(); } lldb::addr_t ResolveValue(const Value *value, Module &module) { ValueMap::iterator i = m_values.find(value); if (i != m_values.end()) return i->second; // Fall back and allocate space [allocation type Alloca] lldb::addr_t data_address = Malloc(value->getType()); if (const Constant *constant = dyn_cast(value)) { if (!ResolveConstant(data_address, constant)) { lldb_private::Error free_error; m_execution_unit.Free(data_address, free_error); return LLDB_INVALID_ADDRESS; } } m_values[value] = data_address; return data_address; } }; static const char *unsupported_opcode_error = "Interpreter doesn't handle one of the expression's opcodes"; static const char *unsupported_operand_error = "Interpreter doesn't handle one of the expression's operands"; // static const char *interpreter_initialization_error = "Interpreter couldn't // be initialized"; static const char *interpreter_internal_error = "Interpreter encountered an internal error"; static const char *bad_value_error = "Interpreter couldn't resolve a value during execution"; static const char *memory_allocation_error = "Interpreter couldn't allocate memory"; static const char *memory_write_error = "Interpreter couldn't write to memory"; static const char *memory_read_error = "Interpreter couldn't read from memory"; static const char *infinite_loop_error = "Interpreter ran for too many cycles"; // static const char *bad_result_error = "Result of expression // is in bad memory"; static const char *too_many_functions_error = "Interpreter doesn't handle modules with multiple function bodies."; static bool CanResolveConstant(llvm::Constant *constant) { switch (constant->getValueID()) { default: return false; case Value::ConstantIntVal: case Value::ConstantFPVal: case Value::FunctionVal: return true; case Value::ConstantExprVal: if (const ConstantExpr *constant_expr = dyn_cast(constant)) { switch (constant_expr->getOpcode()) { default: return false; case Instruction::IntToPtr: case Instruction::PtrToInt: case Instruction::BitCast: return CanResolveConstant(constant_expr->getOperand(0)); case Instruction::GetElementPtr: { ConstantExpr::const_op_iterator op_cursor = constant_expr->op_begin(); Constant *base = dyn_cast(*op_cursor); if (!base) return false; return CanResolveConstant(base); } } } else { return false; } case Value::ConstantPointerNullVal: return true; } } bool IRInterpreter::CanInterpret(llvm::Module &module, llvm::Function &function, lldb_private::Error &error, const bool support_function_calls) { lldb_private::Log *log( lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); bool saw_function_with_body = false; for (Module::iterator fi = module.begin(), fe = module.end(); fi != fe; ++fi) { if (fi->begin() != fi->end()) { if (saw_function_with_body) { if (log) log->Printf("More than one function in the module has a body"); error.SetErrorToGenericError(); error.SetErrorString(too_many_functions_error); return false; } saw_function_with_body = true; } } for (Function::iterator bbi = function.begin(), bbe = function.end(); bbi != bbe; ++bbi) { for (BasicBlock::iterator ii = bbi->begin(), ie = bbi->end(); ii != ie; ++ii) { switch (ii->getOpcode()) { default: { if (log) log->Printf("Unsupported instruction: %s", PrintValue(&*ii).c_str()); error.SetErrorToGenericError(); error.SetErrorString(unsupported_opcode_error); return false; } case Instruction::Add: case Instruction::Alloca: case Instruction::BitCast: case Instruction::Br: case Instruction::PHI: break; case Instruction::Call: { CallInst *call_inst = dyn_cast(ii); if (!call_inst) { error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } if (!CanIgnoreCall(call_inst) && !support_function_calls) { if (log) log->Printf("Unsupported instruction: %s", PrintValue(&*ii).c_str()); error.SetErrorToGenericError(); error.SetErrorString(unsupported_opcode_error); return false; } } break; case Instruction::GetElementPtr: break; case Instruction::ICmp: { ICmpInst *icmp_inst = dyn_cast(ii); if (!icmp_inst) { error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } switch (icmp_inst->getPredicate()) { default: { if (log) log->Printf("Unsupported ICmp predicate: %s", PrintValue(&*ii).c_str()); error.SetErrorToGenericError(); error.SetErrorString(unsupported_opcode_error); return false; } case CmpInst::ICMP_EQ: case CmpInst::ICMP_NE: case CmpInst::ICMP_UGT: case CmpInst::ICMP_UGE: case CmpInst::ICMP_ULT: case CmpInst::ICMP_ULE: case CmpInst::ICMP_SGT: case CmpInst::ICMP_SGE: case CmpInst::ICMP_SLT: case CmpInst::ICMP_SLE: break; } } break; case Instruction::And: case Instruction::AShr: case Instruction::IntToPtr: case Instruction::PtrToInt: case Instruction::Load: case Instruction::LShr: case Instruction::Mul: case Instruction::Or: case Instruction::Ret: case Instruction::SDiv: case Instruction::SExt: case Instruction::Shl: case Instruction::SRem: case Instruction::Store: case Instruction::Sub: case Instruction::Trunc: case Instruction::UDiv: case Instruction::URem: case Instruction::Xor: case Instruction::ZExt: break; } for (int oi = 0, oe = ii->getNumOperands(); oi != oe; ++oi) { Value *operand = ii->getOperand(oi); Type *operand_type = operand->getType(); switch (operand_type->getTypeID()) { default: break; case Type::VectorTyID: { if (log) log->Printf("Unsupported operand type: %s", PrintType(operand_type).c_str()); error.SetErrorString(unsupported_operand_error); return false; } } if (Constant *constant = llvm::dyn_cast(operand)) { if (!CanResolveConstant(constant)) { if (log) log->Printf("Unsupported constant: %s", PrintValue(constant).c_str()); error.SetErrorString(unsupported_operand_error); return false; } } } } } return true; } bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function, llvm::ArrayRef args, lldb_private::IRExecutionUnit &execution_unit, lldb_private::Error &error, lldb::addr_t stack_frame_bottom, lldb::addr_t stack_frame_top, lldb_private::ExecutionContext &exe_ctx) { lldb_private::Log *log( lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (log) { std::string s; raw_string_ostream oss(s); module.print(oss, NULL); oss.flush(); log->Printf("Module as passed in to IRInterpreter::Interpret: \n\"%s\"", s.c_str()); } DataLayout data_layout(&module); InterpreterStackFrame frame(data_layout, execution_unit, stack_frame_bottom, stack_frame_top); if (frame.m_frame_process_address == LLDB_INVALID_ADDRESS) { error.SetErrorString("Couldn't allocate stack frame"); } int arg_index = 0; for (llvm::Function::arg_iterator ai = function.arg_begin(), ae = function.arg_end(); ai != ae; ++ai, ++arg_index) { if (args.size() <= static_cast(arg_index)) { error.SetErrorString("Not enough arguments passed in to function"); return false; } lldb::addr_t ptr = args[arg_index]; frame.MakeArgument(&*ai, ptr); } uint32_t num_insts = 0; frame.Jump(&function.front()); while (frame.m_ii != frame.m_ie && (++num_insts < 4096)) { const Instruction *inst = &*frame.m_ii; if (log) log->Printf("Interpreting %s", PrintValue(inst).c_str()); switch (inst->getOpcode()) { default: break; case Instruction::Add: case Instruction::Sub: case Instruction::Mul: case Instruction::SDiv: case Instruction::UDiv: case Instruction::SRem: case Instruction::URem: case Instruction::Shl: case Instruction::LShr: case Instruction::AShr: case Instruction::And: case Instruction::Or: case Instruction::Xor: { const BinaryOperator *bin_op = dyn_cast(inst); if (!bin_op) { if (log) log->Printf( "getOpcode() returns %s, but instruction is not a BinaryOperator", inst->getOpcodeName()); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } Value *lhs = inst->getOperand(0); Value *rhs = inst->getOperand(1); lldb_private::Scalar L; lldb_private::Scalar R; if (!frame.EvaluateValue(L, lhs, module)) { if (log) log->Printf("Couldn't evaluate %s", PrintValue(lhs).c_str()); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } if (!frame.EvaluateValue(R, rhs, module)) { if (log) log->Printf("Couldn't evaluate %s", PrintValue(rhs).c_str()); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } lldb_private::Scalar result; switch (inst->getOpcode()) { default: break; case Instruction::Add: result = L + R; break; case Instruction::Mul: result = L * R; break; case Instruction::Sub: result = L - R; break; case Instruction::SDiv: L.MakeSigned(); R.MakeSigned(); result = L / R; break; case Instruction::UDiv: L.MakeUnsigned(); R.MakeUnsigned(); result = L / R; break; case Instruction::SRem: L.MakeSigned(); R.MakeSigned(); result = L % R; break; case Instruction::URem: L.MakeUnsigned(); R.MakeUnsigned(); result = L % R; break; case Instruction::Shl: result = L << R; break; case Instruction::AShr: result = L >> R; break; case Instruction::LShr: result = L; result.ShiftRightLogical(R); break; case Instruction::And: result = L & R; break; case Instruction::Or: result = L | R; break; case Instruction::Xor: result = L ^ R; break; } frame.AssignValue(inst, result, module); if (log) { log->Printf("Interpreted a %s", inst->getOpcodeName()); log->Printf(" L : %s", frame.SummarizeValue(lhs).c_str()); log->Printf(" R : %s", frame.SummarizeValue(rhs).c_str()); log->Printf(" = : %s", frame.SummarizeValue(inst).c_str()); } } break; case Instruction::Alloca: { const AllocaInst *alloca_inst = dyn_cast(inst); if (!alloca_inst) { if (log) log->Printf("getOpcode() returns Alloca, but instruction is not an " "AllocaInst"); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } if (alloca_inst->isArrayAllocation()) { if (log) log->Printf( "AllocaInsts are not handled if isArrayAllocation() is true"); error.SetErrorToGenericError(); error.SetErrorString(unsupported_opcode_error); return false; } // The semantics of Alloca are: // Create a region R of virtual memory of type T, backed by a data // buffer // Create a region P of virtual memory of type T*, backed by a data // buffer // Write the virtual address of R into P Type *T = alloca_inst->getAllocatedType(); Type *Tptr = alloca_inst->getType(); lldb::addr_t R = frame.Malloc(T); if (R == LLDB_INVALID_ADDRESS) { if (log) log->Printf("Couldn't allocate memory for an AllocaInst"); error.SetErrorToGenericError(); error.SetErrorString(memory_allocation_error); return false; } lldb::addr_t P = frame.Malloc(Tptr); if (P == LLDB_INVALID_ADDRESS) { if (log) log->Printf("Couldn't allocate the result pointer for an AllocaInst"); error.SetErrorToGenericError(); error.SetErrorString(memory_allocation_error); return false; } lldb_private::Error write_error; execution_unit.WritePointerToMemory(P, R, write_error); if (!write_error.Success()) { if (log) log->Printf("Couldn't write the result pointer for an AllocaInst"); error.SetErrorToGenericError(); error.SetErrorString(memory_write_error); lldb_private::Error free_error; execution_unit.Free(P, free_error); execution_unit.Free(R, free_error); return false; } frame.m_values[alloca_inst] = P; if (log) { log->Printf("Interpreted an AllocaInst"); log->Printf(" R : 0x%" PRIx64, R); log->Printf(" P : 0x%" PRIx64, P); } } break; case Instruction::BitCast: case Instruction::ZExt: { const CastInst *cast_inst = dyn_cast(inst); if (!cast_inst) { if (log) log->Printf( "getOpcode() returns %s, but instruction is not a BitCastInst", cast_inst->getOpcodeName()); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } Value *source = cast_inst->getOperand(0); lldb_private::Scalar S; if (!frame.EvaluateValue(S, source, module)) { if (log) log->Printf("Couldn't evaluate %s", PrintValue(source).c_str()); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } frame.AssignValue(inst, S, module); } break; case Instruction::SExt: { const CastInst *cast_inst = dyn_cast(inst); if (!cast_inst) { if (log) log->Printf( "getOpcode() returns %s, but instruction is not a BitCastInst", cast_inst->getOpcodeName()); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } Value *source = cast_inst->getOperand(0); lldb_private::Scalar S; if (!frame.EvaluateValue(S, source, module)) { if (log) log->Printf("Couldn't evaluate %s", PrintValue(source).c_str()); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } S.MakeSigned(); lldb_private::Scalar S_signextend(S.SLongLong()); frame.AssignValue(inst, S_signextend, module); } break; case Instruction::Br: { const BranchInst *br_inst = dyn_cast(inst); if (!br_inst) { if (log) log->Printf( "getOpcode() returns Br, but instruction is not a BranchInst"); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } if (br_inst->isConditional()) { Value *condition = br_inst->getCondition(); lldb_private::Scalar C; if (!frame.EvaluateValue(C, condition, module)) { if (log) log->Printf("Couldn't evaluate %s", PrintValue(condition).c_str()); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } if (!C.IsZero()) frame.Jump(br_inst->getSuccessor(0)); else frame.Jump(br_inst->getSuccessor(1)); if (log) { log->Printf("Interpreted a BrInst with a condition"); log->Printf(" cond : %s", frame.SummarizeValue(condition).c_str()); } } else { frame.Jump(br_inst->getSuccessor(0)); if (log) { log->Printf("Interpreted a BrInst with no condition"); } } } continue; case Instruction::PHI: { const PHINode *phi_inst = dyn_cast(inst); if (!phi_inst) { if (log) log->Printf( "getOpcode() returns PHI, but instruction is not a PHINode"); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } if (!frame.m_prev_bb) { if (log) log->Printf("Encountered PHI node without having jumped from another " "basic block"); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } Value *value = phi_inst->getIncomingValueForBlock(frame.m_prev_bb); lldb_private::Scalar result; if (!frame.EvaluateValue(result, value, module)) { if (log) log->Printf("Couldn't evaluate %s", PrintValue(value).c_str()); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } frame.AssignValue(inst, result, module); if (log) { log->Printf("Interpreted a %s", inst->getOpcodeName()); log->Printf(" Incoming value : %s", frame.SummarizeValue(value).c_str()); } } break; case Instruction::GetElementPtr: { const GetElementPtrInst *gep_inst = dyn_cast(inst); if (!gep_inst) { if (log) log->Printf("getOpcode() returns GetElementPtr, but instruction is " "not a GetElementPtrInst"); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } const Value *pointer_operand = gep_inst->getPointerOperand(); Type *src_elem_ty = gep_inst->getSourceElementType(); lldb_private::Scalar P; if (!frame.EvaluateValue(P, pointer_operand, module)) { if (log) log->Printf("Couldn't evaluate %s", PrintValue(pointer_operand).c_str()); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } typedef SmallVector IndexVector; typedef IndexVector::iterator IndexIterator; SmallVector indices(gep_inst->idx_begin(), gep_inst->idx_end()); SmallVector const_indices; for (IndexIterator ii = indices.begin(), ie = indices.end(); ii != ie; ++ii) { ConstantInt *constant_index = dyn_cast(*ii); if (!constant_index) { lldb_private::Scalar I; if (!frame.EvaluateValue(I, *ii, module)) { if (log) log->Printf("Couldn't evaluate %s", PrintValue(*ii).c_str()); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } if (log) log->Printf("Evaluated constant index %s as %llu", PrintValue(*ii).c_str(), I.ULongLong(LLDB_INVALID_ADDRESS)); constant_index = cast(ConstantInt::get( (*ii)->getType(), I.ULongLong(LLDB_INVALID_ADDRESS))); } const_indices.push_back(constant_index); } uint64_t offset = data_layout.getIndexedOffsetInType(src_elem_ty, const_indices); lldb_private::Scalar Poffset = P + offset; frame.AssignValue(inst, Poffset, module); if (log) { log->Printf("Interpreted a GetElementPtrInst"); log->Printf(" P : %s", frame.SummarizeValue(pointer_operand).c_str()); log->Printf(" Poffset : %s", frame.SummarizeValue(inst).c_str()); } } break; case Instruction::ICmp: { const ICmpInst *icmp_inst = dyn_cast(inst); if (!icmp_inst) { if (log) log->Printf( "getOpcode() returns ICmp, but instruction is not an ICmpInst"); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } CmpInst::Predicate predicate = icmp_inst->getPredicate(); Value *lhs = inst->getOperand(0); Value *rhs = inst->getOperand(1); lldb_private::Scalar L; lldb_private::Scalar R; if (!frame.EvaluateValue(L, lhs, module)) { if (log) log->Printf("Couldn't evaluate %s", PrintValue(lhs).c_str()); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } if (!frame.EvaluateValue(R, rhs, module)) { if (log) log->Printf("Couldn't evaluate %s", PrintValue(rhs).c_str()); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } lldb_private::Scalar result; switch (predicate) { default: return false; case CmpInst::ICMP_EQ: result = (L == R); break; case CmpInst::ICMP_NE: result = (L != R); break; case CmpInst::ICMP_UGT: L.MakeUnsigned(); R.MakeUnsigned(); result = (L > R); break; case CmpInst::ICMP_UGE: L.MakeUnsigned(); R.MakeUnsigned(); result = (L >= R); break; case CmpInst::ICMP_ULT: L.MakeUnsigned(); R.MakeUnsigned(); result = (L < R); break; case CmpInst::ICMP_ULE: L.MakeUnsigned(); R.MakeUnsigned(); result = (L <= R); break; case CmpInst::ICMP_SGT: L.MakeSigned(); R.MakeSigned(); result = (L > R); break; case CmpInst::ICMP_SGE: L.MakeSigned(); R.MakeSigned(); result = (L >= R); break; case CmpInst::ICMP_SLT: L.MakeSigned(); R.MakeSigned(); result = (L < R); break; case CmpInst::ICMP_SLE: L.MakeSigned(); R.MakeSigned(); result = (L <= R); break; } frame.AssignValue(inst, result, module); if (log) { log->Printf("Interpreted an ICmpInst"); log->Printf(" L : %s", frame.SummarizeValue(lhs).c_str()); log->Printf(" R : %s", frame.SummarizeValue(rhs).c_str()); log->Printf(" = : %s", frame.SummarizeValue(inst).c_str()); } } break; case Instruction::IntToPtr: { const IntToPtrInst *int_to_ptr_inst = dyn_cast(inst); if (!int_to_ptr_inst) { if (log) log->Printf("getOpcode() returns IntToPtr, but instruction is not an " "IntToPtrInst"); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } Value *src_operand = int_to_ptr_inst->getOperand(0); lldb_private::Scalar I; if (!frame.EvaluateValue(I, src_operand, module)) { if (log) log->Printf("Couldn't evaluate %s", PrintValue(src_operand).c_str()); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } frame.AssignValue(inst, I, module); if (log) { log->Printf("Interpreted an IntToPtr"); log->Printf(" Src : %s", frame.SummarizeValue(src_operand).c_str()); log->Printf(" = : %s", frame.SummarizeValue(inst).c_str()); } } break; case Instruction::PtrToInt: { const PtrToIntInst *ptr_to_int_inst = dyn_cast(inst); if (!ptr_to_int_inst) { if (log) log->Printf("getOpcode() returns PtrToInt, but instruction is not an " "PtrToIntInst"); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } Value *src_operand = ptr_to_int_inst->getOperand(0); lldb_private::Scalar I; if (!frame.EvaluateValue(I, src_operand, module)) { if (log) log->Printf("Couldn't evaluate %s", PrintValue(src_operand).c_str()); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } frame.AssignValue(inst, I, module); if (log) { log->Printf("Interpreted a PtrToInt"); log->Printf(" Src : %s", frame.SummarizeValue(src_operand).c_str()); log->Printf(" = : %s", frame.SummarizeValue(inst).c_str()); } } break; case Instruction::Trunc: { const TruncInst *trunc_inst = dyn_cast(inst); if (!trunc_inst) { if (log) log->Printf( "getOpcode() returns Trunc, but instruction is not a TruncInst"); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } Value *src_operand = trunc_inst->getOperand(0); lldb_private::Scalar I; if (!frame.EvaluateValue(I, src_operand, module)) { if (log) log->Printf("Couldn't evaluate %s", PrintValue(src_operand).c_str()); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } frame.AssignValue(inst, I, module); if (log) { log->Printf("Interpreted a Trunc"); log->Printf(" Src : %s", frame.SummarizeValue(src_operand).c_str()); log->Printf(" = : %s", frame.SummarizeValue(inst).c_str()); } } break; case Instruction::Load: { const LoadInst *load_inst = dyn_cast(inst); if (!load_inst) { if (log) log->Printf( "getOpcode() returns Load, but instruction is not a LoadInst"); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } // The semantics of Load are: // Create a region D that will contain the loaded data // Resolve the region P containing a pointer // Dereference P to get the region R that the data should be loaded from // Transfer a unit of type type(D) from R to D const Value *pointer_operand = load_inst->getPointerOperand(); Type *pointer_ty = pointer_operand->getType(); PointerType *pointer_ptr_ty = dyn_cast(pointer_ty); if (!pointer_ptr_ty) { if (log) log->Printf("getPointerOperand()->getType() is not a PointerType"); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } Type *target_ty = pointer_ptr_ty->getElementType(); lldb::addr_t D = frame.ResolveValue(load_inst, module); lldb::addr_t P = frame.ResolveValue(pointer_operand, module); if (D == LLDB_INVALID_ADDRESS) { if (log) log->Printf("LoadInst's value doesn't resolve to anything"); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } if (P == LLDB_INVALID_ADDRESS) { if (log) log->Printf("LoadInst's pointer doesn't resolve to anything"); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } lldb::addr_t R; lldb_private::Error read_error; execution_unit.ReadPointerFromMemory(&R, P, read_error); if (!read_error.Success()) { if (log) log->Printf("Couldn't read the address to be loaded for a LoadInst"); error.SetErrorToGenericError(); error.SetErrorString(memory_read_error); return false; } size_t target_size = data_layout.getTypeStoreSize(target_ty); lldb_private::DataBufferHeap buffer(target_size, 0); read_error.Clear(); execution_unit.ReadMemory(buffer.GetBytes(), R, buffer.GetByteSize(), read_error); if (!read_error.Success()) { if (log) log->Printf("Couldn't read from a region on behalf of a LoadInst"); error.SetErrorToGenericError(); error.SetErrorString(memory_read_error); return false; } lldb_private::Error write_error; execution_unit.WriteMemory(D, buffer.GetBytes(), buffer.GetByteSize(), write_error); if (!write_error.Success()) { if (log) log->Printf("Couldn't write to a region on behalf of a LoadInst"); error.SetErrorToGenericError(); error.SetErrorString(memory_read_error); return false; } if (log) { log->Printf("Interpreted a LoadInst"); log->Printf(" P : 0x%" PRIx64, P); log->Printf(" R : 0x%" PRIx64, R); log->Printf(" D : 0x%" PRIx64, D); } } break; case Instruction::Ret: { return true; } case Instruction::Store: { const StoreInst *store_inst = dyn_cast(inst); if (!store_inst) { if (log) log->Printf( "getOpcode() returns Store, but instruction is not a StoreInst"); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } // The semantics of Store are: // Resolve the region D containing the data to be stored // Resolve the region P containing a pointer // Dereference P to get the region R that the data should be stored in // Transfer a unit of type type(D) from D to R const Value *value_operand = store_inst->getValueOperand(); const Value *pointer_operand = store_inst->getPointerOperand(); Type *pointer_ty = pointer_operand->getType(); PointerType *pointer_ptr_ty = dyn_cast(pointer_ty); if (!pointer_ptr_ty) return false; Type *target_ty = pointer_ptr_ty->getElementType(); lldb::addr_t D = frame.ResolveValue(value_operand, module); lldb::addr_t P = frame.ResolveValue(pointer_operand, module); if (D == LLDB_INVALID_ADDRESS) { if (log) log->Printf("StoreInst's value doesn't resolve to anything"); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } if (P == LLDB_INVALID_ADDRESS) { if (log) log->Printf("StoreInst's pointer doesn't resolve to anything"); error.SetErrorToGenericError(); error.SetErrorString(bad_value_error); return false; } lldb::addr_t R; lldb_private::Error read_error; execution_unit.ReadPointerFromMemory(&R, P, read_error); if (!read_error.Success()) { if (log) log->Printf("Couldn't read the address to be loaded for a LoadInst"); error.SetErrorToGenericError(); error.SetErrorString(memory_read_error); return false; } size_t target_size = data_layout.getTypeStoreSize(target_ty); lldb_private::DataBufferHeap buffer(target_size, 0); read_error.Clear(); execution_unit.ReadMemory(buffer.GetBytes(), D, buffer.GetByteSize(), read_error); if (!read_error.Success()) { if (log) log->Printf("Couldn't read from a region on behalf of a StoreInst"); error.SetErrorToGenericError(); error.SetErrorString(memory_read_error); return false; } lldb_private::Error write_error; execution_unit.WriteMemory(R, buffer.GetBytes(), buffer.GetByteSize(), write_error); if (!write_error.Success()) { if (log) log->Printf("Couldn't write to a region on behalf of a StoreInst"); error.SetErrorToGenericError(); error.SetErrorString(memory_write_error); return false; } if (log) { log->Printf("Interpreted a StoreInst"); log->Printf(" D : 0x%" PRIx64, D); log->Printf(" P : 0x%" PRIx64, P); log->Printf(" R : 0x%" PRIx64, R); } } break; case Instruction::Call: { const CallInst *call_inst = dyn_cast(inst); if (!call_inst) { if (log) log->Printf( "getOpcode() returns %s, but instruction is not a CallInst", inst->getOpcodeName()); error.SetErrorToGenericError(); error.SetErrorString(interpreter_internal_error); return false; } if (CanIgnoreCall(call_inst)) break; // Get the return type llvm::Type *returnType = call_inst->getType(); if (returnType == nullptr) { error.SetErrorToGenericError(); error.SetErrorString("unable to access return type"); return false; } // Work with void, integer and pointer return types if (!returnType->isVoidTy() && !returnType->isIntegerTy() && !returnType->isPointerTy()) { error.SetErrorToGenericError(); error.SetErrorString("return type is not supported"); return false; } // Check we can actually get a thread if (exe_ctx.GetThreadPtr() == nullptr) { error.SetErrorToGenericError(); error.SetErrorStringWithFormat("unable to acquire thread"); return false; } // Make sure we have a valid process if (!exe_ctx.GetProcessPtr()) { error.SetErrorToGenericError(); error.SetErrorStringWithFormat("unable to get the process"); return false; } // Find the address of the callee function lldb_private::Scalar I; const llvm::Value *val = call_inst->getCalledValue(); if (!frame.EvaluateValue(I, val, module)) { error.SetErrorToGenericError(); error.SetErrorString("unable to get address of function"); return false; } lldb_private::Address funcAddr(I.ULongLong(LLDB_INVALID_ADDRESS)); lldb_private::DiagnosticManager diagnostics; lldb_private::EvaluateExpressionOptions options; // We generally receive a function pointer which we must dereference llvm::Type *prototype = val->getType(); if (!prototype->isPointerTy()) { error.SetErrorToGenericError(); error.SetErrorString("call need function pointer"); return false; } // Dereference the function pointer prototype = prototype->getPointerElementType(); if (!(prototype->isFunctionTy() || prototype->isFunctionVarArg())) { error.SetErrorToGenericError(); error.SetErrorString("call need function pointer"); return false; } // Find number of arguments const int numArgs = call_inst->getNumArgOperands(); // We work with a fixed array of 16 arguments which is our upper limit static lldb_private::ABI::CallArgument rawArgs[16]; if (numArgs >= 16) { error.SetErrorToGenericError(); error.SetErrorStringWithFormat("function takes too many arguments"); return false; } // Push all function arguments to the argument list that will // be passed to the call function thread plan for (int i = 0; i < numArgs; i++) { // Get details of this argument llvm::Value *arg_op = call_inst->getArgOperand(i); llvm::Type *arg_ty = arg_op->getType(); // Ensure that this argument is an supported type if (!arg_ty->isIntegerTy() && !arg_ty->isPointerTy()) { error.SetErrorToGenericError(); error.SetErrorStringWithFormat("argument %d must be integer type", i); return false; } // Extract the arguments value lldb_private::Scalar tmp_op = 0; if (!frame.EvaluateValue(tmp_op, arg_op, module)) { error.SetErrorToGenericError(); error.SetErrorStringWithFormat("unable to evaluate argument %d", i); return false; } // Check if this is a string literal or constant string pointer if (arg_ty->isPointerTy()) { // Pointer to just one type assert(arg_ty->getNumContainedTypes() == 1); lldb::addr_t addr = tmp_op.ULongLong(); size_t dataSize = 0; - if (execution_unit.GetAllocSize(addr, dataSize)) { - // Create the required buffer - rawArgs[i].size = dataSize; - rawArgs[i].data_ap.reset(new uint8_t[dataSize + 1]); + bool Success = execution_unit.GetAllocSize(addr, dataSize); + (void)Success; + assert(Success && + "unable to locate host data for transfer to device"); + // Create the required buffer + rawArgs[i].size = dataSize; + rawArgs[i].data_ap.reset(new uint8_t[dataSize + 1]); - // Read string from host memory - execution_unit.ReadMemory(rawArgs[i].data_ap.get(), addr, dataSize, - error); - if (error.Fail()) { - assert(!"we have failed to read the string from memory"); - return false; - } - // Add null terminator - rawArgs[i].data_ap[dataSize] = '\0'; - rawArgs[i].type = lldb_private::ABI::CallArgument::HostPointer; - } else { - assert(!"unable to locate host data for transfer to device"); - return false; - } + // Read string from host memory + execution_unit.ReadMemory(rawArgs[i].data_ap.get(), addr, dataSize, + error); + assert(!error.Fail() && + "we have failed to read the string from memory"); + + // Add null terminator + rawArgs[i].data_ap[dataSize] = '\0'; + rawArgs[i].type = lldb_private::ABI::CallArgument::HostPointer; } else /* if ( arg_ty->isPointerTy() ) */ { rawArgs[i].type = lldb_private::ABI::CallArgument::TargetValue; // Get argument size in bytes rawArgs[i].size = arg_ty->getIntegerBitWidth() / 8; // Push value into argument list for thread plan rawArgs[i].value = tmp_op.ULongLong(); } } // Pack the arguments into an llvm::array llvm::ArrayRef args(rawArgs, numArgs); // Setup a thread plan to call the target function lldb::ThreadPlanSP call_plan_sp( new lldb_private::ThreadPlanCallFunctionUsingABI( exe_ctx.GetThreadRef(), funcAddr, *prototype, *returnType, args, options)); // Check if the plan is valid lldb_private::StreamString ss; if (!call_plan_sp || !call_plan_sp->ValidatePlan(&ss)) { error.SetErrorToGenericError(); error.SetErrorStringWithFormat( "unable to make ThreadPlanCallFunctionUsingABI for 0x%llx", I.ULongLong()); return false; } exe_ctx.GetProcessPtr()->SetRunningUserExpression(true); // Execute the actual function call thread plan lldb::ExpressionResults res = exe_ctx.GetProcessRef().RunThreadPlan( exe_ctx, call_plan_sp, options, diagnostics); // Check that the thread plan completed successfully if (res != lldb::ExpressionResults::eExpressionCompleted) { error.SetErrorToGenericError(); error.SetErrorStringWithFormat("ThreadPlanCallFunctionUsingABI failed"); return false; } exe_ctx.GetProcessPtr()->SetRunningUserExpression(false); // Void return type if (returnType->isVoidTy()) { // Cant assign to void types, so we leave the frame untouched } else // Integer or pointer return type if (returnType->isIntegerTy() || returnType->isPointerTy()) { // Get the encapsulated return value lldb::ValueObjectSP retVal = call_plan_sp.get()->GetReturnValueObject(); lldb_private::Scalar returnVal = -1; lldb_private::ValueObject *vobj = retVal.get(); // Check if the return value is valid if (vobj == nullptr || retVal.empty()) { error.SetErrorToGenericError(); error.SetErrorStringWithFormat("unable to get the return value"); return false; } // Extract the return value as a integer lldb_private::Value &value = vobj->GetValue(); returnVal = value.GetScalar(); // Push the return value as the result frame.AssignValue(inst, returnVal, module); } } break; } ++frame.m_ii; } if (num_insts >= 4096) { error.SetErrorToGenericError(); error.SetErrorString(infinite_loop_error); return false; } return false; } Index: vendor/lldb/dist/source/Expression/IRMemoryMap.cpp =================================================================== --- vendor/lldb/dist/source/Expression/IRMemoryMap.cpp (revision 311541) +++ vendor/lldb/dist/source/Expression/IRMemoryMap.cpp (revision 311542) @@ -1,844 +1,844 @@ //===-- IRMemoryMap.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/Expression/IRMemoryMap.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Core/Scalar.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/LLDBAssert.h" using namespace lldb_private; IRMemoryMap::IRMemoryMap(lldb::TargetSP target_sp) : m_target_wp(target_sp) { if (target_sp) m_process_wp = target_sp->GetProcessSP(); } IRMemoryMap::~IRMemoryMap() { lldb::ProcessSP process_sp = m_process_wp.lock(); if (process_sp) { AllocationMap::iterator iter; Error err; while ((iter = m_allocations.begin()) != m_allocations.end()) { err.Clear(); if (iter->second.m_leak) m_allocations.erase(iter); else Free(iter->first, err); } } } lldb::addr_t IRMemoryMap::FindSpace(size_t size) { // The FindSpace algorithm's job is to find a region of memory that the // underlying process is unlikely to be using. // // The memory returned by this function will never be written to. The only // point is that it should not shadow process memory if possible, so that // expressions processing real values from the process do not use the // wrong data. // // If the process can in fact allocate memory (CanJIT() lets us know this) // then this can be accomplished just be allocating memory in the inferior. // Then no guessing is required. lldb::TargetSP target_sp = m_target_wp.lock(); lldb::ProcessSP process_sp = m_process_wp.lock(); const bool process_is_alive = process_sp && process_sp->IsAlive(); lldb::addr_t ret = LLDB_INVALID_ADDRESS; if (size == 0) return ret; if (process_is_alive && process_sp->CanJIT()) { Error alloc_error; ret = process_sp->AllocateMemory(size, lldb::ePermissionsReadable | lldb::ePermissionsWritable, alloc_error); if (!alloc_error.Success()) return LLDB_INVALID_ADDRESS; else return ret; } // At this point we know that we need to hunt. // // First, go to the end of the existing allocations we've made if there are // any allocations. Otherwise start at the beginning of memory. if (m_allocations.empty()) { ret = 0x0; } else { auto back = m_allocations.rbegin(); lldb::addr_t addr = back->first; size_t alloc_size = back->second.m_size; ret = llvm::alignTo(addr + alloc_size, 4096); } // Now, if it's possible to use the GetMemoryRegionInfo API to detect mapped // regions, walk forward through memory until a region is found that // has adequate space for our allocation. if (process_is_alive) { const uint64_t end_of_memory = process_sp->GetAddressByteSize() == 8 ? 0xffffffffffffffffull : 0xffffffffull; lldbassert(process_sp->GetAddressByteSize() == 4 || end_of_memory != 0xffffffffull); MemoryRegionInfo region_info; Error err = process_sp->GetMemoryRegionInfo(ret, region_info); if (err.Success()) { while (true) { if (region_info.GetReadable() != MemoryRegionInfo::OptionalBool::eNo || region_info.GetWritable() != MemoryRegionInfo::OptionalBool::eNo || region_info.GetExecutable() != MemoryRegionInfo::OptionalBool::eNo) { if (region_info.GetRange().GetRangeEnd() - 1 >= end_of_memory) { ret = LLDB_INVALID_ADDRESS; break; } else { ret = region_info.GetRange().GetRangeEnd(); } } else if (ret + size < region_info.GetRange().GetRangeEnd()) { return ret; } else { // ret stays the same. We just need to walk a bit further. } err = process_sp->GetMemoryRegionInfo( region_info.GetRange().GetRangeEnd(), region_info); if (err.Fail()) { - lldbassert(!"GetMemoryRegionInfo() succeeded, then failed"); + lldbassert(0 && "GetMemoryRegionInfo() succeeded, then failed"); ret = LLDB_INVALID_ADDRESS; break; } } } } // We've tried our algorithm, and it didn't work. Now we have to reset back // to the end of the allocations we've already reported, or use a 'sensible' // default if this is our first allocation. if (m_allocations.empty()) { uint32_t address_byte_size = GetAddressByteSize(); if (address_byte_size != UINT32_MAX) { switch (address_byte_size) { case 8: ret = 0xffffffff00000000ull; break; case 4: ret = 0xee000000ull; break; default: break; } } } else { auto back = m_allocations.rbegin(); lldb::addr_t addr = back->first; size_t alloc_size = back->second.m_size; ret = llvm::alignTo(addr + alloc_size, 4096); } return ret; } IRMemoryMap::AllocationMap::iterator IRMemoryMap::FindAllocation(lldb::addr_t addr, size_t size) { if (addr == LLDB_INVALID_ADDRESS) return m_allocations.end(); AllocationMap::iterator iter = m_allocations.lower_bound(addr); if (iter == m_allocations.end() || iter->first > addr) { if (iter == m_allocations.begin()) return m_allocations.end(); iter--; } if (iter->first <= addr && iter->first + iter->second.m_size >= addr + size) return iter; return m_allocations.end(); } bool IRMemoryMap::IntersectsAllocation(lldb::addr_t addr, size_t size) const { if (addr == LLDB_INVALID_ADDRESS) return false; AllocationMap::const_iterator iter = m_allocations.lower_bound(addr); // Since we only know that the returned interval begins at a location greater // than or // equal to where the given interval begins, it's possible that the given // interval // intersects either the returned interval or the previous interval. Thus, we // need to // check both. Note that we only need to check these two intervals. Since all // intervals // are disjoint it is not possible that an adjacent interval does not // intersect, but a // non-adjacent interval does intersect. if (iter != m_allocations.end()) { if (AllocationsIntersect(addr, size, iter->second.m_process_start, iter->second.m_size)) return true; } if (iter != m_allocations.begin()) { --iter; if (AllocationsIntersect(addr, size, iter->second.m_process_start, iter->second.m_size)) return true; } return false; } bool IRMemoryMap::AllocationsIntersect(lldb::addr_t addr1, size_t size1, lldb::addr_t addr2, size_t size2) { // Given two half open intervals [A, B) and [X, Y), the only 6 permutations // that satisfy // AGetByteOrder(); lldb::TargetSP target_sp = m_target_wp.lock(); if (target_sp) return target_sp->GetArchitecture().GetByteOrder(); return lldb::eByteOrderInvalid; } uint32_t IRMemoryMap::GetAddressByteSize() { lldb::ProcessSP process_sp = m_process_wp.lock(); if (process_sp) return process_sp->GetAddressByteSize(); lldb::TargetSP target_sp = m_target_wp.lock(); if (target_sp) return target_sp->GetArchitecture().GetAddressByteSize(); return UINT32_MAX; } ExecutionContextScope *IRMemoryMap::GetBestExecutionContextScope() const { lldb::ProcessSP process_sp = m_process_wp.lock(); if (process_sp) return process_sp.get(); lldb::TargetSP target_sp = m_target_wp.lock(); if (target_sp) return target_sp.get(); return NULL; } IRMemoryMap::Allocation::Allocation(lldb::addr_t process_alloc, lldb::addr_t process_start, size_t size, uint32_t permissions, uint8_t alignment, AllocationPolicy policy) : m_process_alloc(process_alloc), m_process_start(process_start), m_size(size), m_permissions(permissions), m_alignment(alignment), m_policy(policy), m_leak(false) { switch (policy) { default: assert(0 && "We cannot reach this!"); case eAllocationPolicyHostOnly: m_data.SetByteSize(size); memset(m_data.GetBytes(), 0, size); break; case eAllocationPolicyProcessOnly: break; case eAllocationPolicyMirror: m_data.SetByteSize(size); memset(m_data.GetBytes(), 0, size); break; } } lldb::addr_t IRMemoryMap::Malloc(size_t size, uint8_t alignment, uint32_t permissions, AllocationPolicy policy, bool zero_memory, Error &error) { lldb_private::Log *log( lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); error.Clear(); lldb::ProcessSP process_sp; lldb::addr_t allocation_address = LLDB_INVALID_ADDRESS; lldb::addr_t aligned_address = LLDB_INVALID_ADDRESS; size_t alignment_mask = alignment - 1; size_t allocation_size; if (size == 0) allocation_size = alignment; else allocation_size = (size & alignment_mask) ? ((size + alignment) & (~alignment_mask)) : size; switch (policy) { default: error.SetErrorToGenericError(); error.SetErrorString("Couldn't malloc: invalid allocation policy"); return LLDB_INVALID_ADDRESS; case eAllocationPolicyHostOnly: allocation_address = FindSpace(allocation_size); if (allocation_address == LLDB_INVALID_ADDRESS) { error.SetErrorToGenericError(); error.SetErrorString("Couldn't malloc: address space is full"); return LLDB_INVALID_ADDRESS; } break; case eAllocationPolicyMirror: process_sp = m_process_wp.lock(); if (log) log->Printf("IRMemoryMap::%s process_sp=0x%" PRIx64 ", process_sp->CanJIT()=%s, process_sp->IsAlive()=%s", __FUNCTION__, (lldb::addr_t)process_sp.get(), process_sp && process_sp->CanJIT() ? "true" : "false", process_sp && process_sp->IsAlive() ? "true" : "false"); if (process_sp && process_sp->CanJIT() && process_sp->IsAlive()) { if (!zero_memory) allocation_address = process_sp->AllocateMemory(allocation_size, permissions, error); else allocation_address = process_sp->CallocateMemory(allocation_size, permissions, error); if (!error.Success()) return LLDB_INVALID_ADDRESS; } else { if (log) log->Printf("IRMemoryMap::%s switching to eAllocationPolicyHostOnly " "due to failed condition (see previous expr log message)", __FUNCTION__); policy = eAllocationPolicyHostOnly; allocation_address = FindSpace(allocation_size); if (allocation_address == LLDB_INVALID_ADDRESS) { error.SetErrorToGenericError(); error.SetErrorString("Couldn't malloc: address space is full"); return LLDB_INVALID_ADDRESS; } } break; case eAllocationPolicyProcessOnly: process_sp = m_process_wp.lock(); if (process_sp) { if (process_sp->CanJIT() && process_sp->IsAlive()) { if (!zero_memory) allocation_address = process_sp->AllocateMemory(allocation_size, permissions, error); else allocation_address = process_sp->CallocateMemory(allocation_size, permissions, error); if (!error.Success()) return LLDB_INVALID_ADDRESS; } else { error.SetErrorToGenericError(); error.SetErrorString( "Couldn't malloc: process doesn't support allocating memory"); return LLDB_INVALID_ADDRESS; } } else { error.SetErrorToGenericError(); error.SetErrorString("Couldn't malloc: process doesn't exist, and this " "memory must be in the process"); return LLDB_INVALID_ADDRESS; } break; } lldb::addr_t mask = alignment - 1; aligned_address = (allocation_address + mask) & (~mask); m_allocations[aligned_address] = Allocation(allocation_address, aligned_address, allocation_size, permissions, alignment, policy); if (zero_memory) { Error write_error; std::vector zero_buf(size, 0); WriteMemory(aligned_address, zero_buf.data(), size, write_error); } if (log) { const char *policy_string; switch (policy) { default: policy_string = ""; break; case eAllocationPolicyHostOnly: policy_string = "eAllocationPolicyHostOnly"; break; case eAllocationPolicyProcessOnly: policy_string = "eAllocationPolicyProcessOnly"; break; case eAllocationPolicyMirror: policy_string = "eAllocationPolicyMirror"; break; } log->Printf("IRMemoryMap::Malloc (%" PRIu64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", %s) -> 0x%" PRIx64, (uint64_t)allocation_size, (uint64_t)alignment, (uint64_t)permissions, policy_string, aligned_address); } return aligned_address; } void IRMemoryMap::Leak(lldb::addr_t process_address, Error &error) { error.Clear(); AllocationMap::iterator iter = m_allocations.find(process_address); if (iter == m_allocations.end()) { error.SetErrorToGenericError(); error.SetErrorString("Couldn't leak: allocation doesn't exist"); return; } Allocation &allocation = iter->second; allocation.m_leak = true; } void IRMemoryMap::Free(lldb::addr_t process_address, Error &error) { error.Clear(); AllocationMap::iterator iter = m_allocations.find(process_address); if (iter == m_allocations.end()) { error.SetErrorToGenericError(); error.SetErrorString("Couldn't free: allocation doesn't exist"); return; } Allocation &allocation = iter->second; switch (allocation.m_policy) { default: case eAllocationPolicyHostOnly: { lldb::ProcessSP process_sp = m_process_wp.lock(); if (process_sp) { if (process_sp->CanJIT() && process_sp->IsAlive()) process_sp->DeallocateMemory( allocation.m_process_alloc); // FindSpace allocated this for real } break; } case eAllocationPolicyMirror: case eAllocationPolicyProcessOnly: { lldb::ProcessSP process_sp = m_process_wp.lock(); if (process_sp) process_sp->DeallocateMemory(allocation.m_process_alloc); } } if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)) { log->Printf("IRMemoryMap::Free (0x%" PRIx64 ") freed [0x%" PRIx64 "..0x%" PRIx64 ")", (uint64_t)process_address, iter->second.m_process_start, iter->second.m_process_start + iter->second.m_size); } m_allocations.erase(iter); } bool IRMemoryMap::GetAllocSize(lldb::addr_t address, size_t &size) { AllocationMap::iterator iter = FindAllocation(address, size); if (iter == m_allocations.end()) return false; Allocation &al = iter->second; if (address > (al.m_process_start + al.m_size)) { size = 0; return false; } if (address > al.m_process_start) { int dif = address - al.m_process_start; size = al.m_size - dif; return true; } size = al.m_size; return true; } void IRMemoryMap::WriteMemory(lldb::addr_t process_address, const uint8_t *bytes, size_t size, Error &error) { error.Clear(); AllocationMap::iterator iter = FindAllocation(process_address, size); if (iter == m_allocations.end()) { lldb::ProcessSP process_sp = m_process_wp.lock(); if (process_sp) { process_sp->WriteMemory(process_address, bytes, size, error); return; } error.SetErrorToGenericError(); error.SetErrorString("Couldn't write: no allocation contains the target " "range and the process doesn't exist"); return; } Allocation &allocation = iter->second; uint64_t offset = process_address - allocation.m_process_start; lldb::ProcessSP process_sp; switch (allocation.m_policy) { default: error.SetErrorToGenericError(); error.SetErrorString("Couldn't write: invalid allocation policy"); return; case eAllocationPolicyHostOnly: if (!allocation.m_data.GetByteSize()) { error.SetErrorToGenericError(); error.SetErrorString("Couldn't write: data buffer is empty"); return; } ::memcpy(allocation.m_data.GetBytes() + offset, bytes, size); break; case eAllocationPolicyMirror: if (!allocation.m_data.GetByteSize()) { error.SetErrorToGenericError(); error.SetErrorString("Couldn't write: data buffer is empty"); return; } ::memcpy(allocation.m_data.GetBytes() + offset, bytes, size); process_sp = m_process_wp.lock(); if (process_sp) { process_sp->WriteMemory(process_address, bytes, size, error); if (!error.Success()) return; } break; case eAllocationPolicyProcessOnly: process_sp = m_process_wp.lock(); if (process_sp) { process_sp->WriteMemory(process_address, bytes, size, error); if (!error.Success()) return; } break; } if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)) { log->Printf("IRMemoryMap::WriteMemory (0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRId64 ") went to [0x%" PRIx64 "..0x%" PRIx64 ")", (uint64_t)process_address, (uint64_t)bytes, (uint64_t)size, (uint64_t)allocation.m_process_start, (uint64_t)allocation.m_process_start + (uint64_t)allocation.m_size); } } void IRMemoryMap::WriteScalarToMemory(lldb::addr_t process_address, Scalar &scalar, size_t size, Error &error) { error.Clear(); if (size == UINT32_MAX) size = scalar.GetByteSize(); if (size > 0) { uint8_t buf[32]; const size_t mem_size = scalar.GetAsMemoryData(buf, size, GetByteOrder(), error); if (mem_size > 0) { return WriteMemory(process_address, buf, mem_size, error); } else { error.SetErrorToGenericError(); error.SetErrorString( "Couldn't write scalar: failed to get scalar as memory data"); } } else { error.SetErrorToGenericError(); error.SetErrorString("Couldn't write scalar: its size was zero"); } return; } void IRMemoryMap::WritePointerToMemory(lldb::addr_t process_address, lldb::addr_t address, Error &error) { error.Clear(); Scalar scalar(address); WriteScalarToMemory(process_address, scalar, GetAddressByteSize(), error); } void IRMemoryMap::ReadMemory(uint8_t *bytes, lldb::addr_t process_address, size_t size, Error &error) { error.Clear(); AllocationMap::iterator iter = FindAllocation(process_address, size); if (iter == m_allocations.end()) { lldb::ProcessSP process_sp = m_process_wp.lock(); if (process_sp) { process_sp->ReadMemory(process_address, bytes, size, error); return; } lldb::TargetSP target_sp = m_target_wp.lock(); if (target_sp) { Address absolute_address(process_address); target_sp->ReadMemory(absolute_address, false, bytes, size, error); return; } error.SetErrorToGenericError(); error.SetErrorString("Couldn't read: no allocation contains the target " "range, and neither the process nor the target exist"); return; } Allocation &allocation = iter->second; uint64_t offset = process_address - allocation.m_process_start; if (offset > allocation.m_size) { error.SetErrorToGenericError(); error.SetErrorString("Couldn't read: data is not in the allocation"); return; } lldb::ProcessSP process_sp; switch (allocation.m_policy) { default: error.SetErrorToGenericError(); error.SetErrorString("Couldn't read: invalid allocation policy"); return; case eAllocationPolicyHostOnly: if (!allocation.m_data.GetByteSize()) { error.SetErrorToGenericError(); error.SetErrorString("Couldn't read: data buffer is empty"); return; } if (allocation.m_data.GetByteSize() < offset + size) { error.SetErrorToGenericError(); error.SetErrorString("Couldn't read: not enough underlying data"); return; } ::memcpy(bytes, allocation.m_data.GetBytes() + offset, size); break; case eAllocationPolicyMirror: process_sp = m_process_wp.lock(); if (process_sp) { process_sp->ReadMemory(process_address, bytes, size, error); if (!error.Success()) return; } else { if (!allocation.m_data.GetByteSize()) { error.SetErrorToGenericError(); error.SetErrorString("Couldn't read: data buffer is empty"); return; } ::memcpy(bytes, allocation.m_data.GetBytes() + offset, size); } break; case eAllocationPolicyProcessOnly: process_sp = m_process_wp.lock(); if (process_sp) { process_sp->ReadMemory(process_address, bytes, size, error); if (!error.Success()) return; } break; } if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)) { log->Printf("IRMemoryMap::ReadMemory (0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRId64 ") came from [0x%" PRIx64 "..0x%" PRIx64 ")", (uint64_t)process_address, (uint64_t)bytes, (uint64_t)size, (uint64_t)allocation.m_process_start, (uint64_t)allocation.m_process_start + (uint64_t)allocation.m_size); } } void IRMemoryMap::ReadScalarFromMemory(Scalar &scalar, lldb::addr_t process_address, size_t size, Error &error) { error.Clear(); if (size > 0) { DataBufferHeap buf(size, 0); ReadMemory(buf.GetBytes(), process_address, size, error); if (!error.Success()) return; DataExtractor extractor(buf.GetBytes(), buf.GetByteSize(), GetByteOrder(), GetAddressByteSize()); lldb::offset_t offset = 0; switch (size) { default: error.SetErrorToGenericError(); error.SetErrorStringWithFormat( "Couldn't read scalar: unsupported size %" PRIu64, (uint64_t)size); return; case 1: scalar = extractor.GetU8(&offset); break; case 2: scalar = extractor.GetU16(&offset); break; case 4: scalar = extractor.GetU32(&offset); break; case 8: scalar = extractor.GetU64(&offset); break; } } else { error.SetErrorToGenericError(); error.SetErrorString("Couldn't read scalar: its size was zero"); } return; } void IRMemoryMap::ReadPointerFromMemory(lldb::addr_t *address, lldb::addr_t process_address, Error &error) { error.Clear(); Scalar pointer_scalar; ReadScalarFromMemory(pointer_scalar, process_address, GetAddressByteSize(), error); if (!error.Success()) return; *address = pointer_scalar.ULongLong(); return; } void IRMemoryMap::GetMemoryData(DataExtractor &extractor, lldb::addr_t process_address, size_t size, Error &error) { error.Clear(); if (size > 0) { AllocationMap::iterator iter = FindAllocation(process_address, size); if (iter == m_allocations.end()) { error.SetErrorToGenericError(); error.SetErrorStringWithFormat( "Couldn't find an allocation containing [0x%" PRIx64 "..0x%" PRIx64 ")", process_address, process_address + size); return; } Allocation &allocation = iter->second; switch (allocation.m_policy) { default: error.SetErrorToGenericError(); error.SetErrorString( "Couldn't get memory data: invalid allocation policy"); return; case eAllocationPolicyProcessOnly: error.SetErrorToGenericError(); error.SetErrorString( "Couldn't get memory data: memory is only in the target"); return; case eAllocationPolicyMirror: { lldb::ProcessSP process_sp = m_process_wp.lock(); if (!allocation.m_data.GetByteSize()) { error.SetErrorToGenericError(); error.SetErrorString("Couldn't get memory data: data buffer is empty"); return; } if (process_sp) { process_sp->ReadMemory(allocation.m_process_start, allocation.m_data.GetBytes(), allocation.m_data.GetByteSize(), error); if (!error.Success()) return; uint64_t offset = process_address - allocation.m_process_start; extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size, GetByteOrder(), GetAddressByteSize()); return; } } break; case eAllocationPolicyHostOnly: if (!allocation.m_data.GetByteSize()) { error.SetErrorToGenericError(); error.SetErrorString("Couldn't get memory data: data buffer is empty"); return; } uint64_t offset = process_address - allocation.m_process_start; extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size, GetByteOrder(), GetAddressByteSize()); return; } } else { error.SetErrorToGenericError(); error.SetErrorString("Couldn't get memory data: its size was zero"); return; } } Index: vendor/lldb/dist/source/Host/common/Editline.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/Editline.cpp (revision 311541) +++ vendor/lldb/dist/source/Host/common/Editline.cpp (revision 311542) @@ -1,1369 +1,1396 @@ //===-- Editline.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 "lldb/Core/Error.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/StringList.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Editline.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/SelectHelper.h" using namespace lldb_private; using namespace lldb_private::line_editor; // Workaround for what looks like an OS X-specific issue, but other platforms // may benefit from something similar if issues arise. The libedit library // doesn't explicitly initialize the curses termcap library, which it gets away // with until TERM is set to VT100 where it stumbles over an implementation // assumption that may not exist on other platforms. The setupterm() function // would normally require headers that don't work gracefully in this context, so // the function declaraction has been hoisted here. #if defined(__APPLE__) extern "C" { int setupterm(char *term, int fildes, int *errret); } #define USE_SETUPTERM_WORKAROUND #endif // Editline uses careful cursor management to achieve the illusion of editing a // multi-line block of text // with a single line editor. Preserving this illusion requires fairly careful // management of cursor // state. Read and understand the relationship between DisplayInput(), // MoveCursor(), SetCurrentLine(), // and SaveEditedLine() before making changes. #define ESCAPE "\x1b" #define ANSI_FAINT ESCAPE "[2m" #define ANSI_UNFAINT ESCAPE "[22m" #define ANSI_CLEAR_BELOW ESCAPE "[J" #define ANSI_CLEAR_RIGHT ESCAPE "[K" #define ANSI_SET_COLUMN_N ESCAPE "[%dG" #define ANSI_UP_N_ROWS ESCAPE "[%dA" #define ANSI_DOWN_N_ROWS ESCAPE "[%dB" #if LLDB_EDITLINE_USE_WCHAR #define EditLineConstString(str) L##str #define EditLineStringFormatSpec "%ls" #else #define EditLineConstString(str) str #define EditLineStringFormatSpec "%s" // use #defines so wide version functions and structs will resolve to old // versions // for case of libedit not built with wide char support #define history_w history #define history_winit history_init #define history_wend history_end #define HistoryW History #define HistEventW HistEvent #define LineInfoW LineInfo #define el_wgets el_gets #define el_wgetc el_getc #define el_wpush el_push #define el_wparse el_parse #define el_wset el_set #define el_wget el_get #define el_wline el_line #define el_winsertstr el_insertstr #define el_wdeletestr el_deletestr #endif // #if LLDB_EDITLINE_USE_WCHAR bool IsOnlySpaces(const EditLineStringType &content) { for (wchar_t ch : content) { if (ch != EditLineCharType(' ')) return false; } return true; } EditLineStringType CombineLines(const std::vector &lines) { EditLineStringStreamType combined_stream; for (EditLineStringType line : lines) { combined_stream << line.c_str() << "\n"; } return combined_stream.str(); } std::vector SplitLines(const EditLineStringType &input) { std::vector result; size_t start = 0; while (start < input.length()) { size_t end = input.find('\n', start); if (end == std::string::npos) { result.insert(result.end(), input.substr(start)); break; } result.insert(result.end(), input.substr(start, end - start)); start = end + 1; } return result; } EditLineStringType FixIndentation(const EditLineStringType &line, int indent_correction) { if (indent_correction == 0) return line; if (indent_correction < 0) return line.substr(-indent_correction); return EditLineStringType(indent_correction, EditLineCharType(' ')) + line; } int GetIndentation(const EditLineStringType &line) { int space_count = 0; for (EditLineCharType ch : line) { if (ch != EditLineCharType(' ')) break; ++space_count; } return space_count; } bool IsInputPending(FILE *file) { // FIXME: This will be broken on Windows if we ever re-enable Editline. You // can't use select // on something that isn't a socket. This will have to be re-written to not // use a FILE*, but // instead use some kind of yet-to-be-created abstraction that select-like // functionality on // non-socket objects. const int fd = fileno(file); SelectHelper select_helper; select_helper.SetTimeout(std::chrono::microseconds(0)); select_helper.FDSetRead(fd); return select_helper.Select().Success(); } namespace lldb_private { namespace line_editor { typedef std::weak_ptr EditlineHistoryWP; // EditlineHistory objects are sometimes shared between multiple // Editline instances with the same program name. class EditlineHistory { private: // Use static GetHistory() function to get a EditlineHistorySP to one of these // objects EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries) : m_history(NULL), m_event(), m_prefix(prefix), m_path() { m_history = history_winit(); history_w(m_history, &m_event, H_SETSIZE, size); if (unique_entries) history_w(m_history, &m_event, H_SETUNIQUE, 1); } const char *GetHistoryFilePath() { if (m_path.empty() && m_history && !m_prefix.empty()) { FileSpec parent_path{"~/.lldb", true}; char history_path[PATH_MAX]; if (FileSystem::MakeDirectory(parent_path, lldb::eFilePermissionsDirectoryDefault) .Success()) { snprintf(history_path, sizeof(history_path), "~/.lldb/%s-history", m_prefix.c_str()); } else { snprintf(history_path, sizeof(history_path), "~/%s-widehistory", m_prefix.c_str()); } m_path = FileSpec(history_path, true).GetPath(); } if (m_path.empty()) return NULL; return m_path.c_str(); } public: ~EditlineHistory() { Save(); if (m_history) { history_wend(m_history); m_history = NULL; } } static EditlineHistorySP GetHistory(const std::string &prefix) { typedef std::map WeakHistoryMap; static std::recursive_mutex g_mutex; static WeakHistoryMap g_weak_map; std::lock_guard guard(g_mutex); WeakHistoryMap::const_iterator pos = g_weak_map.find(prefix); EditlineHistorySP history_sp; if (pos != g_weak_map.end()) { history_sp = pos->second.lock(); if (history_sp) return history_sp; g_weak_map.erase(pos); } history_sp.reset(new EditlineHistory(prefix, 800, true)); g_weak_map[prefix] = history_sp; return history_sp; } bool IsValid() const { return m_history != NULL; } HistoryW *GetHistoryPtr() { return m_history; } void Enter(const EditLineCharType *line_cstr) { if (m_history) history_w(m_history, &m_event, H_ENTER, line_cstr); } bool Load() { if (m_history) { const char *path = GetHistoryFilePath(); if (path) { history_w(m_history, &m_event, H_LOAD, path); return true; } } return false; } bool Save() { if (m_history) { const char *path = GetHistoryFilePath(); if (path) { history_w(m_history, &m_event, H_SAVE, path); return true; } } return false; } protected: HistoryW *m_history; // The history object HistEventW m_event; // The history event needed to contain all history events std::string m_prefix; // The prefix name (usually the editline program name) // to use when loading/saving history std::string m_path; // Path to the history file }; } } //------------------------------------------------------------------ // Editline private methods //------------------------------------------------------------------ void Editline::SetBaseLineNumber(int line_number) { std::stringstream line_number_stream; line_number_stream << line_number; m_base_line_number = line_number; m_line_number_digits = std::max(3, (int)line_number_stream.str().length() + 1); } std::string Editline::PromptForIndex(int line_index) { bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0; std::string prompt = m_set_prompt; if (use_line_numbers && prompt.length() == 0) { prompt = ": "; } std::string continuation_prompt = prompt; if (m_set_continuation_prompt.length() > 0) { continuation_prompt = m_set_continuation_prompt; // Ensure that both prompts are the same length through space padding while (continuation_prompt.length() < prompt.length()) { continuation_prompt += ' '; } while (prompt.length() < continuation_prompt.length()) { prompt += ' '; } } if (use_line_numbers) { StreamString prompt_stream; prompt_stream.Printf( "%*d%s", m_line_number_digits, m_base_line_number + line_index, (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str()); return std::move(prompt_stream.GetString()); } return (line_index == 0) ? prompt : continuation_prompt; } void Editline::SetCurrentLine(int line_index) { m_current_line_index = line_index; m_current_prompt = PromptForIndex(line_index); } int Editline::GetPromptWidth() { return (int)PromptForIndex(0).length(); } bool Editline::IsEmacs() { const char *editor; el_get(m_editline, EL_EDITOR, &editor); return editor[0] == 'e'; } bool Editline::IsOnlySpaces() { const LineInfoW *info = el_wline(m_editline); for (const EditLineCharType *character = info->buffer; character < info->lastchar; character++) { if (*character != ' ') return false; } return true; } int Editline::GetLineIndexForLocation(CursorLocation location, int cursor_row) { int line = 0; if (location == CursorLocation::EditingPrompt || location == CursorLocation::BlockEnd || location == CursorLocation::EditingCursor) { for (unsigned index = 0; index < m_current_line_index; index++) { line += CountRowsForLine(m_input_lines[index]); } if (location == CursorLocation::EditingCursor) { line += cursor_row; } else if (location == CursorLocation::BlockEnd) { for (unsigned index = m_current_line_index; index < m_input_lines.size(); index++) { line += CountRowsForLine(m_input_lines[index]); } --line; } } return line; } void Editline::MoveCursor(CursorLocation from, CursorLocation to) { const LineInfoW *info = el_wline(m_editline); int editline_cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); int editline_cursor_row = editline_cursor_position / m_terminal_width; // Determine relative starting and ending lines int fromLine = GetLineIndexForLocation(from, editline_cursor_row); int toLine = GetLineIndexForLocation(to, editline_cursor_row); if (toLine != fromLine) { fprintf(m_output_file, (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS, std::abs(toLine - fromLine)); } // Determine target column int toColumn = 1; if (to == CursorLocation::EditingCursor) { toColumn = editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1; } else if (to == CursorLocation::BlockEnd) { toColumn = ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) % 80) + 1; } fprintf(m_output_file, ANSI_SET_COLUMN_N, toColumn); } void Editline::DisplayInput(int firstIndex) { fprintf(m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1); int line_count = (int)m_input_lines.size(); const char *faint = m_color_prompts ? ANSI_FAINT : ""; const char *unfaint = m_color_prompts ? ANSI_UNFAINT : ""; for (int index = firstIndex; index < line_count; index++) { fprintf(m_output_file, "%s" "%s" "%s" EditLineStringFormatSpec " ", faint, PromptForIndex(index).c_str(), unfaint, m_input_lines[index].c_str()); if (index < line_count - 1) fprintf(m_output_file, "\n"); } } int Editline::CountRowsForLine(const EditLineStringType &content) { auto prompt = PromptForIndex(0); // Prompt width is constant during an edit session int line_length = (int)(content.length() + prompt.length()); return (line_length / m_terminal_width) + 1; } void Editline::SaveEditedLine() { const LineInfoW *info = el_wline(m_editline); m_input_lines[m_current_line_index] = EditLineStringType(info->buffer, info->lastchar - info->buffer); } StringList Editline::GetInputAsStringList(int line_count) { StringList lines; for (EditLineStringType line : m_input_lines) { if (line_count == 0) break; #if LLDB_EDITLINE_USE_WCHAR lines.AppendString(m_utf8conv.to_bytes(line)); #else lines.AppendString(line); #endif --line_count; } return lines; } unsigned char Editline::RecallHistory(bool earlier) { if (!m_history_sp || !m_history_sp->IsValid()) return CC_ERROR; HistoryW *pHistory = m_history_sp->GetHistoryPtr(); HistEventW history_event; std::vector new_input_lines; // Treat moving from the "live" entry differently if (!m_in_history) { if (earlier == false) return CC_ERROR; // Can't go newer than the "live" entry if (history_w(pHistory, &history_event, H_FIRST) == -1) return CC_ERROR; // Save any edits to the "live" entry in case we return by moving forward in // history // (it would be more bash-like to save over any current entry, but libedit // doesn't // offer the ability to add entries anywhere except the end.) SaveEditedLine(); m_live_history_lines = m_input_lines; m_in_history = true; } else { if (history_w(pHistory, &history_event, earlier ? H_NEXT : H_PREV) == -1) { // Can't move earlier than the earliest entry if (earlier) return CC_ERROR; // ... but moving to newer than the newest yields the "live" entry new_input_lines = m_live_history_lines; m_in_history = false; } } // If we're pulling the lines from history, split them apart if (m_in_history) new_input_lines = SplitLines(history_event.str); // Erase the current edit session and replace it with a new one MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); m_input_lines = new_input_lines; DisplayInput(); // Prepare to edit the last line when moving to previous entry, or the first // line // when moving to next entry SetCurrentLine(m_current_line_index = earlier ? (int)m_input_lines.size() - 1 : 0); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); return CC_NEWLINE; } int Editline::GetCharacter(EditLineCharType *c) { const LineInfoW *info = el_wline(m_editline); // Paint a faint version of the desired prompt over the version libedit draws // (will only be requested if colors are supported) if (m_needs_prompt_repaint) { MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); fprintf(m_output_file, "%s" "%s" "%s", ANSI_FAINT, Prompt(), ANSI_UNFAINT); MoveCursor(CursorLocation::EditingPrompt, CursorLocation::EditingCursor); m_needs_prompt_repaint = false; } if (m_multiline_enabled) { // Detect when the number of rows used for this input line changes due to an // edit int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); int new_line_rows = (lineLength / m_terminal_width) + 1; if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) { // Respond by repainting the current state from this line on MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); SaveEditedLine(); DisplayInput(m_current_line_index); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); } m_current_line_rows = new_line_rows; } // Read an actual character while (true) { lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; char ch = 0; // This mutex is locked by our caller (GetLine). Unlock it while we read a // character // (blocking operation), so we do not hold the mutex indefinitely. This // gives a chance // for someone to interrupt us. After Read returns, immediately lock the // mutex again and // check if we were interrupted. m_output_mutex.unlock(); int read_count = m_input_connection.Read(&ch, 1, llvm::None, status, NULL); m_output_mutex.lock(); if (m_editor_status == EditorStatus::Interrupted) { while (read_count > 0 && status == lldb::eConnectionStatusSuccess) read_count = m_input_connection.Read(&ch, 1, llvm::None, status, NULL); lldbassert(status == lldb::eConnectionStatusInterrupted); return 0; } if (read_count) { -#if LLDB_EDITLINE_USE_WCHAR - // After the initial interruptible read, this is guaranteed not to block - ungetc(ch, m_input_file); - *c = fgetwc(m_input_file); - if (*c != WEOF) + if (CompleteCharacter(ch, *c)) return 1; -#else - *c = ch; - if (ch != (char)EOF) - return 1; -#endif } else { switch (status) { case lldb::eConnectionStatusSuccess: // Success break; case lldb::eConnectionStatusInterrupted: lldbassert(0 && "Interrupts should have been handled above."); case lldb::eConnectionStatusError: // Check GetError() for details case lldb::eConnectionStatusTimedOut: // Request timed out case lldb::eConnectionStatusEndOfFile: // End-of-file encountered case lldb::eConnectionStatusNoConnection: // No connection case lldb::eConnectionStatusLostConnection: // Lost connection while // connected to a valid // connection m_editor_status = EditorStatus::EndOfInput; return 0; } } } } const char *Editline::Prompt() { if (m_color_prompts) m_needs_prompt_repaint = true; return m_current_prompt.c_str(); } unsigned char Editline::BreakLineCommand(int ch) { // Preserve any content beyond the cursor, truncate and save the current line const LineInfoW *info = el_wline(m_editline); auto current_line = EditLineStringType(info->buffer, info->cursor - info->buffer); auto new_line_fragment = EditLineStringType(info->cursor, info->lastchar - info->cursor); m_input_lines[m_current_line_index] = current_line; // Ignore whitespace-only extra fragments when breaking a line if (::IsOnlySpaces(new_line_fragment)) new_line_fragment = EditLineConstString(""); // Establish the new cursor position at the start of a line when inserting a // line break m_revert_cursor_index = 0; // Don't perform automatic formatting when pasting if (!IsInputPending(m_input_file)) { // Apply smart indentation if (m_fix_indentation_callback) { StringList lines = GetInputAsStringList(m_current_line_index + 1); #if LLDB_EDITLINE_USE_WCHAR lines.AppendString(m_utf8conv.to_bytes(new_line_fragment)); #else lines.AppendString(new_line_fragment); #endif int indent_correction = m_fix_indentation_callback( this, lines, 0, m_fix_indentation_callback_baton); new_line_fragment = FixIndentation(new_line_fragment, indent_correction); m_revert_cursor_index = GetIndentation(new_line_fragment); } } // Insert the new line and repaint everything from the split line on down m_input_lines.insert(m_input_lines.begin() + m_current_line_index + 1, new_line_fragment); MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); DisplayInput(m_current_line_index); // Reposition the cursor to the right line and prepare to edit the new line SetCurrentLine(m_current_line_index + 1); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); return CC_NEWLINE; } unsigned char Editline::EndOrAddLineCommand(int ch) { // Don't perform end of input detection when pasting, always treat this as a // line break if (IsInputPending(m_input_file)) { return BreakLineCommand(ch); } // Save any edits to this line SaveEditedLine(); // If this is the end of the last line, consider whether to add a line instead const LineInfoW *info = el_wline(m_editline); if (m_current_line_index == m_input_lines.size() - 1 && info->cursor == info->lastchar) { if (m_is_input_complete_callback) { auto lines = GetInputAsStringList(); if (!m_is_input_complete_callback(this, lines, m_is_input_complete_callback_baton)) { return BreakLineCommand(ch); } // The completion test is allowed to change the input lines when complete m_input_lines.clear(); for (unsigned index = 0; index < lines.GetSize(); index++) { #if LLDB_EDITLINE_USE_WCHAR m_input_lines.insert(m_input_lines.end(), m_utf8conv.from_bytes(lines[index])); #else m_input_lines.insert(m_input_lines.end(), lines[index]); #endif } } } MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd); fprintf(m_output_file, "\n"); m_editor_status = EditorStatus::Complete; return CC_NEWLINE; } unsigned char Editline::DeleteNextCharCommand(int ch) { LineInfoW *info = const_cast(el_wline(m_editline)); // Just delete the next character normally if possible if (info->cursor < info->lastchar) { info->cursor++; el_deletestr(m_editline, 1); return CC_REFRESH; } // Fail when at the end of the last line, except when ^D is pressed on // the line is empty, in which case it is treated as EOF if (m_current_line_index == m_input_lines.size() - 1) { if (ch == 4 && info->buffer == info->lastchar) { fprintf(m_output_file, "^D\n"); m_editor_status = EditorStatus::EndOfInput; return CC_EOF; } return CC_ERROR; } // Prepare to combine this line with the one below MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); // Insert the next line of text at the cursor and restore the cursor position const EditLineCharType *cursor = info->cursor; el_winsertstr(m_editline, m_input_lines[m_current_line_index + 1].c_str()); info->cursor = cursor; SaveEditedLine(); // Delete the extra line m_input_lines.erase(m_input_lines.begin() + m_current_line_index + 1); // Clear and repaint from this line on down DisplayInput(m_current_line_index); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); return CC_REFRESH; } unsigned char Editline::DeletePreviousCharCommand(int ch) { LineInfoW *info = const_cast(el_wline(m_editline)); // Just delete the previous character normally when not at the start of a line if (info->cursor > info->buffer) { el_deletestr(m_editline, 1); return CC_REFRESH; } // No prior line and no prior character? Let the user know if (m_current_line_index == 0) return CC_ERROR; // No prior character, but prior line? Combine with the line above SaveEditedLine(); SetCurrentLine(m_current_line_index - 1); auto priorLine = m_input_lines[m_current_line_index]; m_input_lines.erase(m_input_lines.begin() + m_current_line_index); m_input_lines[m_current_line_index] = priorLine + m_input_lines[m_current_line_index]; // Repaint from the new line down fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, CountRowsForLine(priorLine), 1); DisplayInput(m_current_line_index); // Put the cursor back where libedit expects it to be before returning to // editing // by telling libedit about the newly inserted text MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); el_winsertstr(m_editline, priorLine.c_str()); return CC_REDISPLAY; } unsigned char Editline::PreviousLineCommand(int ch) { SaveEditedLine(); if (m_current_line_index == 0) { return RecallHistory(true); } // Start from a known location MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); // Treat moving up from a blank last line as a deletion of that line if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) { m_input_lines.erase(m_input_lines.begin() + m_current_line_index); fprintf(m_output_file, ANSI_CLEAR_BELOW); } SetCurrentLine(m_current_line_index - 1); fprintf(m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, CountRowsForLine(m_input_lines[m_current_line_index]), 1); return CC_NEWLINE; } unsigned char Editline::NextLineCommand(int ch) { SaveEditedLine(); // Handle attempts to move down from the last line if (m_current_line_index == m_input_lines.size() - 1) { // Don't add an extra line if the existing last line is blank, move through // history instead if (IsOnlySpaces()) { return RecallHistory(false); } // Determine indentation for the new line int indentation = 0; if (m_fix_indentation_callback) { StringList lines = GetInputAsStringList(); lines.AppendString(""); indentation = m_fix_indentation_callback( this, lines, 0, m_fix_indentation_callback_baton); } m_input_lines.insert( m_input_lines.end(), EditLineStringType(indentation, EditLineCharType(' '))); } // Move down past the current line using newlines to force scrolling if needed SetCurrentLine(m_current_line_index + 1); const LineInfoW *info = el_wline(m_editline); int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); int cursor_row = cursor_position / m_terminal_width; for (int line_count = 0; line_count < m_current_line_rows - cursor_row; line_count++) { fprintf(m_output_file, "\n"); } return CC_NEWLINE; } unsigned char Editline::PreviousHistoryCommand(int ch) { SaveEditedLine(); return RecallHistory(true); } unsigned char Editline::NextHistoryCommand(int ch) { SaveEditedLine(); return RecallHistory(false); } unsigned char Editline::FixIndentationCommand(int ch) { if (!m_fix_indentation_callback) return CC_NORM; // Insert the character typed before proceeding EditLineCharType inserted[] = {(EditLineCharType)ch, 0}; el_winsertstr(m_editline, inserted); LineInfoW *info = const_cast(el_wline(m_editline)); int cursor_position = info->cursor - info->buffer; // Save the edits and determine the correct indentation level SaveEditedLine(); StringList lines = GetInputAsStringList(m_current_line_index + 1); int indent_correction = m_fix_indentation_callback( this, lines, cursor_position, m_fix_indentation_callback_baton); // If it is already correct no special work is needed if (indent_correction == 0) return CC_REFRESH; // Change the indentation level of the line std::string currentLine = lines.GetStringAtIndex(m_current_line_index); if (indent_correction > 0) { currentLine = currentLine.insert(0, indent_correction, ' '); } else { currentLine = currentLine.erase(0, -indent_correction); } #if LLDB_EDITLINE_USE_WCHAR m_input_lines[m_current_line_index] = m_utf8conv.from_bytes(currentLine); #else m_input_lines[m_current_line_index] = currentLine; #endif // Update the display to reflect the change MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt); DisplayInput(m_current_line_index); // Reposition the cursor back on the original line and prepare to restart // editing // with a new cursor position SetCurrentLine(m_current_line_index); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); m_revert_cursor_index = cursor_position + indent_correction; return CC_NEWLINE; } unsigned char Editline::RevertLineCommand(int ch) { el_winsertstr(m_editline, m_input_lines[m_current_line_index].c_str()); if (m_revert_cursor_index >= 0) { LineInfoW *info = const_cast(el_wline(m_editline)); info->cursor = info->buffer + m_revert_cursor_index; if (info->cursor > info->lastchar) { info->cursor = info->lastchar; } m_revert_cursor_index = -1; } return CC_REFRESH; } unsigned char Editline::BufferStartCommand(int ch) { SaveEditedLine(); MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); SetCurrentLine(0); m_revert_cursor_index = 0; return CC_NEWLINE; } unsigned char Editline::BufferEndCommand(int ch) { SaveEditedLine(); MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockEnd); SetCurrentLine((int)m_input_lines.size() - 1); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingPrompt); return CC_NEWLINE; } unsigned char Editline::TabCommand(int ch) { if (m_completion_callback == nullptr) return CC_ERROR; const LineInfo *line_info = el_line(m_editline); StringList completions; int page_size = 40; const int num_completions = m_completion_callback( line_info->buffer, line_info->cursor, line_info->lastchar, 0, // Don't skip any matches (start at match zero) -1, // Get all the matches completions, m_completion_callback_baton); if (num_completions == 0) return CC_ERROR; // if (num_completions == -1) // { // el_insertstr (m_editline, m_completion_key); // return CC_REDISPLAY; // } // else if (num_completions == -2) { // Replace the entire line with the first string... el_deletestr(m_editline, line_info->cursor - line_info->buffer); el_insertstr(m_editline, completions.GetStringAtIndex(0)); return CC_REDISPLAY; } // If we get a longer match display that first. const char *completion_str = completions.GetStringAtIndex(0); if (completion_str != nullptr && *completion_str != '\0') { el_insertstr(m_editline, completion_str); return CC_REDISPLAY; } if (num_completions > 1) { int num_elements = num_completions + 1; fprintf(m_output_file, "\n" ANSI_CLEAR_BELOW "Available completions:"); if (num_completions < page_size) { for (int i = 1; i < num_elements; i++) { completion_str = completions.GetStringAtIndex(i); fprintf(m_output_file, "\n\t%s", completion_str); } fprintf(m_output_file, "\n"); } else { int cur_pos = 1; char reply; int got_char; while (cur_pos < num_elements) { int endpoint = cur_pos + page_size; if (endpoint > num_elements) endpoint = num_elements; for (; cur_pos < endpoint; cur_pos++) { completion_str = completions.GetStringAtIndex(cur_pos); fprintf(m_output_file, "\n\t%s", completion_str); } if (cur_pos >= num_elements) { fprintf(m_output_file, "\n"); break; } fprintf(m_output_file, "\nMore (Y/n/a): "); reply = 'n'; got_char = el_getc(m_editline, &reply); if (got_char == -1 || reply == 'n') break; if (reply == 'a') page_size = num_elements - cur_pos; } } DisplayInput(); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); } return CC_REDISPLAY; } void Editline::ConfigureEditor(bool multiline) { if (m_editline && m_multiline_enabled == multiline) return; m_multiline_enabled = multiline; if (m_editline) { // Disable edit mode to stop the terminal from flushing all input // during the call to el_end() since we expect to have multiple editline // instances in this program. el_set(m_editline, EL_EDITMODE, 0); el_end(m_editline); } m_editline = el_init(m_editor_name.c_str(), m_input_file, m_output_file, m_error_file); TerminalSizeChanged(); if (m_history_sp && m_history_sp->IsValid()) { m_history_sp->Load(); el_wset(m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr()); } el_set(m_editline, EL_CLIENTDATA, this); el_set(m_editline, EL_SIGNAL, 0); el_set(m_editline, EL_EDITOR, "emacs"); el_set(m_editline, EL_PROMPT, (EditlinePromptCallbackType)([](EditLine *editline) { return Editline::InstanceFor(editline)->Prompt(); })); el_wset(m_editline, EL_GETCFN, (EditlineGetCharCallbackType)([]( EditLine *editline, EditLineCharType *c) { return Editline::InstanceFor(editline)->GetCharacter(c); })); // Commands used for multiline support, registered whether or not they're used el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-break-line"), EditLineConstString("Insert a line break"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BreakLineCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-end-or-add-line"), EditLineConstString("End editing or continue when incomplete"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->EndOrAddLineCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-delete-next-char"), EditLineConstString("Delete next character"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->DeleteNextCharCommand(ch); })); el_wset( m_editline, EL_ADDFN, EditLineConstString("lldb-delete-previous-char"), EditLineConstString("Delete previous character"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->DeletePreviousCharCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-line"), EditLineConstString("Move to previous line"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->PreviousLineCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-line"), EditLineConstString("Move to next line"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->NextLineCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-previous-history"), EditLineConstString("Move to previous history"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->PreviousHistoryCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-next-history"), EditLineConstString("Move to next history"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->NextHistoryCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-start"), EditLineConstString("Move to start of buffer"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BufferStartCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-buffer-end"), EditLineConstString("Move to end of buffer"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->BufferEndCommand(ch); })); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-fix-indentation"), EditLineConstString("Fix line indentation"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->FixIndentationCommand(ch); })); // Register the complete callback under two names for compatibility with older // clients using // custom .editrc files (largely because libedit has a bad bug where if you // have a bind command // that tries to bind to a function name that doesn't exist, it can corrupt // the heap and // crash your process later.) EditlineCommandCallbackType complete_callback = [](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->TabCommand(ch); }; el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-complete"), EditLineConstString("Invoke completion"), complete_callback); el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb_complete"), EditLineConstString("Invoke completion"), complete_callback); // General bindings we don't mind being overridden if (!multiline) { el_set(m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string } el_set(m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash in emacs mode el_set(m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to auto complete // Allow user-specific customization prior to registering bindings we // absolutely require el_source(m_editline, NULL); // Register an internal binding that external developers shouldn't use el_wset(m_editline, EL_ADDFN, EditLineConstString("lldb-revert-line"), EditLineConstString("Revert line to saved state"), (EditlineCommandCallbackType)([](EditLine *editline, int ch) { return Editline::InstanceFor(editline)->RevertLineCommand(ch); })); // Register keys that perform auto-indent correction if (m_fix_indentation_callback && m_fix_indentation_callback_chars) { char bind_key[2] = {0, 0}; const char *indent_chars = m_fix_indentation_callback_chars; while (*indent_chars) { bind_key[0] = *indent_chars; el_set(m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL); ++indent_chars; } } // Multi-line editor bindings if (multiline) { el_set(m_editline, EL_BIND, "\n", "lldb-end-or-add-line", NULL); el_set(m_editline, EL_BIND, "\r", "lldb-end-or-add-line", NULL); el_set(m_editline, EL_BIND, ESCAPE "\n", "lldb-break-line", NULL); el_set(m_editline, EL_BIND, ESCAPE "\r", "lldb-break-line", NULL); el_set(m_editline, EL_BIND, "^p", "lldb-previous-line", NULL); el_set(m_editline, EL_BIND, "^n", "lldb-next-line", NULL); el_set(m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL); el_set(m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL); el_set(m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL); el_set(m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL); // Editor-specific bindings if (IsEmacs()) { el_set(m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL); el_set(m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL); el_set(m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL); el_set(m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL); el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[A", "lldb-previous-history", NULL); el_set(m_editline, EL_BIND, ESCAPE ESCAPE "[B", "lldb-next-history", NULL); el_set(m_editline, EL_BIND, ESCAPE "[1;3A", "lldb-previous-history", NULL); el_set(m_editline, EL_BIND, ESCAPE "[1;3B", "lldb-next-history", NULL); } else { el_set(m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL); el_set(m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line", NULL); el_set(m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL); el_set(m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL); el_set(m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char", NULL); el_set(m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char", NULL); // Escape is absorbed exiting edit mode, so re-register important // sequences // without the prefix el_set(m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL); el_set(m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL); el_set(m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL); } } } //------------------------------------------------------------------ // Editline public methods //------------------------------------------------------------------ Editline *Editline::InstanceFor(EditLine *editline) { Editline *editor; el_get(editline, EL_CLIENTDATA, &editor); return editor; } Editline::Editline(const char *editline_name, FILE *input_file, FILE *output_file, FILE *error_file, bool color_prompts) : m_editor_status(EditorStatus::Complete), m_color_prompts(color_prompts), m_input_file(input_file), m_output_file(output_file), m_error_file(error_file), m_input_connection(fileno(input_file), false) { // Get a shared history instance m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name; m_history_sp = EditlineHistory::GetHistory(m_editor_name); #ifdef USE_SETUPTERM_WORKAROUND if (m_output_file) { const int term_fd = fileno(m_output_file); if (term_fd != -1) { static std::mutex *g_init_terminal_fds_mutex_ptr = nullptr; static std::set *g_init_terminal_fds_ptr = nullptr; static std::once_flag g_once_flag; std::call_once(g_once_flag, [&]() { g_init_terminal_fds_mutex_ptr = new std::mutex(); // NOTE: Leak to avoid C++ destructor chain issues g_init_terminal_fds_ptr = new std::set(); // NOTE: Leak to avoid // C++ destructor chain // issues }); // We must make sure to initialize the terminal a given file descriptor // only once. If we do this multiple times, we start leaking memory. std::lock_guard guard(*g_init_terminal_fds_mutex_ptr); if (g_init_terminal_fds_ptr->find(term_fd) == g_init_terminal_fds_ptr->end()) { g_init_terminal_fds_ptr->insert(term_fd); setupterm((char *)0, term_fd, (int *)0); } } } #endif } Editline::~Editline() { if (m_editline) { // Disable edit mode to stop the terminal from flushing all input // during the call to el_end() since we expect to have multiple editline // instances in this program. el_set(m_editline, EL_EDITMODE, 0); el_end(m_editline); m_editline = nullptr; } // EditlineHistory objects are sometimes shared between multiple // Editline instances with the same program name. So just release // our shared pointer and if we are the last owner, it will save the // history to the history save file automatically. m_history_sp.reset(); } void Editline::SetPrompt(const char *prompt) { m_set_prompt = prompt == nullptr ? "" : prompt; } void Editline::SetContinuationPrompt(const char *continuation_prompt) { m_set_continuation_prompt = continuation_prompt == nullptr ? "" : continuation_prompt; } void Editline::TerminalSizeChanged() { if (m_editline != nullptr) { el_resize(m_editline); int columns; // Despite the man page claiming non-zero indicates success, it's actually // zero if (el_get(m_editline, EL_GETTC, "co", &columns) == 0) { m_terminal_width = columns; if (m_current_line_rows != -1) { const LineInfoW *info = el_wline(m_editline); int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); m_current_line_rows = (lineLength / columns) + 1; } } else { m_terminal_width = INT_MAX; m_current_line_rows = 1; } } } const char *Editline::GetPrompt() { return m_set_prompt.c_str(); } uint32_t Editline::GetCurrentLine() { return m_current_line_index; } bool Editline::Interrupt() { bool result = true; std::lock_guard guard(m_output_mutex); if (m_editor_status == EditorStatus::Editing) { fprintf(m_output_file, "^C\n"); result = m_input_connection.InterruptRead(); } m_editor_status = EditorStatus::Interrupted; return result; } bool Editline::Cancel() { bool result = true; std::lock_guard guard(m_output_mutex); if (m_editor_status == EditorStatus::Editing) { MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); fprintf(m_output_file, ANSI_CLEAR_BELOW); result = m_input_connection.InterruptRead(); } m_editor_status = EditorStatus::Interrupted; return result; } void Editline::SetAutoCompleteCallback(CompleteCallbackType callback, void *baton) { m_completion_callback = callback; m_completion_callback_baton = baton; } void Editline::SetIsInputCompleteCallback(IsInputCompleteCallbackType callback, void *baton) { m_is_input_complete_callback = callback; m_is_input_complete_callback_baton = baton; } bool Editline::SetFixIndentationCallback(FixIndentationCallbackType callback, void *baton, const char *indent_chars) { m_fix_indentation_callback = callback; m_fix_indentation_callback_baton = baton; m_fix_indentation_callback_chars = indent_chars; return false; } bool Editline::GetLine(std::string &line, bool &interrupted) { ConfigureEditor(false); m_input_lines = std::vector(); m_input_lines.insert(m_input_lines.begin(), EditLineConstString("")); std::lock_guard guard(m_output_mutex); lldbassert(m_editor_status != EditorStatus::Editing); if (m_editor_status == EditorStatus::Interrupted) { m_editor_status = EditorStatus::Complete; interrupted = true; return true; } SetCurrentLine(0); m_in_history = false; m_editor_status = EditorStatus::Editing; m_revert_cursor_index = -1; int count; auto input = el_wgets(m_editline, &count); interrupted = m_editor_status == EditorStatus::Interrupted; if (!interrupted) { if (input == nullptr) { fprintf(m_output_file, "\n"); m_editor_status = EditorStatus::EndOfInput; } else { m_history_sp->Enter(input); #if LLDB_EDITLINE_USE_WCHAR line = m_utf8conv.to_bytes(SplitLines(input)[0]); #else line = SplitLines(input)[0]; #endif m_editor_status = EditorStatus::Complete; } } return m_editor_status != EditorStatus::EndOfInput; } bool Editline::GetLines(int first_line_number, StringList &lines, bool &interrupted) { ConfigureEditor(true); // Print the initial input lines, then move the cursor back up to the start of // input SetBaseLineNumber(first_line_number); m_input_lines = std::vector(); m_input_lines.insert(m_input_lines.begin(), EditLineConstString("")); std::lock_guard guard(m_output_mutex); // Begin the line editing loop DisplayInput(); SetCurrentLine(0); MoveCursor(CursorLocation::BlockEnd, CursorLocation::BlockStart); m_editor_status = EditorStatus::Editing; m_in_history = false; m_revert_cursor_index = -1; while (m_editor_status == EditorStatus::Editing) { int count; m_current_line_rows = -1; el_wpush(m_editline, EditLineConstString( "\x1b[^")); // Revert to the existing line content el_wgets(m_editline, &count); } interrupted = m_editor_status == EditorStatus::Interrupted; if (!interrupted) { // Save the completed entry in history before returning m_history_sp->Enter(CombineLines(m_input_lines).c_str()); lines = GetInputAsStringList(); } return m_editor_status != EditorStatus::EndOfInput; } void Editline::PrintAsync(Stream *stream, const char *s, size_t len) { std::lock_guard guard(m_output_mutex); if (m_editor_status == EditorStatus::Editing) { MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); fprintf(m_output_file, ANSI_CLEAR_BELOW); } stream->Write(s, len); stream->Flush(); if (m_editor_status == EditorStatus::Editing) { DisplayInput(); MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); } +} + +bool Editline::CompleteCharacter(char ch, EditLineCharType &out) { +#if !LLDB_EDITLINE_USE_WCHAR + if (ch == (char)EOF) + return false; + + out = ch; + return true; +#else + std::codecvt_utf8 cvt; + llvm::SmallString<4> input; + for (;;) { + const char *from_next; + wchar_t *to_next; + std::mbstate_t state = std::mbstate_t(); + input.push_back(ch); + switch (cvt.in(state, input.begin(), input.end(), from_next, &out, &out + 1, + to_next)) { + case std::codecvt_base::ok: + return out != WEOF; + + case std::codecvt_base::error: + case std::codecvt_base::noconv: + return false; + + case std::codecvt_base::partial: + lldb::ConnectionStatus status; + size_t read_count = m_input_connection.Read( + &ch, 1, std::chrono::seconds(0), status, nullptr); + if (read_count == 0) + return false; + break; + } + } +#endif } Index: vendor/lldb/dist/source/Host/windows/EditLineWin.cpp =================================================================== --- vendor/lldb/dist/source/Host/windows/EditLineWin.cpp (revision 311541) +++ vendor/lldb/dist/source/Host/windows/EditLineWin.cpp (revision 311542) @@ -1,351 +1,350 @@ //===-- EditLineWin.cpp -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // this file is only relevant for Visual C++ #if defined(_WIN32) #include "lldb/Host/windows/windows.h" #include "lldb/Host/windows/editlinewin.h" +#include "llvm/Support/ErrorHandling.h" #include #include // edit line EL_ADDFN function pointer type typedef unsigned char (*el_addfn_func)(EditLine *e, int ch); typedef const char *(*el_prompt_func)(EditLine *); // edit line wrapper binding container struct el_binding { // const char *name; const char *help; // function pointer to callback routine el_addfn_func func; // ascii key this function is bound to const char *key; }; // stored key bindings static std::vector _bindings; // TODO: this should in fact be related to the exact edit line context we create static void *clientData = NULL; // store the current prompt string // default to what we expect to receive anyway static const char *_prompt = "(lldb) "; #if !defined(_WIP_INPUT_METHOD) static char *el_get_s(char *buffer, int chars) { return gets_s(buffer, chars); } #else static void con_output(char _in) { HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE); DWORD written = 0; // get the cursor position CONSOLE_SCREEN_BUFFER_INFO info; GetConsoleScreenBufferInfo(hout, &info); // output this char WriteConsoleOutputCharacterA(hout, &_in, 1, info.dwCursorPosition, &written); // advance cursor position info.dwCursorPosition.X++; SetConsoleCursorPosition(hout, info.dwCursorPosition); } static void con_backspace(void) { HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE); DWORD written = 0; // get cursor position CONSOLE_SCREEN_BUFFER_INFO info; GetConsoleScreenBufferInfo(hout, &info); // nudge cursor backwards info.dwCursorPosition.X--; SetConsoleCursorPosition(hout, info.dwCursorPosition); // blank out the last character WriteConsoleOutputCharacterA(hout, " ", 1, info.dwCursorPosition, &written); } static void con_return(void) { HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE); DWORD written = 0; // get cursor position CONSOLE_SCREEN_BUFFER_INFO info; GetConsoleScreenBufferInfo(hout, &info); // move onto the new line info.dwCursorPosition.X = 0; info.dwCursorPosition.Y++; SetConsoleCursorPosition(hout, info.dwCursorPosition); } static bool runBind(char _key) { for (int i = 0; i < _bindings.size(); i++) { el_binding *bind = _bindings[i]; if (bind->key[0] == _key) { bind->func((EditLine *)-1, _key); return true; } } return false; } // replacement get_s which is EL_BIND aware static char *el_get_s(char *buffer, int chars) { // char *head = buffer; // for (;; Sleep(10)) { // INPUT_RECORD _record; // DWORD _read = 0; if (ReadConsoleInputA(GetStdHandle(STD_INPUT_HANDLE), &_record, 1, &_read) == FALSE) break; // if we didn't read a key if (_read == 0) continue; // only interested in key events if (_record.EventType != KEY_EVENT) continue; // is the key down if (!_record.Event.KeyEvent.bKeyDown) continue; // read the ascii key character char _key = _record.Event.KeyEvent.uChar.AsciiChar; // non ascii conformant key press if (_key == 0) { // check the scan code // if VK_UP scroll back through history // if VK_DOWN scroll forward through history continue; } // try to execute any bind this key may have if (runBind(_key)) continue; // if we read a return key if (_key == '\n' || _key == '\r') { con_return(); break; } // key is backspace if (_key == 0x8) { // avoid deleting past beginning if (head > buffer) { con_backspace(); head--; } continue; } // add this key to the input buffer if ((head - buffer) < (chars - 1)) { con_output(_key); *(head++) = _key; } } // insert end of line character *head = '\0'; return buffer; } #endif // edit line initialize EditLine *el_init(const char *, FILE *, FILE *, FILE *) { // SetConsoleTitleA("lldb"); // return dummy handle return (EditLine *)-1; } const char *el_gets(EditLine *el, int *length) { // print the prompt if we have one if (_prompt != NULL) printf("%s", _prompt); // create a buffer for the user input char *buffer = new char[MAX_PATH]; // try to get user input string if (el_get_s(buffer, MAX_PATH)) { // get the string length in 'length' while (buffer[*length] != '\0') (*length)++; // return the input buffer // remember that this memory has the be free'd somewhere return buffer; } else { // on error delete[] buffer; return NULL; } } int el_set(EditLine *el, int code, ...) { va_list vl; va_start(vl, code); // switch (code) { // edit line set prompt message case (EL_PROMPT): { // EL_PROMPT, char *(*f)( EditLine *) // define a prompt printing function as 'f', which is to return a // string that // contains the prompt. // get the function pointer from the arg list void *func_vp = (void *)va_arg(vl, el_prompt_func); // cast to suitable prototype el_prompt_func func_fp = (el_prompt_func)func_vp; // call to get the prompt as a string _prompt = func_fp(el); } break; case (EL_PROMPT_ESC): { // EL_PROMPT, char *(*f)( EditLine *) // define a prompt printing function as 'f', which is to return a // string that // contains the prompt. // get the function pointer from the arg list void *func_vp = (void *)va_arg(vl, el_prompt_func); va_arg(vl, int); // call to get the prompt as a string el_prompt_func func_fp = (el_prompt_func)func_vp; _prompt = func_fp(el); } break; case (EL_EDITOR): { // EL_EDITOR, const char *mode // set editing mode to "emacs" or "vi" } break; case (EL_HIST): { // EL_HIST, History *(*fun)(History *, int op, ... ), const char *ptr // defines which history function to use, which is usually history(). // Ptr should be the // value returned by history_init(). } break; case (EL_ADDFN): { // EL_ADDFN, const char *name, const char *help, unsigned char // (*func)(EditLine *e, int ch) // add a user defined function, func), referred to as 'name' which is // invoked when a key which is bound to 'name' is // entered. 'help' is a description of 'name'. at invocation time, 'ch' // is the key which caused the invocation. the // return value of 'func()' should be one of: // CC_NORM add a normal character // CC_NEWLINE end of line was entered // CC_EOF EOF was entered // CC_ARGHACK expecting further command input as arguments, do // nothing visually. // CC_REFRESH refresh display. // CC_REFRESH_BEEP refresh display and beep. // CC_CURSOR cursor moved so update and perform CC_REFRESH // CC_REDISPLAY redisplay entire input line. this is useful // if a key binding outputs extra information. // CC_ERROR an error occurred. beep and flush tty. // CC_FATAL fatal error, reset tty to known state. el_binding *binding = new el_binding; binding->name = va_arg(vl, const char *); binding->help = va_arg(vl, const char *); binding->func = va_arg(vl, el_addfn_func); binding->key = 0; // add this to the bindings list _bindings.push_back(binding); } break; case (EL_BIND): { // EL_BIND, const char *, ..., NULL // perform the BIND built-in command. Refer to editrc(5) for more // information. const char *name = va_arg(vl, const char *); for (auto bind : _bindings) { if (strcmp(bind->name, name) == 0) { bind->key = va_arg(vl, const char *); break; } } } break; case (EL_CLIENTDATA): { clientData = va_arg(vl, void *); } break; } return 0; } void el_end(EditLine *el) { // assert( !"Not implemented!" ); } -void el_reset(EditLine *) { assert(!"Not implemented!"); } +void el_reset(EditLine *) { llvm_unreachable("Not implemented!"); } int el_getc(EditLine *, char *) { - assert(!"Not implemented!"); - return 0; + llvm_unreachable("Not implemented!"); } void el_push(EditLine *, const char *) {} void el_beep(EditLine *) { Beep(1000, 500); } int el_parse(EditLine *, int, const char **) { - assert(!"Not implemented!"); - return 0; + llvm_unreachable("Not implemented!"); } int el_get(EditLine *el, int code, ...) { va_list vl; va_start(vl, code); switch (code) { case (EL_CLIENTDATA): { void **dout = va_arg(vl, void **); *dout = clientData; } break; default: - assert(!"Not implemented!"); + llvm_unreachable("Not implemented!"); } return 0; } int el_source(EditLine *el, const char *file) { // init edit line by reading the contents of 'file' // nothing to do here on windows... return 0; } -void el_resize(EditLine *) { assert(!"Not implemented!"); } +void el_resize(EditLine *) { llvm_unreachable("Not implemented!"); } const LineInfo *el_line(EditLine *el) { return 0; } int el_insertstr(EditLine *, const char *) { // assert( !"Not implemented!" ); return 0; } -void el_deletestr(EditLine *, int) { assert(!"Not implemented!"); } +void el_deletestr(EditLine *, int) { llvm_unreachable("Not implemented!"); } History *history_init(void) { // return dummy handle return (History *)-1; } void history_end(History *) { // assert( !"Not implemented!" ); } int history(History *, HistEvent *, int op, ...) { // perform operation 'op' on the history list with // optional arguments as needed by the operation. return 0; } #endif Index: vendor/lldb/dist/source/Interpreter/OptionValueProperties.cpp =================================================================== --- vendor/lldb/dist/source/Interpreter/OptionValueProperties.cpp (revision 311541) +++ vendor/lldb/dist/source/Interpreter/OptionValueProperties.cpp (revision 311542) @@ -1,676 +1,675 @@ //===-- OptionValueProperties.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/OptionValueProperties.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/Flags.h" #include "lldb/Core/Stream.h" #include "lldb/Core/StringList.h" #include "lldb/Core/UserSettingsController.h" #include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/OptionValues.h" #include "lldb/Interpreter/Property.h" using namespace lldb; using namespace lldb_private; OptionValueProperties::OptionValueProperties(const ConstString &name) : OptionValue(), m_name(name), m_properties(), m_name_to_index() {} OptionValueProperties::OptionValueProperties( const OptionValueProperties &global_properties) : OptionValue(global_properties), std::enable_shared_from_this(), m_name(global_properties.m_name), m_properties(global_properties.m_properties), m_name_to_index(global_properties.m_name_to_index) { // We now have an exact copy of "global_properties". We need to now // find all non-global settings and copy the property values so that // all non-global settings get new OptionValue instances created for // them. const size_t num_properties = m_properties.size(); for (size_t i = 0; i < num_properties; ++i) { // Duplicate any values that are not global when constructing properties // from // a global copy if (m_properties[i].IsGlobal() == false) { lldb::OptionValueSP new_value_sp(m_properties[i].GetValue()->DeepCopy()); m_properties[i].SetOptionValue(new_value_sp); } } } size_t OptionValueProperties::GetNumProperties() const { return m_properties.size(); } void OptionValueProperties::Initialize(const PropertyDefinition *defs) { for (size_t i = 0; defs[i].name; ++i) { Property property(defs[i]); assert(property.IsValid()); m_name_to_index.Append(property.GetName(), m_properties.size()); property.GetValue()->SetParent(shared_from_this()); m_properties.push_back(property); } m_name_to_index.Sort(); } void OptionValueProperties::SetValueChangedCallback( uint32_t property_idx, OptionValueChangedCallback callback, void *baton) { Property *property = ProtectedGetPropertyAtIndex(property_idx); if (property) property->SetValueChangedCallback(callback, baton); } void OptionValueProperties::AppendProperty(const ConstString &name, const ConstString &desc, bool is_global, const OptionValueSP &value_sp) { Property property(name, desc, is_global, value_sp); m_name_to_index.Append(name.GetStringRef(), m_properties.size()); m_properties.push_back(property); value_sp->SetParent(shared_from_this()); m_name_to_index.Sort(); } // bool // OptionValueProperties::GetQualifiedName (Stream &strm) //{ // bool dumped_something = false; //// lldb::OptionValuePropertiesSP parent_sp(GetParent ()); //// if (parent_sp) //// { //// parent_sp->GetQualifiedName (strm); //// strm.PutChar('.'); //// dumped_something = true; //// } // if (m_name) // { // strm << m_name; // dumped_something = true; // } // return dumped_something; //} // lldb::OptionValueSP OptionValueProperties::GetValueForKey(const ExecutionContext *exe_ctx, const ConstString &key, bool will_modify) const { lldb::OptionValueSP value_sp; size_t idx = m_name_to_index.Find(key.GetStringRef(), SIZE_MAX); if (idx < m_properties.size()) value_sp = GetPropertyAtIndex(exe_ctx, will_modify, idx)->GetValue(); return value_sp; } lldb::OptionValueSP OptionValueProperties::GetSubValue(const ExecutionContext *exe_ctx, llvm::StringRef name, bool will_modify, Error &error) const { lldb::OptionValueSP value_sp; if (name.empty()) return OptionValueSP(); llvm::StringRef sub_name; ConstString key; size_t key_len = name.find_first_of(".[{"); if (key_len != llvm::StringRef::npos) { key.SetString(name.take_front(key_len)); sub_name = name.drop_front(key_len); } else key.SetString(name); value_sp = GetValueForKey(exe_ctx, key, will_modify); if (sub_name.empty() || !value_sp) return value_sp; switch (sub_name[0]) { case '.': { lldb::OptionValueSP return_val_sp; return_val_sp = value_sp->GetSubValue(exe_ctx, sub_name.drop_front(), will_modify, error); if (!return_val_sp) { if (Properties::IsSettingExperimental(sub_name.drop_front())) { size_t experimental_len = strlen(Properties::GetExperimentalSettingsName()); if (sub_name[experimental_len + 1] == '.') return_val_sp = value_sp->GetSubValue( exe_ctx, sub_name.drop_front(experimental_len + 2), will_modify, error); // It isn't an error if an experimental setting is not present. if (!return_val_sp) error.Clear(); } } return return_val_sp; } case '{': // Predicate matching for predicates like // "{}" // strings are parsed by the current OptionValueProperties subclass // to mean whatever they want to. For instance a subclass of // OptionValueProperties for a lldb_private::Target might implement: // "target.run-args{arch==i386}" -- only set run args if the arch is // i386 // "target.run-args{path=/tmp/a/b/c/a.out}" -- only set run args if the // path matches // "target.run-args{basename==test&&arch==x86_64}" -- only set run args // if executable basename is "test" and arch is "x86_64" if (sub_name[1]) { llvm::StringRef predicate_start = sub_name.drop_front(); size_t pos = predicate_start.find_first_of('}'); if (pos != llvm::StringRef::npos) { auto predicate = predicate_start.take_front(pos); auto rest = predicate_start.drop_front(pos); if (PredicateMatches(exe_ctx, predicate)) { if (!rest.empty()) { // Still more subvalue string to evaluate return value_sp->GetSubValue(exe_ctx, rest, will_modify, error); } else { // We have a match! break; } } } } // Predicate didn't match or wasn't correctly formed value_sp.reset(); break; case '[': // Array or dictionary access for subvalues like: // "[12]" -- access 12th array element // "['hello']" -- dictionary access of key named hello return value_sp->GetSubValue(exe_ctx, sub_name, will_modify, error); default: value_sp.reset(); break; } return value_sp; } Error OptionValueProperties::SetSubValue(const ExecutionContext *exe_ctx, VarSetOperationType op, llvm::StringRef name, llvm::StringRef value) { Error error; const bool will_modify = true; lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, will_modify, error)); if (value_sp) error = value_sp->SetValueFromString(value, op); else { if (error.AsCString() == nullptr) error.SetErrorStringWithFormat("invalid value path '%s'", name.str().c_str()); } return error; } uint32_t OptionValueProperties::GetPropertyIndex(const ConstString &name) const { return m_name_to_index.Find(name.GetStringRef(), SIZE_MAX); } const Property * OptionValueProperties::GetProperty(const ExecutionContext *exe_ctx, bool will_modify, const ConstString &name) const { return GetPropertyAtIndex( exe_ctx, will_modify, m_name_to_index.Find(name.GetStringRef(), SIZE_MAX)); } const Property *OptionValueProperties::GetPropertyAtIndex( const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const { return ProtectedGetPropertyAtIndex(idx); } lldb::OptionValueSP OptionValueProperties::GetPropertyValueAtIndex( const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const { const Property *setting = GetPropertyAtIndex(exe_ctx, will_modify, idx); if (setting) return setting->GetValue(); return OptionValueSP(); } OptionValuePathMappings * OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings( const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const { OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx)); if (value_sp) return value_sp->GetAsPathMappings(); return nullptr; } OptionValueFileSpecList * OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList( const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const { OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx)); if (value_sp) return value_sp->GetAsFileSpecList(); return nullptr; } OptionValueArch *OptionValueProperties::GetPropertyAtIndexAsOptionValueArch( const ExecutionContext *exe_ctx, uint32_t idx) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); if (property) return property->GetValue()->GetAsArch(); return nullptr; } OptionValueLanguage * OptionValueProperties::GetPropertyAtIndexAsOptionValueLanguage( const ExecutionContext *exe_ctx, uint32_t idx) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); if (property) return property->GetValue()->GetAsLanguage(); return nullptr; } bool OptionValueProperties::GetPropertyAtIndexAsArgs( const ExecutionContext *exe_ctx, uint32_t idx, Args &args) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) { const OptionValueArray *array = value->GetAsArray(); if (array) return array->GetArgs(args); else { const OptionValueDictionary *dict = value->GetAsDictionary(); if (dict) return dict->GetArgs(args); } } } return false; } bool OptionValueProperties::SetPropertyAtIndexFromArgs( const ExecutionContext *exe_ctx, uint32_t idx, const Args &args) { const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) { OptionValueArray *array = value->GetAsArray(); if (array) return array->SetArgs(args, eVarSetOperationAssign).Success(); else { OptionValueDictionary *dict = value->GetAsDictionary(); if (dict) return dict->SetArgs(args, eVarSetOperationAssign).Success(); } } } return false; } bool OptionValueProperties::GetPropertyAtIndexAsBoolean( const ExecutionContext *exe_ctx, uint32_t idx, bool fail_value) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->GetBooleanValue(fail_value); } return fail_value; } bool OptionValueProperties::SetPropertyAtIndexAsBoolean( const ExecutionContext *exe_ctx, uint32_t idx, bool new_value) { const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) { value->SetBooleanValue(new_value); return true; } } return false; } OptionValueDictionary * OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary( const ExecutionContext *exe_ctx, uint32_t idx) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); if (property) return property->GetValue()->GetAsDictionary(); return nullptr; } int64_t OptionValueProperties::GetPropertyAtIndexAsEnumeration( const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->GetEnumerationValue(fail_value); } return fail_value; } bool OptionValueProperties::SetPropertyAtIndexAsEnumeration( const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) { const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->SetEnumerationValue(new_value); } return false; } const FormatEntity::Entry * OptionValueProperties::GetPropertyAtIndexAsFormatEntity( const ExecutionContext *exe_ctx, uint32_t idx) { const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->GetFormatEntity(); } return nullptr; } OptionValueFileSpec * OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec( const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->GetAsFileSpec(); } return nullptr; } FileSpec OptionValueProperties::GetPropertyAtIndexAsFileSpec( const ExecutionContext *exe_ctx, uint32_t idx) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->GetFileSpecValue(); } return FileSpec(); } bool OptionValueProperties::SetPropertyAtIndexAsFileSpec( const ExecutionContext *exe_ctx, uint32_t idx, const FileSpec &new_file_spec) { const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->SetFileSpecValue(new_file_spec); } return false; } const RegularExpression * OptionValueProperties::GetPropertyAtIndexAsOptionValueRegex( const ExecutionContext *exe_ctx, uint32_t idx) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->GetRegexValue(); } return nullptr; } OptionValueSInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64( const ExecutionContext *exe_ctx, uint32_t idx) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->GetAsSInt64(); } return nullptr; } int64_t OptionValueProperties::GetPropertyAtIndexAsSInt64( const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->GetSInt64Value(fail_value); } return fail_value; } bool OptionValueProperties::SetPropertyAtIndexAsSInt64( const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value) { const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->SetSInt64Value(new_value); } return false; } llvm::StringRef OptionValueProperties::GetPropertyAtIndexAsString( const ExecutionContext *exe_ctx, uint32_t idx, llvm::StringRef fail_value) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->GetStringValue(fail_value); } return fail_value; } bool OptionValueProperties::SetPropertyAtIndexAsString( const ExecutionContext *exe_ctx, uint32_t idx, llvm::StringRef new_value) { const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->SetStringValue(new_value); } return false; } OptionValueString *OptionValueProperties::GetPropertyAtIndexAsOptionValueString( const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const { OptionValueSP value_sp(GetPropertyValueAtIndex(exe_ctx, will_modify, idx)); if (value_sp) return value_sp->GetAsString(); return nullptr; } uint64_t OptionValueProperties::GetPropertyAtIndexAsUInt64( const ExecutionContext *exe_ctx, uint32_t idx, uint64_t fail_value) const { const Property *property = GetPropertyAtIndex(exe_ctx, false, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->GetUInt64Value(fail_value); } return fail_value; } bool OptionValueProperties::SetPropertyAtIndexAsUInt64( const ExecutionContext *exe_ctx, uint32_t idx, uint64_t new_value) { const Property *property = GetPropertyAtIndex(exe_ctx, true, idx); if (property) { OptionValue *value = property->GetValue().get(); if (value) return value->SetUInt64Value(new_value); } return false; } bool OptionValueProperties::Clear() { const size_t num_properties = m_properties.size(); for (size_t i = 0; i < num_properties; ++i) m_properties[i].GetValue()->Clear(); return true; } Error OptionValueProperties::SetValueFromString(llvm::StringRef value, VarSetOperationType op) { Error error; // Args args(value_cstr); // const size_t argc = args.GetArgumentCount(); switch (op) { case eVarSetOperationClear: Clear(); break; case eVarSetOperationReplace: case eVarSetOperationAssign: case eVarSetOperationRemove: case eVarSetOperationInsertBefore: case eVarSetOperationInsertAfter: case eVarSetOperationAppend: case eVarSetOperationInvalid: error = OptionValue::SetValueFromString(value, op); break; } return error; } void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) { const size_t num_properties = m_properties.size(); for (size_t i = 0; i < num_properties; ++i) { const Property *property = GetPropertyAtIndex(exe_ctx, false, i); if (property) { OptionValue *option_value = property->GetValue().get(); assert(option_value); const bool transparent_value = option_value->ValueIsTransparent(); property->Dump(exe_ctx, strm, dump_mask); if (!transparent_value) strm.EOL(); } } } Error OptionValueProperties::DumpPropertyValue(const ExecutionContext *exe_ctx, Stream &strm, llvm::StringRef property_path, uint32_t dump_mask) { Error error; const bool will_modify = false; lldb::OptionValueSP value_sp( GetSubValue(exe_ctx, property_path, will_modify, error)); if (value_sp) { if (!value_sp->ValueIsTransparent()) { if (dump_mask & eDumpOptionName) strm.PutCString(property_path); if (dump_mask & ~eDumpOptionName) strm.PutChar(' '); } value_sp->DumpValue(exe_ctx, strm, dump_mask); } return error; } lldb::OptionValueSP OptionValueProperties::DeepCopy() const { - assert(!"this shouldn't happen"); - return lldb::OptionValueSP(); + llvm_unreachable("this shouldn't happen"); } const Property *OptionValueProperties::GetPropertyAtPath( const ExecutionContext *exe_ctx, bool will_modify, llvm::StringRef name) const { const Property *property = nullptr; if (name.empty()) return nullptr; llvm::StringRef sub_name; ConstString key; size_t key_len = name.find_first_of(".[{"); if (key_len != llvm::StringRef::npos) { key.SetString(name.take_front(key_len)); sub_name = name.drop_front(key_len); } else key.SetString(name); property = GetProperty(exe_ctx, will_modify, key); if (sub_name.empty() || !property) return property; if (sub_name[0] == '.') { OptionValueProperties *sub_properties = property->GetValue()->GetAsProperties(); if (sub_properties) return sub_properties->GetPropertyAtPath(exe_ctx, will_modify, sub_name.drop_front()); } return nullptr; } void OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter, Stream &strm) const { size_t max_name_len = 0; const size_t num_properties = m_properties.size(); for (size_t i = 0; i < num_properties; ++i) { const Property *property = ProtectedGetPropertyAtIndex(i); if (property) max_name_len = std::max(property->GetName().size(), max_name_len); } for (size_t i = 0; i < num_properties; ++i) { const Property *property = ProtectedGetPropertyAtIndex(i); if (property) property->DumpDescription(interpreter, strm, max_name_len, false); } } void OptionValueProperties::Apropos( llvm::StringRef keyword, std::vector &matching_properties) const { const size_t num_properties = m_properties.size(); StreamString strm; for (size_t i = 0; i < num_properties; ++i) { const Property *property = ProtectedGetPropertyAtIndex(i); if (property) { const OptionValueProperties *properties = property->GetValue()->GetAsProperties(); if (properties) { properties->Apropos(keyword, matching_properties); } else { bool match = false; llvm::StringRef name = property->GetName(); if (name.contains_lower(keyword)) match = true; else { llvm::StringRef desc = property->GetDescription(); if (desc.contains_lower(keyword)) match = true; } if (match) { matching_properties.push_back(property); } } } } } lldb::OptionValuePropertiesSP OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx, const ConstString &name) { lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, name, false)); if (option_value_sp) { OptionValueProperties *ov_properties = option_value_sp->GetAsProperties(); if (ov_properties) return ov_properties->shared_from_this(); } return lldb::OptionValuePropertiesSP(); } Index: vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp (revision 311542) @@ -1,679 +1,680 @@ //===-- ClangModulesDeclVendor.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 #include "clang/Basic/TargetInfo.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Parse/Parser.h" #include "clang/Sema/Lookup.h" #include "clang/Serialization/ASTReader.h" #include "llvm/Support/Path.h" // Project includes #include "ClangModulesDeclVendor.h" #include "lldb/Core/Log.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Target/Target.h" #include "lldb/Utility/LLDBAssert.h" using namespace lldb_private; namespace { // Any Clang compiler requires a consumer for diagnostics. This one stores them // as strings // so we can provide them to the user in case a module failed to load. class StoringDiagnosticConsumer : public clang::DiagnosticConsumer { public: StoringDiagnosticConsumer(); void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &info) override; void ClearDiagnostics(); void DumpDiagnostics(Stream &error_stream); private: typedef std::pair IDAndDiagnostic; std::vector m_diagnostics; Log *m_log; }; // The private implementation of our ClangModulesDeclVendor. Contains all the // Clang state required // to load modules. class ClangModulesDeclVendorImpl : public ClangModulesDeclVendor { public: ClangModulesDeclVendorImpl( - llvm::IntrusiveRefCntPtr &diagnostics_engine, - llvm::IntrusiveRefCntPtr &compiler_invocation, - std::unique_ptr &&compiler_instance, - std::unique_ptr &&parser); + llvm::IntrusiveRefCntPtr diagnostics_engine, + std::shared_ptr compiler_invocation, + std::unique_ptr compiler_instance, + std::unique_ptr parser); ~ClangModulesDeclVendorImpl() override = default; bool AddModule(ModulePath &path, ModuleVector *exported_modules, Stream &error_stream) override; bool AddModulesForCompileUnit(CompileUnit &cu, ModuleVector &exported_modules, Stream &error_stream) override; uint32_t FindDecls(const ConstString &name, bool append, uint32_t max_matches, std::vector &decls) override; void ForEachMacro(const ModuleVector &modules, std::function handler) override; private: void ReportModuleExportsHelper(std::set &exports, clang::Module *module); void ReportModuleExports(ModuleVector &exports, clang::Module *module); clang::ModuleLoadResult DoGetModule(clang::ModuleIdPath path, bool make_visible); bool m_enabled = false; llvm::IntrusiveRefCntPtr m_diagnostics_engine; - llvm::IntrusiveRefCntPtr m_compiler_invocation; + std::shared_ptr m_compiler_invocation; std::unique_ptr m_compiler_instance; std::unique_ptr m_parser; size_t m_source_location_index = 0; // used to give name components fake SourceLocations typedef std::vector ImportedModule; typedef std::map ImportedModuleMap; typedef std::set ImportedModuleSet; ImportedModuleMap m_imported_modules; ImportedModuleSet m_user_imported_modules; }; } // anonymous namespace StoringDiagnosticConsumer::StoringDiagnosticConsumer() { m_log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS); } void StoringDiagnosticConsumer::HandleDiagnostic( clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &info) { llvm::SmallVector diagnostic_string; info.FormatDiagnostic(diagnostic_string); m_diagnostics.push_back( IDAndDiagnostic(DiagLevel, std::string(diagnostic_string.data(), diagnostic_string.size()))); } void StoringDiagnosticConsumer::ClearDiagnostics() { m_diagnostics.clear(); } void StoringDiagnosticConsumer::DumpDiagnostics(Stream &error_stream) { for (IDAndDiagnostic &diag : m_diagnostics) { switch (diag.first) { default: error_stream.PutCString(diag.second); error_stream.PutChar('\n'); break; case clang::DiagnosticsEngine::Level::Ignored: break; } } } static FileSpec GetResourceDir() { static FileSpec g_cached_resource_dir; static std::once_flag g_once_flag; std::call_once(g_once_flag, []() { HostInfo::GetLLDBPath(lldb::ePathTypeClangDir, g_cached_resource_dir); }); return g_cached_resource_dir; } ClangModulesDeclVendor::ClangModulesDeclVendor() {} ClangModulesDeclVendor::~ClangModulesDeclVendor() {} ClangModulesDeclVendorImpl::ClangModulesDeclVendorImpl( - llvm::IntrusiveRefCntPtr &diagnostics_engine, - llvm::IntrusiveRefCntPtr &compiler_invocation, - std::unique_ptr &&compiler_instance, - std::unique_ptr &&parser) - : ClangModulesDeclVendor(), m_diagnostics_engine(diagnostics_engine), - m_compiler_invocation(compiler_invocation), + llvm::IntrusiveRefCntPtr diagnostics_engine, + std::shared_ptr compiler_invocation, + std::unique_ptr compiler_instance, + std::unique_ptr parser) + : m_diagnostics_engine(std::move(diagnostics_engine)), + m_compiler_invocation(std::move(compiler_invocation)), m_compiler_instance(std::move(compiler_instance)), - m_parser(std::move(parser)), m_imported_modules() {} + m_parser(std::move(parser)) {} void ClangModulesDeclVendorImpl::ReportModuleExportsHelper( std::set &exports, clang::Module *module) { if (exports.count(reinterpret_cast(module))) return; exports.insert(reinterpret_cast(module)); llvm::SmallVector sub_exports; module->getExportedModules(sub_exports); for (clang::Module *module : sub_exports) { ReportModuleExportsHelper(exports, module); } } void ClangModulesDeclVendorImpl::ReportModuleExports( ClangModulesDeclVendor::ModuleVector &exports, clang::Module *module) { std::set exports_set; ReportModuleExportsHelper(exports_set, module); for (ModuleID module : exports_set) { exports.push_back(module); } } bool ClangModulesDeclVendorImpl::AddModule(ModulePath &path, ModuleVector *exported_modules, Stream &error_stream) { // Fail early. if (m_compiler_instance->hadModuleLoaderFatalFailure()) { error_stream.PutCString("error: Couldn't load a module because the module " "loader is in a fatal state.\n"); return false; } // Check if we've already imported this module. std::vector imported_module; for (ConstString path_component : path) { imported_module.push_back(path_component); } { ImportedModuleMap::iterator mi = m_imported_modules.find(imported_module); if (mi != m_imported_modules.end()) { if (exported_modules) { ReportModuleExports(*exported_modules, mi->second); } return true; } } if (!m_compiler_instance->getPreprocessor() .getHeaderSearchInfo() .lookupModule(path[0].GetStringRef())) { error_stream.Printf("error: Header search couldn't locate module %s\n", path[0].AsCString()); return false; } llvm::SmallVector, 4> clang_path; { clang::SourceManager &source_manager = m_compiler_instance->getASTContext().getSourceManager(); for (ConstString path_component : path) { clang_path.push_back(std::make_pair( &m_compiler_instance->getASTContext().Idents.get( path_component.GetStringRef()), source_manager.getLocForStartOfFile(source_manager.getMainFileID()) .getLocWithOffset(m_source_location_index++))); } } StoringDiagnosticConsumer *diagnostic_consumer = static_cast( m_compiler_instance->getDiagnostics().getClient()); diagnostic_consumer->ClearDiagnostics(); clang::Module *top_level_module = DoGetModule(clang_path.front(), false); if (!top_level_module) { diagnostic_consumer->DumpDiagnostics(error_stream); error_stream.Printf("error: Couldn't load top-level module %s\n", path[0].AsCString()); return false; } clang::Module *submodule = top_level_module; for (size_t ci = 1; ci < path.size(); ++ci) { llvm::StringRef component = path[ci].GetStringRef(); submodule = submodule->findSubmodule(component.str()); if (!submodule) { diagnostic_consumer->DumpDiagnostics(error_stream); error_stream.Printf("error: Couldn't load submodule %s\n", component.str().c_str()); return false; } } clang::Module *requested_module = DoGetModule(clang_path, true); if (requested_module != nullptr) { if (exported_modules) { ReportModuleExports(*exported_modules, requested_module); } m_imported_modules[imported_module] = requested_module; m_enabled = true; return true; } return false; } bool ClangModulesDeclVendor::LanguageSupportsClangModules( lldb::LanguageType language) { switch (language) { default: return false; // C++ and friends to be added case lldb::LanguageType::eLanguageTypeC: case lldb::LanguageType::eLanguageTypeC11: case lldb::LanguageType::eLanguageTypeC89: case lldb::LanguageType::eLanguageTypeC99: case lldb::LanguageType::eLanguageTypeObjC: return true; } } bool ClangModulesDeclVendorImpl::AddModulesForCompileUnit( CompileUnit &cu, ClangModulesDeclVendor::ModuleVector &exported_modules, Stream &error_stream) { if (LanguageSupportsClangModules(cu.GetLanguage())) { std::vector imported_modules = cu.GetImportedModules(); for (ConstString imported_module : imported_modules) { std::vector path; path.push_back(imported_module); if (!AddModule(path, &exported_modules, error_stream)) { return false; } } return true; } return true; } // ClangImporter::lookupValue uint32_t ClangModulesDeclVendorImpl::FindDecls(const ConstString &name, bool append, uint32_t max_matches, std::vector &decls) { if (!m_enabled) { return 0; } if (!append) decls.clear(); clang::IdentifierInfo &ident = m_compiler_instance->getASTContext().Idents.get(name.GetStringRef()); clang::LookupResult lookup_result( m_compiler_instance->getSema(), clang::DeclarationName(&ident), clang::SourceLocation(), clang::Sema::LookupOrdinaryName); m_compiler_instance->getSema().LookupName( lookup_result, m_compiler_instance->getSema().getScopeForContext( m_compiler_instance->getASTContext().getTranslationUnitDecl())); uint32_t num_matches = 0; for (clang::NamedDecl *named_decl : lookup_result) { if (num_matches >= max_matches) return num_matches; decls.push_back(named_decl); ++num_matches; } return num_matches; } void ClangModulesDeclVendorImpl::ForEachMacro( const ClangModulesDeclVendor::ModuleVector &modules, std::function handler) { if (!m_enabled) { return; } typedef std::map ModulePriorityMap; ModulePriorityMap module_priorities; ssize_t priority = 0; for (ModuleID module : modules) { module_priorities[module] = priority++; } if (m_compiler_instance->getPreprocessor().getExternalSource()) { m_compiler_instance->getPreprocessor() .getExternalSource() ->ReadDefinedMacros(); } for (clang::Preprocessor::macro_iterator mi = m_compiler_instance->getPreprocessor().macro_begin(), me = m_compiler_instance->getPreprocessor().macro_end(); mi != me; ++mi) { const clang::IdentifierInfo *ii = nullptr; { if (clang::IdentifierInfoLookup *lookup = m_compiler_instance->getPreprocessor() .getIdentifierTable() .getExternalIdentifierLookup()) { lookup->get(mi->first->getName()); } if (!ii) { ii = mi->first; } } ssize_t found_priority = -1; clang::MacroInfo *macro_info = nullptr; for (clang::ModuleMacro *module_macro : m_compiler_instance->getPreprocessor().getLeafModuleMacros(ii)) { clang::Module *module = module_macro->getOwningModule(); { ModulePriorityMap::iterator pi = module_priorities.find(reinterpret_cast(module)); if (pi != module_priorities.end() && pi->second > found_priority) { macro_info = module_macro->getMacroInfo(); found_priority = pi->second; } } clang::Module *top_level_module = module->getTopLevelModule(); if (top_level_module != module) { ModulePriorityMap::iterator pi = module_priorities.find( reinterpret_cast(top_level_module)); if ((pi != module_priorities.end()) && pi->second > found_priority) { macro_info = module_macro->getMacroInfo(); found_priority = pi->second; } } } if (macro_info) { std::string macro_expansion = "#define "; macro_expansion.append(mi->first->getName().str()); { if (macro_info->isFunctionLike()) { macro_expansion.append("("); bool first_arg = true; for (clang::MacroInfo::arg_iterator ai = macro_info->arg_begin(), ae = macro_info->arg_end(); ai != ae; ++ai) { if (!first_arg) { macro_expansion.append(", "); } else { first_arg = false; } macro_expansion.append((*ai)->getName().str()); } if (macro_info->isC99Varargs()) { if (first_arg) { macro_expansion.append("..."); } else { macro_expansion.append(", ..."); } } else if (macro_info->isGNUVarargs()) { macro_expansion.append("..."); } macro_expansion.append(")"); } macro_expansion.append(" "); bool first_token = true; for (clang::MacroInfo::tokens_iterator ti = macro_info->tokens_begin(), te = macro_info->tokens_end(); ti != te; ++ti) { if (!first_token) { macro_expansion.append(" "); } else { first_token = false; } if (ti->isLiteral()) { if (const char *literal_data = ti->getLiteralData()) { std::string token_str(literal_data, ti->getLength()); macro_expansion.append(token_str); } else { bool invalid = false; const char *literal_source = m_compiler_instance->getSourceManager().getCharacterData( ti->getLocation(), &invalid); if (invalid) { - lldbassert(!"Unhandled token kind"); + lldbassert(0 && "Unhandled token kind"); macro_expansion.append(""); } else { macro_expansion.append( std::string(literal_source, ti->getLength())); } } } else if (const char *punctuator_spelling = clang::tok::getPunctuatorSpelling(ti->getKind())) { macro_expansion.append(punctuator_spelling); } else if (const char *keyword_spelling = clang::tok::getKeywordSpelling(ti->getKind())) { macro_expansion.append(keyword_spelling); } else { switch (ti->getKind()) { case clang::tok::TokenKind::identifier: macro_expansion.append(ti->getIdentifierInfo()->getName().str()); break; case clang::tok::TokenKind::raw_identifier: macro_expansion.append(ti->getRawIdentifier().str()); break; default: macro_expansion.append(ti->getName()); break; } } } if (handler(macro_expansion)) { return; } } } } } clang::ModuleLoadResult ClangModulesDeclVendorImpl::DoGetModule(clang::ModuleIdPath path, bool make_visible) { clang::Module::NameVisibilityKind visibility = make_visible ? clang::Module::AllVisible : clang::Module::Hidden; const bool is_inclusion_directive = false; return m_compiler_instance->loadModule(path.front().second, path, visibility, is_inclusion_directive); } static const char *ModuleImportBufferName = "LLDBModulesMemoryBuffer"; lldb_private::ClangModulesDeclVendor * ClangModulesDeclVendor::Create(Target &target) { // FIXME we should insure programmatically that the expression parser's // compiler and the modules runtime's // compiler are both initialized in the same way – preferably by the same // code. if (!target.GetPlatform()->SupportsModules()) return nullptr; const ArchSpec &arch = target.GetArchitecture(); std::vector compiler_invocation_arguments = { "clang", "-fmodules", "-fimplicit-module-maps", "-fcxx-modules", "-fsyntax-only", "-femit-all-decls", "-target", arch.GetTriple().str(), "-fmodules-validate-system-headers", "-Werror=non-modular-include-in-framework-module"}; target.GetPlatform()->AddClangModuleCompilationOptions( &target, compiler_invocation_arguments); compiler_invocation_arguments.push_back(ModuleImportBufferName); // Add additional search paths with { "-I", path } or { "-F", path } here. { llvm::SmallString<128> DefaultModuleCache; const bool erased_on_reboot = false; llvm::sys::path::system_temp_directory(erased_on_reboot, DefaultModuleCache); llvm::sys::path::append(DefaultModuleCache, "org.llvm.clang"); llvm::sys::path::append(DefaultModuleCache, "ModuleCache"); std::string module_cache_argument("-fmodules-cache-path="); module_cache_argument.append(DefaultModuleCache.str().str()); compiler_invocation_arguments.push_back(module_cache_argument); } FileSpecList &module_search_paths = target.GetClangModuleSearchPaths(); for (size_t spi = 0, spe = module_search_paths.GetSize(); spi < spe; ++spi) { const FileSpec &search_path = module_search_paths.GetFileSpecAtIndex(spi); std::string search_path_argument = "-I"; search_path_argument.append(search_path.GetPath()); compiler_invocation_arguments.push_back(search_path_argument); } { FileSpec clang_resource_dir = GetResourceDir(); if (clang_resource_dir.IsDirectory()) { compiler_invocation_arguments.push_back("-resource-dir"); compiler_invocation_arguments.push_back(clang_resource_dir.GetPath()); } } llvm::IntrusiveRefCntPtr diagnostics_engine = clang::CompilerInstance::createDiagnostics(new clang::DiagnosticOptions, new StoringDiagnosticConsumer); std::vector compiler_invocation_argument_cstrs; for (const std::string &arg : compiler_invocation_arguments) { compiler_invocation_argument_cstrs.push_back(arg.c_str()); } - llvm::IntrusiveRefCntPtr invocation( + std::shared_ptr invocation = clang::createInvocationFromCommandLine(compiler_invocation_argument_cstrs, - diagnostics_engine)); + diagnostics_engine); if (!invocation) return nullptr; std::unique_ptr source_buffer = llvm::MemoryBuffer::getMemBuffer( "extern int __lldb __attribute__((unavailable));", ModuleImportBufferName); invocation->getPreprocessorOpts().addRemappedFile(ModuleImportBufferName, source_buffer.release()); std::unique_ptr instance( new clang::CompilerInstance); instance->setDiagnostics(diagnostics_engine.get()); - instance->setInvocation(invocation.get()); + instance->setInvocation(invocation); std::unique_ptr action(new clang::SyntaxOnlyAction); instance->setTarget(clang::TargetInfo::CreateTargetInfo( *diagnostics_engine, instance->getInvocation().TargetOpts)); if (!instance->hasTarget()) return nullptr; instance->getTarget().adjust(instance->getLangOpts()); if (!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) return nullptr; instance->getPreprocessor().enableIncrementalProcessing(); instance->createModuleManager(); instance->createSema(action->getTranslationUnitKind(), nullptr); const bool skipFunctionBodies = false; std::unique_ptr parser(new clang::Parser( instance->getPreprocessor(), instance->getSema(), skipFunctionBodies)); instance->getPreprocessor().EnterMainSourceFile(); parser->Initialize(); clang::Parser::DeclGroupPtrTy parsed; while (!parser->ParseTopLevelDecl(parsed)) ; - return new ClangModulesDeclVendorImpl(diagnostics_engine, invocation, + return new ClangModulesDeclVendorImpl(std::move(diagnostics_engine), + std::move(invocation), std::move(instance), std::move(parser)); } Index: vendor/lldb/dist/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptx86ABIFixups.cpp (revision 311542) @@ -1,297 +1,297 @@ //===-- RenderScriptx86ABIFixups.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 #include "llvm/ADT/StringRef.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Pass.h" // Project includes #include "lldb/Core/Log.h" #include "lldb/Target/Process.h" using namespace lldb_private; namespace { bool isRSAPICall(llvm::Module &module, llvm::CallInst *call_inst) { // TODO get the list of renderscript modules from lldb and check if // this llvm::Module calls into any of them. (void)module; const auto func_name = call_inst->getCalledFunction()->getName(); if (func_name.startswith("llvm") || func_name.startswith("lldb")) return false; if (call_inst->getCalledFunction()->isIntrinsic()) return false; return true; } bool isRSLargeReturnCall(llvm::Module &module, llvm::CallInst *call_inst) { // i686 and x86_64 returns for large vectors in the RenderScript API are not // handled as normal // register pairs, but as a hidden sret type. This is not reflected in the // debug info or mangled // symbol name, and the android ABI for x86 and x86_64, (as well as the // emulators) specifies there is // no AVX, so bcc generates an sret function because we cannot natively return // 256 bit vectors. // This function simply checks whether a function has a > 128bit return type. // It is perhaps an // unreliable heuristic, and relies on bcc not generating AVX code, so if the // android ABI one day // provides for AVX, this function may go out of fashion. (void)module; if (!call_inst || !call_inst->getCalledFunction()) return false; return call_inst->getCalledFunction() ->getReturnType() ->getPrimitiveSizeInBits() > 128; } bool isRSAllocationPtrTy(const llvm::Type *type) { if (!type->isPointerTy()) return false; auto ptr_type = type->getPointerElementType(); return ptr_type->isStructTy() && ptr_type->getStructName().startswith("struct.rs_allocation"); } bool isRSAllocationTyCallSite(llvm::Module &module, llvm::CallInst *call_inst) { (void)module; if (!call_inst->hasByValArgument()) return false; for (const auto ¶m : call_inst->operand_values()) if (isRSAllocationPtrTy(param->getType())) return true; return false; } llvm::FunctionType *cloneToStructRetFnTy(llvm::CallInst *call_inst) { // on x86 StructReturn functions return a pointer to the return value, rather // than the return // value itself [ref](http://www.agner.org/optimize/calling_conventions.pdf // section 6). // We create a return type by getting the pointer type of the old return type, // and inserting a new // initial argument of pointer type of the original return type. Log *log( GetLogIfAnyCategoriesSet(LIBLLDB_LOG_LANGUAGE | LIBLLDB_LOG_EXPRESSIONS)); assert(call_inst && "no CallInst"); llvm::Function *orig = call_inst->getCalledFunction(); assert(orig && "CallInst has no called function"); llvm::FunctionType *orig_type = orig->getFunctionType(); auto name = orig->getName(); if (log) log->Printf("%s - cloning to StructRet function for '%s'", __FUNCTION__, name.str().c_str()); unsigned num_params = orig_type->getNumParams(); std::vector new_params{num_params + 1, nullptr}; std::vector params{orig_type->param_begin(), orig_type->param_end()}; // This may not work if the function is somehow declared void as llvm is // strongly typed // and represents void* with i8* assert(!orig_type->getReturnType()->isVoidTy() && "Cannot add StructRet attribute to void function"); llvm::PointerType *return_type_ptr_type = llvm::PointerType::getUnqual(orig->getReturnType()); assert(return_type_ptr_type && "failed to get function return type PointerType"); if (!return_type_ptr_type) return nullptr; if (log) log->Printf("%s - return type pointer type for StructRet clone @ '0x%p':\n", __FUNCTION__, (void *)return_type_ptr_type); // put the the sret pointer argument in place at the beginning of the argument // list. params.emplace(params.begin(), return_type_ptr_type); assert(params.size() == num_params + 1); return llvm::FunctionType::get(return_type_ptr_type, params, orig->isVarArg()); } bool findRSCallSites(llvm::Module &module, std::set &rs_callsites, bool (*predicate)(llvm::Module &, llvm::CallInst *)) { bool found = false; for (auto &func : module.getFunctionList()) for (auto &block : func.getBasicBlockList()) for (auto &inst : block) { llvm::CallInst *call_inst = llvm::dyn_cast_or_null(&inst); if (!call_inst || !call_inst->getCalledFunction()) // This is not the call-site you are looking for... continue; if (isRSAPICall(module, call_inst) && predicate(module, call_inst)) { rs_callsites.insert(call_inst); found = true; } } return found; } bool fixupX86StructRetCalls(llvm::Module &module) { bool changed = false; // changing a basic block while iterating over it seems to have some undefined // behaviour // going on so we find all RS callsites first, then fix them up after // consuming // the iterator. std::set rs_callsites; if (!findRSCallSites(module, rs_callsites, isRSLargeReturnCall)) return false; for (auto call_inst : rs_callsites) { llvm::FunctionType *new_func_type = cloneToStructRetFnTy(call_inst); assert(new_func_type && "failed to clone functionType for Renderscript ABI fixup"); llvm::CallSite call_site(call_inst); llvm::Function *func = call_inst->getCalledFunction(); assert(func && "cannot resolve function in RenderScriptRuntime"); // Copy the original call arguments std::vector new_call_args(call_site.arg_begin(), call_site.arg_end()); // Allocate enough space to store the return value of the original function // we pass a pointer to this allocation as the StructRet param, and then // copy its // value into the lldb return value llvm::AllocaInst *return_value_alloc = new llvm::AllocaInst( func->getReturnType(), "var_vector_return_alloc", call_inst); // use the new allocation as the new first argument new_call_args.emplace(new_call_args.begin(), llvm::cast(return_value_alloc)); llvm::PointerType *new_func_ptr_type = llvm::PointerType::get(new_func_type, 0); // Create the type cast from the old function type to the new one llvm::Constant *new_func_cast = llvm::ConstantExpr::getCast( llvm::Instruction::BitCast, func, new_func_ptr_type); // create an allocation for a new function pointer llvm::AllocaInst *new_func_ptr = new llvm::AllocaInst(new_func_ptr_type, "new_func_ptr", call_inst); // store the new_func_cast to the newly allocated space - (void)new llvm::StoreInst(new_func_cast, new_func_ptr, - "new_func_ptr_load_cast", call_inst); + (new llvm::StoreInst(new_func_cast, new_func_ptr, call_inst)) + ->setName("new_func_ptr_load_cast"); // load the new function address ready for a jump llvm::LoadInst *new_func_addr_load = new llvm::LoadInst(new_func_ptr, "load_func_pointer", call_inst); // and create a callinstruction from it llvm::CallInst *new_call_inst = llvm::CallInst::Create( new_func_addr_load, new_call_args, "new_func_call", call_inst); new_call_inst->setCallingConv(call_inst->getCallingConv()); new_call_inst->setTailCall(call_inst->isTailCall()); llvm::LoadInst *lldb_save_result_address = new llvm::LoadInst(return_value_alloc, "save_return_val", call_inst); // Now remove the old broken call call_inst->replaceAllUsesWith(lldb_save_result_address); call_inst->eraseFromParent(); changed = true; } return changed; } bool fixupRSAllocationStructByValCalls(llvm::Module &module) { // On x86_64, calls to functions in the RS runtime that take an // `rs_allocation` type argument // are actually handled as by-ref params by bcc, but appear to be passed by // value by lldb (the callsite all use // `struct byval`). // On x86_64 Linux, struct arguments are transferred in registers if the // struct size is no bigger than // 128bits [ref](http://www.agner.org/optimize/calling_conventions.pdf) // section 7.1 "Passing and returning objects" // otherwise passed on the stack. // an object of type `rs_allocation` is actually 256bits, so should be passed // on the stack. However, code generated // by bcc actually treats formal params of type `rs_allocation` as // `rs_allocation *` so we need to convert the // calling convention to pass by reference, and remove any hint of byval from // formal parameters. bool changed = false; std::set rs_callsites; if (!findRSCallSites(module, rs_callsites, isRSAllocationTyCallSite)) return false; std::set rs_functions; // for all call instructions for (auto call_inst : rs_callsites) { // add the called function to a set so that we can strip its byval // attributes in another pass rs_functions.insert(call_inst->getCalledFunction()); // get the function attributes llvm::AttributeSet call_attribs = call_inst->getAttributes(); // iterate over the argument attributes for (size_t i = 1; i <= call_attribs.getNumSlots(); ++i) { // if this argument is passed by val if (call_attribs.hasAttribute(i, llvm::Attribute::ByVal)) { // strip away the byval attribute call_inst->removeAttribute(i, llvm::Attribute::ByVal); changed = true; } } } llvm::AttributeSet attr_byval = llvm::AttributeSet::get(module.getContext(), 1u, llvm::Attribute::ByVal); // for all called function decls for (auto func : rs_functions) { // inspect all of the arguments in the call llvm::SymbolTableList &arg_list = func->getArgumentList(); for (auto &arg : arg_list) { if (arg.hasByValAttr()) { arg.removeAttr(attr_byval); changed = true; } } } return changed; } } // end anonymous namespace namespace lldb_private { namespace lldb_renderscript { bool fixupX86FunctionCalls(llvm::Module &module) { return fixupX86StructRetCalls(module); } bool fixupX86_64FunctionCalls(llvm::Module &module) { bool changed = false; changed |= fixupX86StructRetCalls(module); changed |= fixupRSAllocationStructByValCalls(module); return changed; } } // end namespace lldb_renderscript } // end namespace lldb_private Index: vendor/lldb/dist/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp (revision 311542) @@ -1,1043 +1,1042 @@ //===-- ProcessKDP.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 #include // C++ Includes #include // Other libraries and framework includes #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/UUID.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Host.h" #include "lldb/Host/Symbols.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/common/TCPSocket.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionGroupString.h" #include "lldb/Interpreter/OptionGroupUInt64.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/StringExtractor.h" #define USEC_PER_SEC 1000000 // Project includes #include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h" #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" #include "ProcessKDP.h" #include "ProcessKDPLog.h" #include "ThreadKDP.h" using namespace lldb; using namespace lldb_private; namespace { static PropertyDefinition g_properties[] = { {"packet-timeout", OptionValue::eTypeUInt64, true, 5, NULL, NULL, "Specify the default packet timeout in seconds."}, {NULL, OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL}}; enum { ePropertyPacketTimeout }; class PluginProperties : public Properties { public: static ConstString GetSettingName() { return ProcessKDP::GetPluginNameStatic(); } PluginProperties() : Properties() { m_collection_sp.reset(new OptionValueProperties(GetSettingName())); m_collection_sp->Initialize(g_properties); } virtual ~PluginProperties() {} uint64_t GetPacketTimeout() { const uint32_t idx = ePropertyPacketTimeout; return m_collection_sp->GetPropertyAtIndexAsUInt64( NULL, idx, g_properties[idx].default_uint_value); } }; typedef std::shared_ptr ProcessKDPPropertiesSP; static const ProcessKDPPropertiesSP &GetGlobalPluginProperties() { static ProcessKDPPropertiesSP g_settings_sp; if (!g_settings_sp) g_settings_sp.reset(new PluginProperties()); return g_settings_sp; } } // anonymous namespace end static const lldb::tid_t g_kernel_tid = 1; ConstString ProcessKDP::GetPluginNameStatic() { static ConstString g_name("kdp-remote"); return g_name; } const char *ProcessKDP::GetPluginDescriptionStatic() { return "KDP Remote protocol based debugging plug-in for darwin kernel " "debugging."; } void ProcessKDP::Terminate() { PluginManager::UnregisterPlugin(ProcessKDP::CreateInstance); } lldb::ProcessSP ProcessKDP::CreateInstance(TargetSP target_sp, ListenerSP listener_sp, const FileSpec *crash_file_path) { lldb::ProcessSP process_sp; if (crash_file_path == NULL) process_sp.reset(new ProcessKDP(target_sp, listener_sp)); return process_sp; } bool ProcessKDP::CanDebug(TargetSP target_sp, bool plugin_specified_by_name) { if (plugin_specified_by_name) return true; // For now we are just making sure the file exists for a given module Module *exe_module = target_sp->GetExecutableModulePointer(); if (exe_module) { const llvm::Triple &triple_ref = target_sp->GetArchitecture().GetTriple(); switch (triple_ref.getOS()) { case llvm::Triple::Darwin: // Should use "macosx" for desktop and "ios" for // iOS, but accept darwin just in case case llvm::Triple::MacOSX: // For desktop targets case llvm::Triple::IOS: // For arm targets case llvm::Triple::TvOS: case llvm::Triple::WatchOS: if (triple_ref.getVendor() == llvm::Triple::Apple) { ObjectFile *exe_objfile = exe_module->GetObjectFile(); if (exe_objfile->GetType() == ObjectFile::eTypeExecutable && exe_objfile->GetStrata() == ObjectFile::eStrataKernel) return true; } break; default: break; } } return false; } //---------------------------------------------------------------------- // ProcessKDP constructor //---------------------------------------------------------------------- ProcessKDP::ProcessKDP(TargetSP target_sp, ListenerSP listener_sp) : Process(target_sp, listener_sp), m_comm("lldb.process.kdp-remote.communication"), m_async_broadcaster(NULL, "lldb.process.kdp-remote.async-broadcaster"), m_dyld_plugin_name(), m_kernel_load_addr(LLDB_INVALID_ADDRESS), m_command_sp(), m_kernel_thread_wp() { m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue, "async thread continue"); const uint64_t timeout_seconds = GetGlobalPluginProperties()->GetPacketTimeout(); if (timeout_seconds > 0) m_comm.SetPacketTimeout(std::chrono::seconds(timeout_seconds)); } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- ProcessKDP::~ProcessKDP() { Clear(); // We need to call finalize on the process before destroying ourselves // to make sure all of the broadcaster cleanup goes as planned. If we // destruct this class, then Process::~Process() might have problems // trying to fully destroy the broadcaster. Finalize(); } //---------------------------------------------------------------------- // PluginInterface //---------------------------------------------------------------------- lldb_private::ConstString ProcessKDP::GetPluginName() { return GetPluginNameStatic(); } uint32_t ProcessKDP::GetPluginVersion() { return 1; } Error ProcessKDP::WillLaunch(Module *module) { Error error; error.SetErrorString("launching not supported in kdp-remote plug-in"); return error; } Error ProcessKDP::WillAttachToProcessWithID(lldb::pid_t pid) { Error error; error.SetErrorString( "attaching to a by process ID not supported in kdp-remote plug-in"); return error; } Error ProcessKDP::WillAttachToProcessWithName(const char *process_name, bool wait_for_launch) { Error error; error.SetErrorString( "attaching to a by process name not supported in kdp-remote plug-in"); return error; } bool ProcessKDP::GetHostArchitecture(ArchSpec &arch) { uint32_t cpu = m_comm.GetCPUType(); if (cpu) { uint32_t sub = m_comm.GetCPUSubtype(); arch.SetArchitecture(eArchTypeMachO, cpu, sub); // Leave architecture vendor as unspecified unknown arch.GetTriple().setVendor(llvm::Triple::UnknownVendor); arch.GetTriple().setVendorName(llvm::StringRef()); return true; } arch.Clear(); return false; } Error ProcessKDP::DoConnectRemote(Stream *strm, llvm::StringRef remote_url) { Error error; // Don't let any JIT happen when doing KDP as we can't allocate // memory and we don't want to be mucking with threads that might // already be handling exceptions SetCanJIT(false); if (remote_url.empty()) { error.SetErrorStringWithFormat("empty connection URL"); return error; } std::unique_ptr conn_ap( new ConnectionFileDescriptor()); if (conn_ap.get()) { // Only try once for now. // TODO: check if we should be retrying? const uint32_t max_retry_count = 1; for (uint32_t retry_count = 0; retry_count < max_retry_count; ++retry_count) { if (conn_ap->Connect(remote_url, &error) == eConnectionStatusSuccess) break; usleep(100000); } } if (conn_ap->IsConnected()) { const TCPSocket &socket = static_cast(*conn_ap->GetReadObject()); const uint16_t reply_port = socket.GetLocalPortNumber(); if (reply_port != 0) { m_comm.SetConnection(conn_ap.release()); if (m_comm.SendRequestReattach(reply_port)) { if (m_comm.SendRequestConnect(reply_port, reply_port, "Greetings from LLDB...")) { m_comm.GetVersion(); Target &target = GetTarget(); ArchSpec kernel_arch; // The host architecture GetHostArchitecture(kernel_arch); ArchSpec target_arch = target.GetArchitecture(); // Merge in any unspecified stuff into the target architecture in // case the target arch isn't set at all or incompletely. target_arch.MergeFrom(kernel_arch); target.SetArchitecture(target_arch); /* Get the kernel's UUID and load address via KDP_KERNELVERSION * packet. */ /* An EFI kdp session has neither UUID nor load address. */ UUID kernel_uuid = m_comm.GetUUID(); addr_t kernel_load_addr = m_comm.GetLoadAddress(); if (m_comm.RemoteIsEFI()) { // Select an invalid plugin name for the dynamic loader so one // doesn't get used // since EFI does its own manual loading via python scripting static ConstString g_none_dynamic_loader("none"); m_dyld_plugin_name = g_none_dynamic_loader; if (kernel_uuid.IsValid()) { // If EFI passed in a UUID= try to lookup UUID // The slide will not be provided. But the UUID // lookup will be used to launch EFI debug scripts // from the dSYM, that can load all of the symbols. ModuleSpec module_spec; module_spec.GetUUID() = kernel_uuid; module_spec.GetArchitecture() = target.GetArchitecture(); // Lookup UUID locally, before attempting dsymForUUID like action module_spec.GetSymbolFileSpec() = Symbols::LocateExecutableSymbolFile(module_spec); if (module_spec.GetSymbolFileSpec()) { ModuleSpec executable_module_spec = Symbols::LocateExecutableObjectFile(module_spec); if (executable_module_spec.GetFileSpec().Exists()) { module_spec.GetFileSpec() = executable_module_spec.GetFileSpec(); } } if (!module_spec.GetSymbolFileSpec() || !module_spec.GetSymbolFileSpec()) Symbols::DownloadObjectAndSymbolFile(module_spec, true); if (module_spec.GetFileSpec().Exists()) { ModuleSP module_sp(new Module(module_spec)); if (module_sp.get() && module_sp->GetObjectFile()) { // Get the current target executable ModuleSP exe_module_sp(target.GetExecutableModule()); // Make sure you don't already have the right module loaded // and they will be uniqued if (exe_module_sp.get() != module_sp.get()) target.SetExecutableModule(module_sp, false); } } } } else if (m_comm.RemoteIsDarwinKernel()) { m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); if (kernel_load_addr != LLDB_INVALID_ADDRESS) { m_kernel_load_addr = kernel_load_addr; } } // Set the thread ID UpdateThreadListIfNeeded(); SetID(1); GetThreadList(); SetPrivateState(eStateStopped); StreamSP async_strm_sp(target.GetDebugger().GetAsyncOutputStream()); if (async_strm_sp) { const char *cstr; if ((cstr = m_comm.GetKernelVersion()) != NULL) { async_strm_sp->Printf("Version: %s\n", cstr); async_strm_sp->Flush(); } // if ((cstr = m_comm.GetImagePath ()) != NULL) // { // async_strm_sp->Printf ("Image Path: // %s\n", cstr); // async_strm_sp->Flush(); // } } } else { error.SetErrorString("KDP_REATTACH failed"); } } else { error.SetErrorString("KDP_REATTACH failed"); } } else { error.SetErrorString("invalid reply port from UDP connection"); } } else { if (error.Success()) error.SetErrorStringWithFormat("failed to connect to '%s'", remote_url.str().c_str()); } if (error.Fail()) m_comm.Disconnect(); return error; } //---------------------------------------------------------------------- // Process Control //---------------------------------------------------------------------- Error ProcessKDP::DoLaunch(Module *exe_module, ProcessLaunchInfo &launch_info) { Error error; error.SetErrorString("launching not supported in kdp-remote plug-in"); return error; } Error ProcessKDP::DoAttachToProcessWithID( lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) { Error error; error.SetErrorString( "attach to process by ID is not suppported in kdp remote debugging"); return error; } Error ProcessKDP::DoAttachToProcessWithName( const char *process_name, const ProcessAttachInfo &attach_info) { Error error; error.SetErrorString( "attach to process by name is not suppported in kdp remote debugging"); return error; } void ProcessKDP::DidAttach(ArchSpec &process_arch) { Process::DidAttach(process_arch); Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); if (log) log->Printf("ProcessKDP::DidAttach()"); if (GetID() != LLDB_INVALID_PROCESS_ID) { GetHostArchitecture(process_arch); } } addr_t ProcessKDP::GetImageInfoAddress() { return m_kernel_load_addr; } lldb_private::DynamicLoader *ProcessKDP::GetDynamicLoader() { if (m_dyld_ap.get() == NULL) m_dyld_ap.reset(DynamicLoader::FindPlugin( this, m_dyld_plugin_name.IsEmpty() ? NULL : m_dyld_plugin_name.GetCString())); return m_dyld_ap.get(); } Error ProcessKDP::WillResume() { return Error(); } Error ProcessKDP::DoResume() { Error error; Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); // Only start the async thread if we try to do any process control if (!m_async_thread.IsJoinable()) StartAsyncThread(); bool resume = false; // With KDP there is only one thread we can tell what to do ThreadSP kernel_thread_sp(m_thread_list.FindThreadByProtocolID(g_kernel_tid)); if (kernel_thread_sp) { const StateType thread_resume_state = kernel_thread_sp->GetTemporaryResumeState(); if (log) log->Printf("ProcessKDP::DoResume() thread_resume_state = %s", StateAsCString(thread_resume_state)); switch (thread_resume_state) { case eStateSuspended: // Nothing to do here when a thread will stay suspended // we just leave the CPU mask bit set to zero for the thread if (log) log->Printf("ProcessKDP::DoResume() = suspended???"); break; case eStateStepping: { lldb::RegisterContextSP reg_ctx_sp( kernel_thread_sp->GetRegisterContext()); if (reg_ctx_sp) { if (log) log->Printf( "ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep (true);"); reg_ctx_sp->HardwareSingleStep(true); resume = true; } else { error.SetErrorStringWithFormat( "KDP thread 0x%llx has no register context", kernel_thread_sp->GetID()); } } break; case eStateRunning: { lldb::RegisterContextSP reg_ctx_sp( kernel_thread_sp->GetRegisterContext()); if (reg_ctx_sp) { if (log) log->Printf("ProcessKDP::DoResume () reg_ctx_sp->HardwareSingleStep " "(false);"); reg_ctx_sp->HardwareSingleStep(false); resume = true; } else { error.SetErrorStringWithFormat( "KDP thread 0x%llx has no register context", kernel_thread_sp->GetID()); } } break; default: // The only valid thread resume states are listed above - assert(!"invalid thread resume state"); - break; + llvm_unreachable("invalid thread resume state"); } } if (resume) { if (log) log->Printf("ProcessKDP::DoResume () sending resume"); if (m_comm.SendRequestResume()) { m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue); SetPrivateState(eStateRunning); } else error.SetErrorString("KDP resume failed"); } else { error.SetErrorString("kernel thread is suspended"); } return error; } lldb::ThreadSP ProcessKDP::GetKernelThread() { // KDP only tells us about one thread/core. Any other threads will usually // be the ones that are read from memory by the OS plug-ins. ThreadSP thread_sp(m_kernel_thread_wp.lock()); if (!thread_sp) { thread_sp.reset(new ThreadKDP(*this, g_kernel_tid)); m_kernel_thread_wp = thread_sp; } return thread_sp; } bool ProcessKDP::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) { // locker will keep a mutex locked until it goes out of scope Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_THREAD)); if (log && log->GetMask().Test(KDP_LOG_VERBOSE)) log->Printf("ProcessKDP::%s (pid = %" PRIu64 ")", __FUNCTION__, GetID()); // Even though there is a CPU mask, it doesn't mean we can see each CPU // individually, there is really only one. Lets call this thread 1. ThreadSP thread_sp( old_thread_list.FindThreadByProtocolID(g_kernel_tid, false)); if (!thread_sp) thread_sp = GetKernelThread(); new_thread_list.AddThread(thread_sp); return new_thread_list.GetSize(false) > 0; } void ProcessKDP::RefreshStateAfterStop() { // Let all threads recover from stopping and do any clean up based // on the previous thread state (if any). m_thread_list.RefreshStateAfterStop(); } Error ProcessKDP::DoHalt(bool &caused_stop) { Error error; if (m_comm.IsRunning()) { if (m_destroy_in_process) { // If we are attemping to destroy, we need to not return an error to // Halt or DoDestroy won't get called. // We are also currently running, so send a process stopped event SetPrivateState(eStateStopped); } else { error.SetErrorString("KDP cannot interrupt a running kernel"); } } return error; } Error ProcessKDP::DoDetach(bool keep_stopped) { Error error; Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); if (log) log->Printf("ProcessKDP::DoDetach(keep_stopped = %i)", keep_stopped); if (m_comm.IsRunning()) { // We are running and we can't interrupt a running kernel, so we need // to just close the connection to the kernel and hope for the best } else { // If we are going to keep the target stopped, then don't send the // disconnect message. if (!keep_stopped && m_comm.IsConnected()) { const bool success = m_comm.SendRequestDisconnect(); if (log) { if (success) log->PutCString( "ProcessKDP::DoDetach() detach packet sent successfully"); else log->PutCString( "ProcessKDP::DoDetach() connection channel shutdown failed"); } m_comm.Disconnect(); } } StopAsyncThread(); m_comm.Clear(); SetPrivateState(eStateDetached); ResumePrivateStateThread(); // KillDebugserverProcess (); return error; } Error ProcessKDP::DoDestroy() { // For KDP there really is no difference between destroy and detach bool keep_stopped = false; return DoDetach(keep_stopped); } //------------------------------------------------------------------ // Process Queries //------------------------------------------------------------------ bool ProcessKDP::IsAlive() { return m_comm.IsConnected() && Process::IsAlive(); } //------------------------------------------------------------------ // Process Memory //------------------------------------------------------------------ size_t ProcessKDP::DoReadMemory(addr_t addr, void *buf, size_t size, Error &error) { uint8_t *data_buffer = (uint8_t *)buf; if (m_comm.IsConnected()) { const size_t max_read_size = 512; size_t total_bytes_read = 0; // Read the requested amount of memory in 512 byte chunks while (total_bytes_read < size) { size_t bytes_to_read_this_request = size - total_bytes_read; if (bytes_to_read_this_request > max_read_size) { bytes_to_read_this_request = max_read_size; } size_t bytes_read = m_comm.SendRequestReadMemory( addr + total_bytes_read, data_buffer + total_bytes_read, bytes_to_read_this_request, error); total_bytes_read += bytes_read; if (error.Fail() || bytes_read == 0) { return total_bytes_read; } } return total_bytes_read; } error.SetErrorString("not connected"); return 0; } size_t ProcessKDP::DoWriteMemory(addr_t addr, const void *buf, size_t size, Error &error) { if (m_comm.IsConnected()) return m_comm.SendRequestWriteMemory(addr, buf, size, error); error.SetErrorString("not connected"); return 0; } lldb::addr_t ProcessKDP::DoAllocateMemory(size_t size, uint32_t permissions, Error &error) { error.SetErrorString( "memory allocation not suppported in kdp remote debugging"); return LLDB_INVALID_ADDRESS; } Error ProcessKDP::DoDeallocateMemory(lldb::addr_t addr) { Error error; error.SetErrorString( "memory deallocation not suppported in kdp remote debugging"); return error; } Error ProcessKDP::EnableBreakpointSite(BreakpointSite *bp_site) { if (m_comm.LocalBreakpointsAreSupported()) { Error error; if (!bp_site->IsEnabled()) { if (m_comm.SendRequestBreakpoint(true, bp_site->GetLoadAddress())) { bp_site->SetEnabled(true); bp_site->SetType(BreakpointSite::eExternal); } else { error.SetErrorString("KDP set breakpoint failed"); } } return error; } return EnableSoftwareBreakpoint(bp_site); } Error ProcessKDP::DisableBreakpointSite(BreakpointSite *bp_site) { if (m_comm.LocalBreakpointsAreSupported()) { Error error; if (bp_site->IsEnabled()) { BreakpointSite::Type bp_type = bp_site->GetType(); if (bp_type == BreakpointSite::eExternal) { if (m_destroy_in_process && m_comm.IsRunning()) { // We are trying to destroy our connection and we are running bp_site->SetEnabled(false); } else { if (m_comm.SendRequestBreakpoint(false, bp_site->GetLoadAddress())) bp_site->SetEnabled(false); else error.SetErrorString("KDP remove breakpoint failed"); } } else { error = DisableSoftwareBreakpoint(bp_site); } } return error; } return DisableSoftwareBreakpoint(bp_site); } Error ProcessKDP::EnableWatchpoint(Watchpoint *wp, bool notify) { Error error; error.SetErrorString( "watchpoints are not suppported in kdp remote debugging"); return error; } Error ProcessKDP::DisableWatchpoint(Watchpoint *wp, bool notify) { Error error; error.SetErrorString( "watchpoints are not suppported in kdp remote debugging"); return error; } void ProcessKDP::Clear() { m_thread_list.Clear(); } Error ProcessKDP::DoSignal(int signo) { Error error; error.SetErrorString( "sending signals is not suppported in kdp remote debugging"); return error; } void ProcessKDP::Initialize() { static std::once_flag g_once_flag; std::call_once(g_once_flag, []() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, DebuggerInitialize); Log::Callbacks log_callbacks = {ProcessKDPLog::DisableLog, ProcessKDPLog::EnableLog, ProcessKDPLog::ListLogCategories}; Log::RegisterLogChannel(ProcessKDP::GetPluginNameStatic(), log_callbacks); }); } void ProcessKDP::DebuggerInitialize(lldb_private::Debugger &debugger) { if (!PluginManager::GetSettingForProcessPlugin( debugger, PluginProperties::GetSettingName())) { const bool is_global_setting = true; PluginManager::CreateSettingForProcessPlugin( debugger, GetGlobalPluginProperties()->GetValueProperties(), ConstString("Properties for the kdp-remote process plug-in."), is_global_setting); } } bool ProcessKDP::StartAsyncThread() { Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); if (log) log->Printf("ProcessKDP::StartAsyncThread ()"); if (m_async_thread.IsJoinable()) return true; m_async_thread = ThreadLauncher::LaunchThread( "", ProcessKDP::AsyncThread, this, NULL); return m_async_thread.IsJoinable(); } void ProcessKDP::StopAsyncThread() { Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); if (log) log->Printf("ProcessKDP::StopAsyncThread ()"); m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit); // Stop the stdio thread if (m_async_thread.IsJoinable()) m_async_thread.Join(nullptr); } void *ProcessKDP::AsyncThread(void *arg) { ProcessKDP *process = (ProcessKDP *)arg; const lldb::pid_t pid = process->GetID(); Log *log(ProcessKDPLog::GetLogIfAllCategoriesSet(KDP_LOG_PROCESS)); if (log) log->Printf("ProcessKDP::AsyncThread (arg = %p, pid = %" PRIu64 ") thread starting...", arg, pid); ListenerSP listener_sp(Listener::MakeListener("ProcessKDP::AsyncThread")); EventSP event_sp; const uint32_t desired_event_mask = eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit; if (listener_sp->StartListeningForEvents(&process->m_async_broadcaster, desired_event_mask) == desired_event_mask) { bool done = false; while (!done) { if (log) log->Printf("ProcessKDP::AsyncThread (pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", pid); if (listener_sp->GetEvent(event_sp, llvm::None)) { uint32_t event_type = event_sp->GetType(); if (log) log->Printf("ProcessKDP::AsyncThread (pid = %" PRIu64 ") Got an event of type: %d...", pid, event_type); // When we are running, poll for 1 second to try and get an exception // to indicate the process has stopped. If we don't get one, check to // make sure no one asked us to exit bool is_running = false; DataExtractor exc_reply_packet; do { switch (event_type) { case eBroadcastBitAsyncContinue: { is_running = true; if (process->m_comm.WaitForPacketWithTimeoutMicroSeconds( exc_reply_packet, 1 * USEC_PER_SEC)) { ThreadSP thread_sp(process->GetKernelThread()); if (thread_sp) { lldb::RegisterContextSP reg_ctx_sp( thread_sp->GetRegisterContext()); if (reg_ctx_sp) reg_ctx_sp->InvalidateAllRegisters(); static_cast(thread_sp.get()) ->SetStopInfoFrom_KDP_EXCEPTION(exc_reply_packet); } // TODO: parse the stop reply packet is_running = false; process->SetPrivateState(eStateStopped); } else { // Check to see if we are supposed to exit. There is no way to // interrupt a running kernel, so all we can do is wait for an // exception or detach... if (listener_sp->GetEvent(event_sp, std::chrono::microseconds(0))) { // We got an event, go through the loop again event_type = event_sp->GetType(); } } } break; case eBroadcastBitAsyncThreadShouldExit: if (log) log->Printf("ProcessKDP::AsyncThread (pid = %" PRIu64 ") got eBroadcastBitAsyncThreadShouldExit...", pid); done = true; is_running = false; break; default: if (log) log->Printf("ProcessKDP::AsyncThread (pid = %" PRIu64 ") got unknown event 0x%8.8x", pid, event_type); done = true; is_running = false; break; } } while (is_running); } else { if (log) log->Printf("ProcessKDP::AsyncThread (pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp) => false", pid); done = true; } } } if (log) log->Printf("ProcessKDP::AsyncThread (arg = %p, pid = %" PRIu64 ") thread exiting...", arg, pid); process->m_async_thread.Reset(); return NULL; } class CommandObjectProcessKDPPacketSend : public CommandObjectParsed { private: OptionGroupOptions m_option_group; OptionGroupUInt64 m_command_byte; OptionGroupString m_packet_data; virtual Options *GetOptions() { return &m_option_group; } public: CommandObjectProcessKDPPacketSend(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "process plugin packet send", "Send a custom packet through the KDP protocol by " "specifying the command byte and the packet " "payload data. A packet will be sent with a " "correct header and payload, and the raw result " "bytes will be displayed as a string value. ", NULL), m_option_group(), m_command_byte(LLDB_OPT_SET_1, true, "command", 'c', 0, eArgTypeNone, "Specify the command byte to use when sending the KDP " "request packet.", 0), m_packet_data(LLDB_OPT_SET_1, false, "payload", 'p', 0, eArgTypeNone, "Specify packet payload bytes as a hex ASCII string with " "no spaces or hex prefixes.", NULL) { m_option_group.Append(&m_command_byte, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Append(&m_packet_data, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); m_option_group.Finalize(); } ~CommandObjectProcessKDPPacketSend() {} bool DoExecute(Args &command, CommandReturnObject &result) { const size_t argc = command.GetArgumentCount(); if (argc == 0) { if (!m_command_byte.GetOptionValue().OptionWasSet()) { result.AppendError( "the --command option must be set to a valid command byte"); result.SetStatus(eReturnStatusFailed); } else { const uint64_t command_byte = m_command_byte.GetOptionValue().GetUInt64Value(0); if (command_byte > 0 && command_byte <= UINT8_MAX) { ProcessKDP *process = (ProcessKDP *)m_interpreter.GetExecutionContext().GetProcessPtr(); if (process) { const StateType state = process->GetState(); if (StateIsStoppedState(state, true)) { std::vector payload_bytes; const char *ascii_hex_bytes_cstr = m_packet_data.GetOptionValue().GetCurrentValue(); if (ascii_hex_bytes_cstr && ascii_hex_bytes_cstr[0]) { StringExtractor extractor(ascii_hex_bytes_cstr); const size_t ascii_hex_bytes_cstr_len = extractor.GetStringRef().size(); if (ascii_hex_bytes_cstr_len & 1) { result.AppendErrorWithFormat("payload data must contain an " "even number of ASCII hex " "characters: '%s'", ascii_hex_bytes_cstr); result.SetStatus(eReturnStatusFailed); return false; } payload_bytes.resize(ascii_hex_bytes_cstr_len / 2); if (extractor.GetHexBytes(payload_bytes, '\xdd') != payload_bytes.size()) { result.AppendErrorWithFormat("payload data must only contain " "ASCII hex characters (no " "spaces or hex prefixes): '%s'", ascii_hex_bytes_cstr); result.SetStatus(eReturnStatusFailed); return false; } } Error error; DataExtractor reply; process->GetCommunication().SendRawRequest( command_byte, payload_bytes.empty() ? NULL : payload_bytes.data(), payload_bytes.size(), reply, error); if (error.Success()) { // Copy the binary bytes into a hex ASCII string for the result StreamString packet; packet.PutBytesAsRawHex8( reply.GetDataStart(), reply.GetByteSize(), endian::InlHostByteOrder(), endian::InlHostByteOrder()); result.AppendMessage(packet.GetString()); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } else { const char *error_cstr = error.AsCString(); if (error_cstr && error_cstr[0]) result.AppendError(error_cstr); else result.AppendErrorWithFormat("unknown error 0x%8.8x", error.GetError()); result.SetStatus(eReturnStatusFailed); return false; } } else { result.AppendErrorWithFormat("process must be stopped in order " "to send KDP packets, state is %s", StateAsCString(state)); result.SetStatus(eReturnStatusFailed); } } else { result.AppendError("invalid process"); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat("invalid command byte 0x%" PRIx64 ", valid values are 1 - 255", command_byte); result.SetStatus(eReturnStatusFailed); } } } else { result.AppendErrorWithFormat("'%s' takes no arguments, only options.", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); } return false; } }; class CommandObjectProcessKDPPacket : public CommandObjectMultiword { private: public: CommandObjectProcessKDPPacket(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "process plugin packet", "Commands that deal with KDP remote packets.", NULL) { LoadSubCommand( "send", CommandObjectSP(new CommandObjectProcessKDPPacketSend(interpreter))); } ~CommandObjectProcessKDPPacket() {} }; class CommandObjectMultiwordProcessKDP : public CommandObjectMultiword { public: CommandObjectMultiwordProcessKDP(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "process plugin", "Commands for operating on a ProcessKDP process.", "process plugin []") { LoadSubCommand("packet", CommandObjectSP(new CommandObjectProcessKDPPacket( interpreter))); } ~CommandObjectMultiwordProcessKDP() {} }; CommandObject *ProcessKDP::GetPluginCommandObject() { if (!m_command_sp) m_command_sp.reset(new CommandObjectMultiwordProcessKDP( GetTarget().GetDebugger().GetCommandInterpreter())); return m_command_sp.get(); } Index: vendor/lldb/dist/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/Process/MacOSX-Kernel/ThreadKDP.cpp (revision 311542) @@ -1,173 +1,172 @@ //===-- ThreadKDP.cpp -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ThreadKDP.h" #include "lldb/Utility/SafeMachO.h" #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamString.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Unwind.h" #include "Plugins/Process/Utility/StopInfoMachException.h" #include "ProcessKDP.h" #include "ProcessKDPLog.h" #include "RegisterContextKDP_arm.h" #include "RegisterContextKDP_arm64.h" #include "RegisterContextKDP_i386.h" #include "RegisterContextKDP_x86_64.h" using namespace lldb; using namespace lldb_private; //---------------------------------------------------------------------- // Thread Registers //---------------------------------------------------------------------- ThreadKDP::ThreadKDP(Process &process, lldb::tid_t tid) : Thread(process, tid), m_thread_name(), m_dispatch_queue_name(), m_thread_dispatch_qaddr(LLDB_INVALID_ADDRESS) { ProcessKDPLog::LogIf(KDP_LOG_THREAD, "%p: ThreadKDP::ThreadKDP (tid = 0x%4.4x)", this, GetID()); } ThreadKDP::~ThreadKDP() { ProcessKDPLog::LogIf(KDP_LOG_THREAD, "%p: ThreadKDP::~ThreadKDP (tid = 0x%4.4x)", this, GetID()); DestroyThread(); } const char *ThreadKDP::GetName() { if (m_thread_name.empty()) return NULL; return m_thread_name.c_str(); } const char *ThreadKDP::GetQueueName() { return NULL; } void ThreadKDP::RefreshStateAfterStop() { // Invalidate all registers in our register context. We don't set "force" to // true because the stop reply packet might have had some register values // that were expedited and these will already be copied into the register // context by the time this function gets called. The KDPRegisterContext // class has been made smart enough to detect when it needs to invalidate // which registers are valid by putting hooks in the register read and // register supply functions where they check the process stop ID and do // the right thing. const bool force = false; lldb::RegisterContextSP reg_ctx_sp(GetRegisterContext()); if (reg_ctx_sp) reg_ctx_sp->InvalidateIfNeeded(force); } bool ThreadKDP::ThreadIDIsValid(lldb::tid_t thread) { return thread != 0; } void ThreadKDP::Dump(Log *log, uint32_t index) {} bool ThreadKDP::ShouldStop(bool &step_more) { return true; } lldb::RegisterContextSP ThreadKDP::GetRegisterContext() { if (m_reg_context_sp.get() == NULL) m_reg_context_sp = CreateRegisterContextForFrame(NULL); return m_reg_context_sp; } lldb::RegisterContextSP ThreadKDP::CreateRegisterContextForFrame(StackFrame *frame) { lldb::RegisterContextSP reg_ctx_sp; uint32_t concrete_frame_idx = 0; if (frame) concrete_frame_idx = frame->GetConcreteFrameIndex(); if (concrete_frame_idx == 0) { ProcessSP process_sp(CalculateProcess()); if (process_sp) { switch (static_cast(process_sp.get()) ->GetCommunication() .GetCPUType()) { case llvm::MachO::CPU_TYPE_ARM: reg_ctx_sp.reset(new RegisterContextKDP_arm(*this, concrete_frame_idx)); break; case llvm::MachO::CPU_TYPE_ARM64: reg_ctx_sp.reset( new RegisterContextKDP_arm64(*this, concrete_frame_idx)); break; case llvm::MachO::CPU_TYPE_I386: reg_ctx_sp.reset( new RegisterContextKDP_i386(*this, concrete_frame_idx)); break; case llvm::MachO::CPU_TYPE_X86_64: reg_ctx_sp.reset( new RegisterContextKDP_x86_64(*this, concrete_frame_idx)); break; default: - assert(!"Add CPU type support in KDP"); - break; + llvm_unreachable("Add CPU type support in KDP"); } } } else { Unwind *unwinder = GetUnwinder(); if (unwinder) reg_ctx_sp = unwinder->CreateRegisterContextForFrame(frame); } return reg_ctx_sp; } bool ThreadKDP::CalculateStopInfo() { ProcessSP process_sp(GetProcess()); if (process_sp) { if (m_cached_stop_info_sp) { SetStopInfo(m_cached_stop_info_sp); } else { SetStopInfo(StopInfo::CreateStopReasonWithSignal(*this, SIGSTOP)); } return true; } return false; } void ThreadKDP::SetStopInfoFrom_KDP_EXCEPTION( const DataExtractor &exc_reply_packet) { lldb::offset_t offset = 0; uint8_t reply_command = exc_reply_packet.GetU8(&offset); if (reply_command == CommunicationKDP::KDP_EXCEPTION) { offset = 8; const uint32_t count = exc_reply_packet.GetU32(&offset); if (count >= 1) { // const uint32_t cpu = exc_reply_packet.GetU32 (&offset); offset += 4; // Skip the useless CPU field const uint32_t exc_type = exc_reply_packet.GetU32(&offset); const uint32_t exc_code = exc_reply_packet.GetU32(&offset); const uint32_t exc_subcode = exc_reply_packet.GetU32(&offset); // We have to make a copy of the stop info because the thread list // will iterate through the threads and clear all stop infos.. // Let the StopInfoMachException::CreateStopReasonWithMachException() // function update the PC if needed as we might hit a software breakpoint // and need to decrement the PC (i386 and x86_64 need this) and KDP // doesn't do this for us. const bool pc_already_adjusted = false; const bool adjust_pc_if_needed = true; m_cached_stop_info_sp = StopInfoMachException::CreateStopReasonWithMachException( *this, exc_type, 2, exc_code, exc_subcode, 0, pc_already_adjusted, adjust_pc_if_needed); } } } Index: vendor/lldb/dist/source/Plugins/Process/POSIX/CrashReason.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/POSIX/CrashReason.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/Process/POSIX/CrashReason.cpp (revision 311542) @@ -1,341 +1,343 @@ //===-- CrashReason.cpp -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "CrashReason.h" #include "llvm/Support/raw_ostream.h" #include namespace { void AppendFaultAddr(std::string &str, lldb::addr_t addr) { std::stringstream ss; ss << " (fault address: 0x" << std::hex << addr << ")"; str += ss.str(); } +#if defined(si_lower) && defined(si_upper) void AppendBounds(std::string &str, lldb::addr_t lower_bound, lldb::addr_t upper_bound, lldb::addr_t addr) { llvm::raw_string_ostream stream(str); if ((unsigned long)addr < lower_bound) stream << ": lower bound violation "; else stream << ": upper bound violation "; stream << "(fault address: 0x"; stream.write_hex(addr); stream << ", lower bound: 0x"; stream.write_hex(lower_bound); stream << ", upper bound: 0x"; stream.write_hex(upper_bound); stream << ")"; stream.flush(); } +#endif CrashReason GetCrashReasonForSIGSEGV(const siginfo_t &info) { assert(info.si_signo == SIGSEGV); switch (info.si_code) { #ifdef SI_KERNEL case SI_KERNEL: // Some platforms will occasionally send nonstandard spurious SI_KERNEL // codes. // One way to get this is via unaligned SIMD loads. return CrashReason::eInvalidAddress; // for lack of anything better #endif case SEGV_MAPERR: return CrashReason::eInvalidAddress; case SEGV_ACCERR: return CrashReason::ePrivilegedAddress; #ifndef SEGV_BNDERR #define SEGV_BNDERR 3 #endif case SEGV_BNDERR: return CrashReason::eBoundViolation; } assert(false && "unexpected si_code for SIGSEGV"); return CrashReason::eInvalidCrashReason; } CrashReason GetCrashReasonForSIGILL(const siginfo_t &info) { assert(info.si_signo == SIGILL); switch (info.si_code) { case ILL_ILLOPC: return CrashReason::eIllegalOpcode; case ILL_ILLOPN: return CrashReason::eIllegalOperand; case ILL_ILLADR: return CrashReason::eIllegalAddressingMode; case ILL_ILLTRP: return CrashReason::eIllegalTrap; case ILL_PRVOPC: return CrashReason::ePrivilegedOpcode; case ILL_PRVREG: return CrashReason::ePrivilegedRegister; case ILL_COPROC: return CrashReason::eCoprocessorError; case ILL_BADSTK: return CrashReason::eInternalStackError; } assert(false && "unexpected si_code for SIGILL"); return CrashReason::eInvalidCrashReason; } CrashReason GetCrashReasonForSIGFPE(const siginfo_t &info) { assert(info.si_signo == SIGFPE); switch (info.si_code) { case FPE_INTDIV: return CrashReason::eIntegerDivideByZero; case FPE_INTOVF: return CrashReason::eIntegerOverflow; case FPE_FLTDIV: return CrashReason::eFloatDivideByZero; case FPE_FLTOVF: return CrashReason::eFloatOverflow; case FPE_FLTUND: return CrashReason::eFloatUnderflow; case FPE_FLTRES: return CrashReason::eFloatInexactResult; case FPE_FLTINV: return CrashReason::eFloatInvalidOperation; case FPE_FLTSUB: return CrashReason::eFloatSubscriptRange; } assert(false && "unexpected si_code for SIGFPE"); return CrashReason::eInvalidCrashReason; } CrashReason GetCrashReasonForSIGBUS(const siginfo_t &info) { assert(info.si_signo == SIGBUS); switch (info.si_code) { case BUS_ADRALN: return CrashReason::eIllegalAlignment; case BUS_ADRERR: return CrashReason::eIllegalAddress; case BUS_OBJERR: return CrashReason::eHardwareError; } assert(false && "unexpected si_code for SIGBUS"); return CrashReason::eInvalidCrashReason; } } std::string GetCrashReasonString(CrashReason reason, const siginfo_t &info) { std::string str; // make sure that siginfo_t has the bound fields available. #if defined(si_lower) && defined(si_upper) if (reason == CrashReason::eBoundViolation) { str = "signal SIGSEGV"; AppendBounds(str, reinterpret_cast(info.si_lower), reinterpret_cast(info.si_upper), reinterpret_cast(info.si_addr)); return str; } #endif return GetCrashReasonString(reason, reinterpret_cast(info.si_addr)); } std::string GetCrashReasonString(CrashReason reason, lldb::addr_t fault_addr) { std::string str; switch (reason) { default: assert(false && "invalid CrashReason"); break; case CrashReason::eInvalidAddress: str = "signal SIGSEGV: invalid address"; AppendFaultAddr(str, fault_addr); break; case CrashReason::ePrivilegedAddress: str = "signal SIGSEGV: address access protected"; AppendFaultAddr(str, fault_addr); break; case CrashReason::eBoundViolation: str = "signal SIGSEGV: bound violation"; break; case CrashReason::eIllegalOpcode: str = "signal SIGILL: illegal instruction"; break; case CrashReason::eIllegalOperand: str = "signal SIGILL: illegal instruction operand"; break; case CrashReason::eIllegalAddressingMode: str = "signal SIGILL: illegal addressing mode"; break; case CrashReason::eIllegalTrap: str = "signal SIGILL: illegal trap"; break; case CrashReason::ePrivilegedOpcode: str = "signal SIGILL: privileged instruction"; break; case CrashReason::ePrivilegedRegister: str = "signal SIGILL: privileged register"; break; case CrashReason::eCoprocessorError: str = "signal SIGILL: coprocessor error"; break; case CrashReason::eInternalStackError: str = "signal SIGILL: internal stack error"; break; case CrashReason::eIllegalAlignment: str = "signal SIGBUS: illegal alignment"; break; case CrashReason::eIllegalAddress: str = "signal SIGBUS: illegal address"; break; case CrashReason::eHardwareError: str = "signal SIGBUS: hardware error"; break; case CrashReason::eIntegerDivideByZero: str = "signal SIGFPE: integer divide by zero"; break; case CrashReason::eIntegerOverflow: str = "signal SIGFPE: integer overflow"; break; case CrashReason::eFloatDivideByZero: str = "signal SIGFPE: floating point divide by zero"; break; case CrashReason::eFloatOverflow: str = "signal SIGFPE: floating point overflow"; break; case CrashReason::eFloatUnderflow: str = "signal SIGFPE: floating point underflow"; break; case CrashReason::eFloatInexactResult: str = "signal SIGFPE: inexact floating point result"; break; case CrashReason::eFloatInvalidOperation: str = "signal SIGFPE: invalid floating point operation"; break; case CrashReason::eFloatSubscriptRange: str = "signal SIGFPE: invalid floating point subscript range"; break; } return str; } const char *CrashReasonAsString(CrashReason reason) { #ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION // Just return the code in ascii for integration builds. chcar str[8]; sprintf(str, "%d", reason); #else const char *str = nullptr; switch (reason) { case CrashReason::eInvalidCrashReason: str = "eInvalidCrashReason"; break; // SIGSEGV crash reasons. case CrashReason::eInvalidAddress: str = "eInvalidAddress"; break; case CrashReason::ePrivilegedAddress: str = "ePrivilegedAddress"; break; case CrashReason::eBoundViolation: str = "eBoundViolation"; break; // SIGILL crash reasons. case CrashReason::eIllegalOpcode: str = "eIllegalOpcode"; break; case CrashReason::eIllegalOperand: str = "eIllegalOperand"; break; case CrashReason::eIllegalAddressingMode: str = "eIllegalAddressingMode"; break; case CrashReason::eIllegalTrap: str = "eIllegalTrap"; break; case CrashReason::ePrivilegedOpcode: str = "ePrivilegedOpcode"; break; case CrashReason::ePrivilegedRegister: str = "ePrivilegedRegister"; break; case CrashReason::eCoprocessorError: str = "eCoprocessorError"; break; case CrashReason::eInternalStackError: str = "eInternalStackError"; break; // SIGBUS crash reasons: case CrashReason::eIllegalAlignment: str = "eIllegalAlignment"; break; case CrashReason::eIllegalAddress: str = "eIllegalAddress"; break; case CrashReason::eHardwareError: str = "eHardwareError"; break; // SIGFPE crash reasons: case CrashReason::eIntegerDivideByZero: str = "eIntegerDivideByZero"; break; case CrashReason::eIntegerOverflow: str = "eIntegerOverflow"; break; case CrashReason::eFloatDivideByZero: str = "eFloatDivideByZero"; break; case CrashReason::eFloatOverflow: str = "eFloatOverflow"; break; case CrashReason::eFloatUnderflow: str = "eFloatUnderflow"; break; case CrashReason::eFloatInexactResult: str = "eFloatInexactResult"; break; case CrashReason::eFloatInvalidOperation: str = "eFloatInvalidOperation"; break; case CrashReason::eFloatSubscriptRange: str = "eFloatSubscriptRange"; break; } #endif return str; } CrashReason GetCrashReason(const siginfo_t &info) { switch (info.si_signo) { case SIGSEGV: return GetCrashReasonForSIGSEGV(info); case SIGBUS: return GetCrashReasonForSIGBUS(info); case SIGFPE: return GetCrashReasonForSIGFPE(info); case SIGILL: return GetCrashReasonForSIGILL(info); } assert(false && "unexpected signal"); return CrashReason::eInvalidCrashReason; } Index: vendor/lldb/dist/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp (revision 311542) @@ -1,748 +1,748 @@ //===-- DynamicRegisterInfo.cpp ----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "DynamicRegisterInfo.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/ArchSpec.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/StructuredData.h" #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Host/StringConvert.h" #include "lldb/Utility/StringExtractor.h" using namespace lldb; using namespace lldb_private; DynamicRegisterInfo::DynamicRegisterInfo() : m_regs(), m_sets(), m_set_reg_nums(), m_set_names(), m_value_regs_map(), m_invalidate_regs_map(), m_dynamic_reg_size_map(), m_reg_data_byte_size(0), m_finalized(false) {} DynamicRegisterInfo::DynamicRegisterInfo( const lldb_private::StructuredData::Dictionary &dict, const lldb_private::ArchSpec &arch) : m_regs(), m_sets(), m_set_reg_nums(), m_set_names(), m_value_regs_map(), m_invalidate_regs_map(), m_dynamic_reg_size_map(), m_reg_data_byte_size(0), m_finalized(false) { SetRegisterInfo(dict, arch); } DynamicRegisterInfo::~DynamicRegisterInfo() {} size_t DynamicRegisterInfo::SetRegisterInfo(const StructuredData::Dictionary &dict, const ArchSpec &arch) { assert(!m_finalized); StructuredData::Array *sets = nullptr; if (dict.GetValueForKeyAsArray("sets", sets)) { const uint32_t num_sets = sets->GetSize(); for (uint32_t i = 0; i < num_sets; ++i) { std::string set_name_str; ConstString set_name; if (sets->GetItemAtIndexAsString(i, set_name_str)) set_name.SetCString(set_name_str.c_str()); if (set_name) { RegisterSet new_set = {set_name.AsCString(), NULL, 0, NULL}; m_sets.push_back(new_set); } else { Clear(); printf("error: register sets must have valid names\n"); return 0; } } m_set_reg_nums.resize(m_sets.size()); } StructuredData::Array *regs = nullptr; if (!dict.GetValueForKeyAsArray("registers", regs)) return 0; const uint32_t num_regs = regs->GetSize(); // typedef std::map > // InvalidateNameMap; // InvalidateNameMap invalidate_map; for (uint32_t i = 0; i < num_regs; ++i) { StructuredData::Dictionary *reg_info_dict = nullptr; if (!regs->GetItemAtIndexAsDictionary(i, reg_info_dict)) { Clear(); printf("error: items in the 'registers' array must be dictionaries\n"); regs->DumpToStdout(); return 0; } // { 'name':'rcx' , 'bitsize' : 64, 'offset' : 16, 'encoding':'uint' // , 'format':'hex' , 'set': 0, 'ehframe' : 2, // 'dwarf' : 2, 'generic':'arg4', 'alt-name':'arg4', }, RegisterInfo reg_info; std::vector value_regs; std::vector invalidate_regs; memset(®_info, 0, sizeof(reg_info)); ConstString name_val; ConstString alt_name_val; if (!reg_info_dict->GetValueForKeyAsString("name", name_val, nullptr)) { Clear(); printf("error: registers must have valid names and offsets\n"); reg_info_dict->DumpToStdout(); return 0; } reg_info.name = name_val.GetCString(); reg_info_dict->GetValueForKeyAsString("alt-name", alt_name_val, nullptr); reg_info.alt_name = alt_name_val.GetCString(); reg_info_dict->GetValueForKeyAsInteger("offset", reg_info.byte_offset, UINT32_MAX); const ByteOrder byte_order = arch.GetByteOrder(); if (reg_info.byte_offset == UINT32_MAX) { // No offset for this register, see if the register has a value expression // which indicates this register is part of another register. Value // expressions // are things like "rax[31:0]" which state that the current register's // value // is in a concrete register "rax" in bits 31:0. If there is a value // expression // we can calculate the offset bool success = false; std::string slice_str; if (reg_info_dict->GetValueForKeyAsString("slice", slice_str, nullptr)) { // Slices use the following format: // REGNAME[MSBIT:LSBIT] // REGNAME - name of the register to grab a slice of // MSBIT - the most significant bit at which the current register value // starts at // LSBIT - the least significant bit at which the current register value // ends at static RegularExpression g_bitfield_regex( llvm::StringRef("([A-Za-z_][A-Za-z0-9_]*)\\[([0-9]+):([0-9]+)\\]")); RegularExpression::Match regex_match(3); if (g_bitfield_regex.Execute(slice_str, ®ex_match)) { llvm::StringRef reg_name_str; std::string msbit_str; std::string lsbit_str; if (regex_match.GetMatchAtIndex(slice_str.c_str(), 1, reg_name_str) && regex_match.GetMatchAtIndex(slice_str.c_str(), 2, msbit_str) && regex_match.GetMatchAtIndex(slice_str.c_str(), 3, lsbit_str)) { const uint32_t msbit = StringConvert::ToUInt32(msbit_str.c_str(), UINT32_MAX); const uint32_t lsbit = StringConvert::ToUInt32(lsbit_str.c_str(), UINT32_MAX); if (msbit != UINT32_MAX && lsbit != UINT32_MAX) { if (msbit > lsbit) { const uint32_t msbyte = msbit / 8; const uint32_t lsbyte = lsbit / 8; ConstString containing_reg_name(reg_name_str); RegisterInfo *containing_reg_info = GetRegisterInfo(containing_reg_name); if (containing_reg_info) { const uint32_t max_bit = containing_reg_info->byte_size * 8; if (msbit < max_bit && lsbit < max_bit) { m_invalidate_regs_map[containing_reg_info ->kinds[eRegisterKindLLDB]] .push_back(i); m_value_regs_map[i].push_back( containing_reg_info->kinds[eRegisterKindLLDB]); m_invalidate_regs_map[i].push_back( containing_reg_info->kinds[eRegisterKindLLDB]); if (byte_order == eByteOrderLittle) { success = true; reg_info.byte_offset = containing_reg_info->byte_offset + lsbyte; } else if (byte_order == eByteOrderBig) { success = true; reg_info.byte_offset = containing_reg_info->byte_offset + msbyte; } else { - assert(!"Invalid byte order"); + llvm_unreachable("Invalid byte order"); } } else { if (msbit > max_bit) printf("error: msbit (%u) must be less than the bitsize " "of the register (%u)\n", msbit, max_bit); else printf("error: lsbit (%u) must be less than the bitsize " "of the register (%u)\n", lsbit, max_bit); } } else { printf("error: invalid concrete register \"%s\"\n", containing_reg_name.GetCString()); } } else { printf("error: msbit (%u) must be greater than lsbit (%u)\n", msbit, lsbit); } } else { printf("error: msbit (%u) and lsbit (%u) must be valid\n", msbit, lsbit); } } else { // TODO: print error invalid slice string that doesn't follow the // format printf("error: failed to extract regex matches for parsing the " "register bitfield regex\n"); } } else { // TODO: print error invalid slice string that doesn't follow the // format printf("error: failed to match against register bitfield regex\n"); } } else { StructuredData::Array *composite_reg_list = nullptr; if (reg_info_dict->GetValueForKeyAsArray("composite", composite_reg_list)) { const size_t num_composite_regs = composite_reg_list->GetSize(); if (num_composite_regs > 0) { uint32_t composite_offset = UINT32_MAX; for (uint32_t composite_idx = 0; composite_idx < num_composite_regs; ++composite_idx) { ConstString composite_reg_name; if (composite_reg_list->GetItemAtIndexAsString( composite_idx, composite_reg_name, nullptr)) { RegisterInfo *composite_reg_info = GetRegisterInfo(composite_reg_name); if (composite_reg_info) { composite_offset = std::min(composite_offset, composite_reg_info->byte_offset); m_value_regs_map[i].push_back( composite_reg_info->kinds[eRegisterKindLLDB]); m_invalidate_regs_map[composite_reg_info ->kinds[eRegisterKindLLDB]] .push_back(i); m_invalidate_regs_map[i].push_back( composite_reg_info->kinds[eRegisterKindLLDB]); } else { // TODO: print error invalid slice string that doesn't follow // the format printf("error: failed to find composite register by name: " "\"%s\"\n", composite_reg_name.GetCString()); } } else { printf( "error: 'composite' list value wasn't a python string\n"); } } if (composite_offset != UINT32_MAX) { reg_info.byte_offset = composite_offset; success = m_value_regs_map.find(i) != m_value_regs_map.end(); } else { printf("error: 'composite' registers must specify at least one " "real register\n"); } } else { printf("error: 'composite' list was empty\n"); } } } if (!success) { Clear(); reg_info_dict->DumpToStdout(); return 0; } } int64_t bitsize = 0; if (!reg_info_dict->GetValueForKeyAsInteger("bitsize", bitsize)) { Clear(); printf("error: invalid or missing 'bitsize' key/value pair in register " "dictionary\n"); reg_info_dict->DumpToStdout(); return 0; } reg_info.byte_size = bitsize / 8; std::string dwarf_opcode_string; if (reg_info_dict->GetValueForKeyAsString("dynamic_size_dwarf_expr_bytes", dwarf_opcode_string)) { reg_info.dynamic_size_dwarf_len = dwarf_opcode_string.length() / 2; assert(reg_info.dynamic_size_dwarf_len > 0); std::vector dwarf_opcode_bytes(reg_info.dynamic_size_dwarf_len); uint32_t j; StringExtractor opcode_extractor; // Swap "dwarf_opcode_string" over into "opcode_extractor" opcode_extractor.GetStringRef().swap(dwarf_opcode_string); uint32_t ret_val = opcode_extractor.GetHexBytesAvail(dwarf_opcode_bytes); assert(ret_val == reg_info.dynamic_size_dwarf_len); for (j = 0; j < reg_info.dynamic_size_dwarf_len; ++j) m_dynamic_reg_size_map[i].push_back(dwarf_opcode_bytes[j]); reg_info.dynamic_size_dwarf_expr_bytes = m_dynamic_reg_size_map[i].data(); } std::string format_str; if (reg_info_dict->GetValueForKeyAsString("format", format_str, nullptr)) { if (Args::StringToFormat(format_str.c_str(), reg_info.format, NULL) .Fail()) { Clear(); printf("error: invalid 'format' value in register dictionary\n"); reg_info_dict->DumpToStdout(); return 0; } } else { reg_info_dict->GetValueForKeyAsInteger("format", reg_info.format, eFormatHex); } std::string encoding_str; if (reg_info_dict->GetValueForKeyAsString("encoding", encoding_str)) reg_info.encoding = Args::StringToEncoding(encoding_str, eEncodingUint); else reg_info_dict->GetValueForKeyAsInteger("encoding", reg_info.encoding, eEncodingUint); size_t set = 0; if (!reg_info_dict->GetValueForKeyAsInteger("set", set, -1) || set >= m_sets.size()) { Clear(); printf("error: invalid 'set' value in register dictionary, valid values " "are 0 - %i\n", (int)set); reg_info_dict->DumpToStdout(); return 0; } // Fill in the register numbers reg_info.kinds[lldb::eRegisterKindLLDB] = i; reg_info.kinds[lldb::eRegisterKindProcessPlugin] = i; uint32_t eh_frame_regno = LLDB_INVALID_REGNUM; reg_info_dict->GetValueForKeyAsInteger("gcc", eh_frame_regno, LLDB_INVALID_REGNUM); if (eh_frame_regno == LLDB_INVALID_REGNUM) reg_info_dict->GetValueForKeyAsInteger("ehframe", eh_frame_regno, LLDB_INVALID_REGNUM); reg_info.kinds[lldb::eRegisterKindEHFrame] = eh_frame_regno; reg_info_dict->GetValueForKeyAsInteger( "dwarf", reg_info.kinds[lldb::eRegisterKindDWARF], LLDB_INVALID_REGNUM); std::string generic_str; if (reg_info_dict->GetValueForKeyAsString("generic", generic_str)) reg_info.kinds[lldb::eRegisterKindGeneric] = Args::StringToGenericRegister(generic_str); else reg_info_dict->GetValueForKeyAsInteger( "generic", reg_info.kinds[lldb::eRegisterKindGeneric], LLDB_INVALID_REGNUM); // Check if this register invalidates any other register values when it is // modified StructuredData::Array *invalidate_reg_list = nullptr; if (reg_info_dict->GetValueForKeyAsArray("invalidate-regs", invalidate_reg_list)) { const size_t num_regs = invalidate_reg_list->GetSize(); if (num_regs > 0) { for (uint32_t idx = 0; idx < num_regs; ++idx) { ConstString invalidate_reg_name; uint64_t invalidate_reg_num; if (invalidate_reg_list->GetItemAtIndexAsString( idx, invalidate_reg_name)) { RegisterInfo *invalidate_reg_info = GetRegisterInfo(invalidate_reg_name); if (invalidate_reg_info) { m_invalidate_regs_map[i].push_back( invalidate_reg_info->kinds[eRegisterKindLLDB]); } else { // TODO: print error invalid slice string that doesn't follow the // format printf("error: failed to find a 'invalidate-regs' register for " "\"%s\" while parsing register \"%s\"\n", invalidate_reg_name.GetCString(), reg_info.name); } } else if (invalidate_reg_list->GetItemAtIndexAsInteger( idx, invalidate_reg_num)) { if (invalidate_reg_num != UINT64_MAX) m_invalidate_regs_map[i].push_back(invalidate_reg_num); else printf("error: 'invalidate-regs' list value wasn't a valid " "integer\n"); } else { printf("error: 'invalidate-regs' list value wasn't a python string " "or integer\n"); } } } else { printf("error: 'invalidate-regs' contained an empty list\n"); } } // Calculate the register offset const size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size; if (m_reg_data_byte_size < end_reg_offset) m_reg_data_byte_size = end_reg_offset; m_regs.push_back(reg_info); m_set_reg_nums[set].push_back(i); } Finalize(arch); return m_regs.size(); } void DynamicRegisterInfo::AddRegister(RegisterInfo ®_info, ConstString ®_name, ConstString ®_alt_name, ConstString &set_name) { assert(!m_finalized); const uint32_t reg_num = m_regs.size(); reg_info.name = reg_name.AsCString(); assert(reg_info.name); reg_info.alt_name = reg_alt_name.AsCString(NULL); uint32_t i; if (reg_info.value_regs) { for (i = 0; reg_info.value_regs[i] != LLDB_INVALID_REGNUM; ++i) m_value_regs_map[reg_num].push_back(reg_info.value_regs[i]); } if (reg_info.invalidate_regs) { for (i = 0; reg_info.invalidate_regs[i] != LLDB_INVALID_REGNUM; ++i) m_invalidate_regs_map[reg_num].push_back(reg_info.invalidate_regs[i]); } if (reg_info.dynamic_size_dwarf_expr_bytes) { for (i = 0; i < reg_info.dynamic_size_dwarf_len; ++i) m_dynamic_reg_size_map[reg_num].push_back( reg_info.dynamic_size_dwarf_expr_bytes[i]); reg_info.dynamic_size_dwarf_expr_bytes = m_dynamic_reg_size_map[reg_num].data(); } m_regs.push_back(reg_info); uint32_t set = GetRegisterSetIndexByName(set_name, true); assert(set < m_sets.size()); assert(set < m_set_reg_nums.size()); assert(set < m_set_names.size()); m_set_reg_nums[set].push_back(reg_num); size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size; if (m_reg_data_byte_size < end_reg_offset) m_reg_data_byte_size = end_reg_offset; } void DynamicRegisterInfo::Finalize(const ArchSpec &arch) { if (m_finalized) return; m_finalized = true; const size_t num_sets = m_sets.size(); for (size_t set = 0; set < num_sets; ++set) { assert(m_sets.size() == m_set_reg_nums.size()); m_sets[set].num_registers = m_set_reg_nums[set].size(); m_sets[set].registers = &m_set_reg_nums[set][0]; } // sort and unique all value registers and make sure each is terminated with // LLDB_INVALID_REGNUM for (reg_to_regs_map::iterator pos = m_value_regs_map.begin(), end = m_value_regs_map.end(); pos != end; ++pos) { if (pos->second.size() > 1) { std::sort(pos->second.begin(), pos->second.end()); reg_num_collection::iterator unique_end = std::unique(pos->second.begin(), pos->second.end()); if (unique_end != pos->second.end()) pos->second.erase(unique_end, pos->second.end()); } assert(!pos->second.empty()); if (pos->second.back() != LLDB_INVALID_REGNUM) pos->second.push_back(LLDB_INVALID_REGNUM); } // Now update all value_regs with each register info as needed const size_t num_regs = m_regs.size(); for (size_t i = 0; i < num_regs; ++i) { if (m_value_regs_map.find(i) != m_value_regs_map.end()) m_regs[i].value_regs = m_value_regs_map[i].data(); else m_regs[i].value_regs = NULL; } // Expand all invalidation dependencies for (reg_to_regs_map::iterator pos = m_invalidate_regs_map.begin(), end = m_invalidate_regs_map.end(); pos != end; ++pos) { const uint32_t reg_num = pos->first; if (m_regs[reg_num].value_regs) { reg_num_collection extra_invalid_regs; for (const uint32_t invalidate_reg_num : pos->second) { reg_to_regs_map::iterator invalidate_pos = m_invalidate_regs_map.find(invalidate_reg_num); if (invalidate_pos != m_invalidate_regs_map.end()) { for (const uint32_t concrete_invalidate_reg_num : invalidate_pos->second) { if (concrete_invalidate_reg_num != reg_num) extra_invalid_regs.push_back(concrete_invalidate_reg_num); } } } pos->second.insert(pos->second.end(), extra_invalid_regs.begin(), extra_invalid_regs.end()); } } // sort and unique all invalidate registers and make sure each is terminated // with // LLDB_INVALID_REGNUM for (reg_to_regs_map::iterator pos = m_invalidate_regs_map.begin(), end = m_invalidate_regs_map.end(); pos != end; ++pos) { if (pos->second.size() > 1) { std::sort(pos->second.begin(), pos->second.end()); reg_num_collection::iterator unique_end = std::unique(pos->second.begin(), pos->second.end()); if (unique_end != pos->second.end()) pos->second.erase(unique_end, pos->second.end()); } assert(!pos->second.empty()); if (pos->second.back() != LLDB_INVALID_REGNUM) pos->second.push_back(LLDB_INVALID_REGNUM); } // Now update all invalidate_regs with each register info as needed for (size_t i = 0; i < num_regs; ++i) { if (m_invalidate_regs_map.find(i) != m_invalidate_regs_map.end()) m_regs[i].invalidate_regs = m_invalidate_regs_map[i].data(); else m_regs[i].invalidate_regs = NULL; } // Check if we need to automatically set the generic registers in case // they weren't set bool generic_regs_specified = false; for (const auto ® : m_regs) { if (reg.kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM) { generic_regs_specified = true; break; } } if (!generic_regs_specified) { switch (arch.GetMachine()) { case llvm::Triple::aarch64: case llvm::Triple::aarch64_be: for (auto ® : m_regs) { if (strcmp(reg.name, "pc") == 0) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; else if ((strcmp(reg.name, "fp") == 0) || (strcmp(reg.name, "x29") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; else if ((strcmp(reg.name, "lr") == 0) || (strcmp(reg.name, "x30") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; else if ((strcmp(reg.name, "sp") == 0) || (strcmp(reg.name, "x31") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; else if (strcmp(reg.name, "cpsr") == 0) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; } break; case llvm::Triple::arm: case llvm::Triple::armeb: case llvm::Triple::thumb: case llvm::Triple::thumbeb: for (auto ® : m_regs) { if ((strcmp(reg.name, "pc") == 0) || (strcmp(reg.name, "r15") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; else if ((strcmp(reg.name, "sp") == 0) || (strcmp(reg.name, "r13") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; else if ((strcmp(reg.name, "lr") == 0) || (strcmp(reg.name, "r14") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; else if ((strcmp(reg.name, "r7") == 0) && arch.GetTriple().getVendor() == llvm::Triple::Apple) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; else if ((strcmp(reg.name, "r11") == 0) && arch.GetTriple().getVendor() != llvm::Triple::Apple) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; else if (strcmp(reg.name, "fp") == 0) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; else if (strcmp(reg.name, "cpsr") == 0) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; } break; case llvm::Triple::x86: for (auto ® : m_regs) { if ((strcmp(reg.name, "eip") == 0) || (strcmp(reg.name, "pc") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; else if ((strcmp(reg.name, "esp") == 0) || (strcmp(reg.name, "sp") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; else if ((strcmp(reg.name, "ebp") == 0) || (strcmp(reg.name, "fp") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; else if ((strcmp(reg.name, "eflags") == 0) || (strcmp(reg.name, "flags") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; } break; case llvm::Triple::x86_64: for (auto ® : m_regs) { if ((strcmp(reg.name, "rip") == 0) || (strcmp(reg.name, "pc") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; else if ((strcmp(reg.name, "rsp") == 0) || (strcmp(reg.name, "sp") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; else if ((strcmp(reg.name, "rbp") == 0) || (strcmp(reg.name, "fp") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; else if ((strcmp(reg.name, "rflags") == 0) || (strcmp(reg.name, "flags") == 0)) reg.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; } break; default: break; } } } size_t DynamicRegisterInfo::GetNumRegisters() const { return m_regs.size(); } size_t DynamicRegisterInfo::GetNumRegisterSets() const { return m_sets.size(); } size_t DynamicRegisterInfo::GetRegisterDataByteSize() const { return m_reg_data_byte_size; } const RegisterInfo * DynamicRegisterInfo::GetRegisterInfoAtIndex(uint32_t i) const { if (i < m_regs.size()) return &m_regs[i]; return NULL; } RegisterInfo *DynamicRegisterInfo::GetRegisterInfoAtIndex(uint32_t i) { if (i < m_regs.size()) return &m_regs[i]; return NULL; } const RegisterSet *DynamicRegisterInfo::GetRegisterSet(uint32_t i) const { if (i < m_sets.size()) return &m_sets[i]; return NULL; } uint32_t DynamicRegisterInfo::GetRegisterSetIndexByName(ConstString &set_name, bool can_create) { name_collection::iterator pos, end = m_set_names.end(); for (pos = m_set_names.begin(); pos != end; ++pos) { if (*pos == set_name) return std::distance(m_set_names.begin(), pos); } m_set_names.push_back(set_name); m_set_reg_nums.resize(m_set_reg_nums.size() + 1); RegisterSet new_set = {set_name.AsCString(), NULL, 0, NULL}; m_sets.push_back(new_set); return m_sets.size() - 1; } uint32_t DynamicRegisterInfo::ConvertRegisterKindToRegisterNumber(uint32_t kind, uint32_t num) const { reg_collection::const_iterator pos, end = m_regs.end(); for (pos = m_regs.begin(); pos != end; ++pos) { if (pos->kinds[kind] == num) return std::distance(m_regs.begin(), pos); } return LLDB_INVALID_REGNUM; } void DynamicRegisterInfo::Clear() { m_regs.clear(); m_sets.clear(); m_set_reg_nums.clear(); m_set_names.clear(); m_value_regs_map.clear(); m_invalidate_regs_map.clear(); m_dynamic_reg_size_map.clear(); m_reg_data_byte_size = 0; m_finalized = false; } void DynamicRegisterInfo::Dump() const { StreamFile s(stdout, false); const size_t num_regs = m_regs.size(); s.Printf("%p: DynamicRegisterInfo contains %" PRIu64 " registers:\n", static_cast(this), static_cast(num_regs)); for (size_t i = 0; i < num_regs; ++i) { s.Printf("[%3" PRIu64 "] name = %-10s", (uint64_t)i, m_regs[i].name); s.Printf(", size = %2u, offset = %4u, encoding = %u, format = %-10s", m_regs[i].byte_size, m_regs[i].byte_offset, m_regs[i].encoding, FormatManager::GetFormatAsCString(m_regs[i].format)); if (m_regs[i].kinds[eRegisterKindProcessPlugin] != LLDB_INVALID_REGNUM) s.Printf(", process plugin = %3u", m_regs[i].kinds[eRegisterKindProcessPlugin]); if (m_regs[i].kinds[eRegisterKindDWARF] != LLDB_INVALID_REGNUM) s.Printf(", dwarf = %3u", m_regs[i].kinds[eRegisterKindDWARF]); if (m_regs[i].kinds[eRegisterKindEHFrame] != LLDB_INVALID_REGNUM) s.Printf(", ehframe = %3u", m_regs[i].kinds[eRegisterKindEHFrame]); if (m_regs[i].kinds[eRegisterKindGeneric] != LLDB_INVALID_REGNUM) s.Printf(", generic = %3u", m_regs[i].kinds[eRegisterKindGeneric]); if (m_regs[i].alt_name) s.Printf(", alt-name = %s", m_regs[i].alt_name); if (m_regs[i].value_regs) { s.Printf(", value_regs = [ "); for (size_t j = 0; m_regs[i].value_regs[j] != LLDB_INVALID_REGNUM; ++j) { s.Printf("%s ", m_regs[m_regs[i].value_regs[j]].name); } s.Printf("]"); } if (m_regs[i].invalidate_regs) { s.Printf(", invalidate_regs = [ "); for (size_t j = 0; m_regs[i].invalidate_regs[j] != LLDB_INVALID_REGNUM; ++j) { s.Printf("%s ", m_regs[m_regs[i].invalidate_regs[j]].name); } s.Printf("]"); } s.EOL(); } const size_t num_sets = m_sets.size(); s.Printf("%p: DynamicRegisterInfo contains %" PRIu64 " register sets:\n", static_cast(this), static_cast(num_sets)); for (size_t i = 0; i < num_sets; ++i) { s.Printf("set[%" PRIu64 "] name = %s, regs = [", (uint64_t)i, m_sets[i].name); for (size_t idx = 0; idx < m_sets[i].num_registers; ++idx) { s.Printf("%s ", m_regs[m_sets[i].registers[idx]].name); } s.Printf("]\n"); } } lldb_private::RegisterInfo *DynamicRegisterInfo::GetRegisterInfo( const lldb_private::ConstString ®_name) { for (auto ®_info : m_regs) { // We can use pointer comparison since we used a ConstString to set // the "name" member in AddRegister() if (reg_info.name == reg_name.GetCString()) { return ®_info; } } return NULL; } Index: vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLLDB.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLLDB.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/Process/Utility/RegisterContextLLDB.cpp (revision 311542) @@ -1,2099 +1,2095 @@ //===-- RegisterContextLLDB.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/Address.h" #include "lldb/Core/AddressRange.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Value.h" #include "lldb/Expression/DWARFExpression.h" #include "lldb/Symbol/ArmUnwindInfo.h" #include "lldb/Symbol/DWARFCallFrameInfo.h" #include "lldb/Symbol/FuncUnwinders.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/lldb-private.h" #include "RegisterContextLLDB.h" using namespace lldb; using namespace lldb_private; static ConstString GetSymbolOrFunctionName(const SymbolContext &sym_ctx) { if (sym_ctx.symbol) return sym_ctx.symbol->GetName(); else if (sym_ctx.function) return sym_ctx.function->GetName(); return ConstString(); } RegisterContextLLDB::RegisterContextLLDB(Thread &thread, const SharedPtr &next_frame, SymbolContext &sym_ctx, uint32_t frame_number, UnwindLLDB &unwind_lldb) : RegisterContext(thread, frame_number), m_thread(thread), m_fast_unwind_plan_sp(), m_full_unwind_plan_sp(), m_fallback_unwind_plan_sp(), m_all_registers_available(false), m_frame_type(-1), m_cfa(LLDB_INVALID_ADDRESS), m_start_pc(), m_current_pc(), m_current_offset(0), m_current_offset_backed_up_one(0), m_sym_ctx(sym_ctx), m_sym_ctx_valid(false), m_frame_number(frame_number), m_registers(), m_parent_unwind(unwind_lldb) { m_sym_ctx.Clear(false); m_sym_ctx_valid = false; if (IsFrameZero()) { InitializeZerothFrame(); } else { InitializeNonZerothFrame(); } // This same code exists over in the GetFullUnwindPlanForFrame() but it may // not have been executed yet if (IsFrameZero() || next_frame->m_frame_type == eTrapHandlerFrame || next_frame->m_frame_type == eDebuggerFrame) { m_all_registers_available = true; } } bool RegisterContextLLDB::IsUnwindPlanValidForCurrentPC( lldb::UnwindPlanSP unwind_plan_sp, int &valid_pc_offset) { if (!unwind_plan_sp) return false; // check if m_current_pc is valid if (unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { // yes - current offset can be used as is valid_pc_offset = m_current_offset; return true; } // if m_current_offset <= 0, we've got nothing else to try if (m_current_offset <= 0) return false; // check pc - 1 to see if it's valid Address pc_minus_one(m_current_pc); pc_minus_one.SetOffset(m_current_pc.GetOffset() - 1); if (unwind_plan_sp->PlanValidAtAddress(pc_minus_one)) { // *valid_pc_offset = m_current_offset - 1; valid_pc_offset = m_current_pc.GetOffset() - 1; return true; } return false; } // Initialize a RegisterContextLLDB which is the first frame of a stack -- the // zeroth frame or currently // executing frame. void RegisterContextLLDB::InitializeZerothFrame() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); ExecutionContext exe_ctx(m_thread.shared_from_this()); RegisterContextSP reg_ctx_sp = m_thread.GetRegisterContext(); if (reg_ctx_sp.get() == NULL) { m_frame_type = eNotAValidFrame; UnwindLogMsg("frame does not have a register context"); return; } addr_t current_pc = reg_ctx_sp->GetPC(); if (current_pc == LLDB_INVALID_ADDRESS) { m_frame_type = eNotAValidFrame; UnwindLogMsg("frame does not have a pc"); return; } Process *process = exe_ctx.GetProcessPtr(); // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs // this will strip bit zero in case we read a PC from memory or from the LR. // (which would be a no-op in frame 0 where we get it from the register set, // but still a good idea to make the call here for other ABIs that may exist.) ABI *abi = process->GetABI().get(); if (abi) current_pc = abi->FixCodeAddress(current_pc); // Initialize m_current_pc, an Address object, based on current_pc, an addr_t. m_current_pc.SetLoadAddress(current_pc, &process->GetTarget()); // If we don't have a Module for some reason, we're not going to find // symbol/function information - just // stick in some reasonable defaults and hope we can unwind past this frame. ModuleSP pc_module_sp(m_current_pc.GetModule()); if (!m_current_pc.IsValid() || !pc_module_sp) { UnwindLogMsg("using architectural default unwind method"); } // We require either a symbol or function in the symbols context to be // successfully // filled in or this context is of no use to us. const uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; if (pc_module_sp.get() && (pc_module_sp->ResolveSymbolContextForAddress( m_current_pc, resolve_scope, m_sym_ctx) & resolve_scope)) { m_sym_ctx_valid = true; } if (m_sym_ctx.symbol) { UnwindLogMsg("with pc value of 0x%" PRIx64 ", symbol name is '%s'", current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } else if (m_sym_ctx.function) { UnwindLogMsg("with pc value of 0x%" PRIx64 ", function name is '%s'", current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } else { UnwindLogMsg("with pc value of 0x%" PRIx64 ", no symbol/function name is known.", current_pc); } AddressRange addr_range; m_sym_ctx.GetAddressRange(resolve_scope, 0, false, addr_range); if (IsTrapHandlerSymbol(process, m_sym_ctx)) { m_frame_type = eTrapHandlerFrame; } else { // FIXME: Detect eDebuggerFrame here. m_frame_type = eNormalFrame; } // If we were able to find a symbol/function, set addr_range to the bounds of // that symbol/function. // else treat the current pc value as the start_pc and record no offset. if (addr_range.GetBaseAddress().IsValid()) { m_start_pc = addr_range.GetBaseAddress(); if (m_current_pc.GetSection() == m_start_pc.GetSection()) { m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset(); } else if (m_current_pc.GetModule() == m_start_pc.GetModule()) { // This means that whatever symbol we kicked up isn't really correct // --- we should not cross section boundaries ... We really should NULL // out // the function/symbol in this case unless there is a bad assumption // here due to inlined functions? m_current_offset = m_current_pc.GetFileAddress() - m_start_pc.GetFileAddress(); } m_current_offset_backed_up_one = m_current_offset; } else { m_start_pc = m_current_pc; m_current_offset = -1; m_current_offset_backed_up_one = -1; } // We've set m_frame_type and m_sym_ctx before these calls. m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame(); m_full_unwind_plan_sp = GetFullUnwindPlanForFrame(); UnwindPlan::RowSP active_row; lldb::RegisterKind row_register_kind = eRegisterKindGeneric; if (m_full_unwind_plan_sp && m_full_unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); if (active_row.get() && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr())); UnwindLogMsg("%s", active_row_strm.GetData()); } } if (!active_row.get()) { UnwindLogMsg("could not find an unwindplan row for this frame's pc"); m_frame_type = eNotAValidFrame; return; } if (!ReadCFAValueForRow(row_register_kind, active_row, m_cfa)) { // Try the fall back unwind plan since the // full unwind plan failed. FuncUnwindersSP func_unwinders_sp; UnwindPlanSP call_site_unwind_plan; bool cfa_status = false; if (m_sym_ctx_valid) { func_unwinders_sp = pc_module_sp->GetObjectFile() ->GetUnwindTable() .GetFuncUnwindersContainingAddress(m_current_pc, m_sym_ctx); } if (func_unwinders_sp.get() != nullptr) call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite( process->GetTarget(), m_current_offset_backed_up_one); if (call_site_unwind_plan.get() != nullptr) { m_fallback_unwind_plan_sp = call_site_unwind_plan; if (TryFallbackUnwindPlan()) cfa_status = true; } if (!cfa_status) { UnwindLogMsg("could not read CFA value for first frame."); m_frame_type = eNotAValidFrame; return; } } UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64 " using %s UnwindPlan", (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()), (uint64_t)m_cfa, m_full_unwind_plan_sp->GetSourceName().GetCString()); } // Initialize a RegisterContextLLDB for the non-zeroth frame -- rely on the // RegisterContextLLDB "below" it // to provide things like its current pc value. void RegisterContextLLDB::InitializeNonZerothFrame() { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (IsFrameZero()) { m_frame_type = eNotAValidFrame; UnwindLogMsg("non-zeroth frame tests positive for IsFrameZero -- that " "shouldn't happen."); return; } if (!GetNextFrame().get() || !GetNextFrame()->IsValid()) { m_frame_type = eNotAValidFrame; UnwindLogMsg("Could not get next frame, marking this frame as invalid."); return; } if (!m_thread.GetRegisterContext()) { m_frame_type = eNotAValidFrame; UnwindLogMsg("Could not get register context for this thread, marking this " "frame as invalid."); return; } addr_t pc; if (!ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) { UnwindLogMsg("could not get pc value"); m_frame_type = eNotAValidFrame; return; } if (log) { UnwindLogMsg("pc = 0x%" PRIx64, pc); addr_t reg_val; if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, reg_val)) UnwindLogMsg("fp = 0x%" PRIx64, reg_val); if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, reg_val)) UnwindLogMsg("sp = 0x%" PRIx64, reg_val); } // A pc of 0x0 means it's the end of the stack crawl unless we're above a trap // handler function bool above_trap_handler = false; if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsTrapHandlerFrame()) above_trap_handler = true; if (pc == 0 || pc == 0x1) { if (above_trap_handler == false) { m_frame_type = eNotAValidFrame; UnwindLogMsg("this frame has a pc of 0x0"); return; } } ExecutionContext exe_ctx(m_thread.shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs // this will strip bit zero in case we read a PC from memory or from the LR. ABI *abi = process->GetABI().get(); if (abi) pc = abi->FixCodeAddress(pc); m_current_pc.SetLoadAddress(pc, &process->GetTarget()); // If we don't have a Module for some reason, we're not going to find // symbol/function information - just // stick in some reasonable defaults and hope we can unwind past this frame. ModuleSP pc_module_sp(m_current_pc.GetModule()); if (!m_current_pc.IsValid() || !pc_module_sp) { UnwindLogMsg("using architectural default unwind method"); // Test the pc value to see if we know it's in an unmapped/non-executable // region of memory. uint32_t permissions; if (process->GetLoadAddressPermissions(pc, permissions) && (permissions & ePermissionsExecutable) == 0) { // If this is the second frame off the stack, we may have unwound the // first frame // incorrectly. But using the architecture default unwind plan may get us // back on // track -- albeit possibly skipping a real frame. Give this frame a // clearly-invalid // pc and see if we can get any further. if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsFrameZero()) { UnwindLogMsg("had a pc of 0x%" PRIx64 " which is not in executable " "memory but on frame 1 -- " "allowing it once.", (uint64_t)pc); m_frame_type = eSkipFrame; } else { // anywhere other than the second frame, a non-executable pc means we're // off in the weeds -- stop now. m_frame_type = eNotAValidFrame; UnwindLogMsg("pc is in a non-executable section of memory and this " "isn't the 2nd frame in the stack walk."); return; } } if (abi) { m_fast_unwind_plan_sp.reset(); m_full_unwind_plan_sp.reset(new UnwindPlan(lldb::eRegisterKindGeneric)); abi->CreateDefaultUnwindPlan(*m_full_unwind_plan_sp); if (m_frame_type != eSkipFrame) // don't override eSkipFrame { m_frame_type = eNormalFrame; } m_all_registers_available = false; m_current_offset = -1; m_current_offset_backed_up_one = -1; RegisterKind row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); UnwindPlan::RowSP row = m_full_unwind_plan_sp->GetRowForFunctionOffset(0); if (row.get()) { if (!ReadCFAValueForRow(row_register_kind, row, m_cfa)) { UnwindLogMsg("failed to get cfa value"); if (m_frame_type != eSkipFrame) // don't override eSkipFrame { m_frame_type = eNotAValidFrame; } return; } // A couple of sanity checks.. if (m_cfa == LLDB_INVALID_ADDRESS || m_cfa == 0 || m_cfa == 1) { UnwindLogMsg("could not find a valid cfa address"); m_frame_type = eNotAValidFrame; return; } // m_cfa should point into the stack memory; if we can query memory // region permissions, // see if the memory is allocated & readable. if (process->GetLoadAddressPermissions(m_cfa, permissions) && (permissions & ePermissionsReadable) == 0) { m_frame_type = eNotAValidFrame; UnwindLogMsg( "the CFA points to a region of memory that is not readable"); return; } } else { UnwindLogMsg("could not find a row for function offset zero"); m_frame_type = eNotAValidFrame; return; } if (CheckIfLoopingStack()) { TryFallbackUnwindPlan(); if (CheckIfLoopingStack()) { UnwindLogMsg("same CFA address as next frame, assuming the unwind is " "looping - stopping"); m_frame_type = eNotAValidFrame; return; } } UnwindLogMsg("initialized frame cfa is 0x%" PRIx64, (uint64_t)m_cfa); return; } m_frame_type = eNotAValidFrame; UnwindLogMsg("could not find any symbol for this pc, or a default unwind " "plan, to continue unwind."); return; } bool resolve_tail_call_address = false; // m_current_pc can be one past the // address range of the function... // If the saved pc does not point to a function/symbol because it is // beyond the bounds of the correct function and there's no symbol there, // we do *not* want ResolveSymbolContextForAddress to back up the pc by 1, // because then we might not find the correct unwind information later. // Instead, let ResolveSymbolContextForAddress fail, and handle the case // via decr_pc_and_recompute_addr_range below. const uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; uint32_t resolved_scope = pc_module_sp->ResolveSymbolContextForAddress( m_current_pc, resolve_scope, m_sym_ctx, resolve_tail_call_address); // We require either a symbol or function in the symbols context to be // successfully // filled in or this context is of no use to us. if (resolve_scope & resolved_scope) { m_sym_ctx_valid = true; } if (m_sym_ctx.symbol) { UnwindLogMsg("with pc value of 0x%" PRIx64 ", symbol name is '%s'", pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } else if (m_sym_ctx.function) { UnwindLogMsg("with pc value of 0x%" PRIx64 ", function name is '%s'", pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } else { UnwindLogMsg("with pc value of 0x%" PRIx64 ", no symbol/function name is known.", pc); } AddressRange addr_range; if (!m_sym_ctx.GetAddressRange(resolve_scope, 0, false, addr_range)) { m_sym_ctx_valid = false; } bool decr_pc_and_recompute_addr_range = false; // If the symbol lookup failed... if (m_sym_ctx_valid == false) decr_pc_and_recompute_addr_range = true; // Or if we're in the middle of the stack (and not "above" an asynchronous // event like sigtramp), // and our "current" pc is the start of a function... if (m_sym_ctx_valid && GetNextFrame()->m_frame_type != eTrapHandlerFrame && GetNextFrame()->m_frame_type != eDebuggerFrame && addr_range.GetBaseAddress().IsValid() && addr_range.GetBaseAddress().GetSection() == m_current_pc.GetSection() && addr_range.GetBaseAddress().GetOffset() == m_current_pc.GetOffset()) { decr_pc_and_recompute_addr_range = true; } // We need to back up the pc by 1 byte and re-search for the Symbol to handle // the case where the "saved pc" // value is pointing to the next function, e.g. if a function ends with a CALL // instruction. // FIXME this may need to be an architectural-dependent behavior; if so we'll // need to add a member function // to the ABI plugin and consult that. if (decr_pc_and_recompute_addr_range) { UnwindLogMsg("Backing up the pc value of 0x%" PRIx64 " by 1 and re-doing symbol lookup; old symbol was %s", pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); Address temporary_pc; temporary_pc.SetLoadAddress(pc - 1, &process->GetTarget()); m_sym_ctx.Clear(false); m_sym_ctx_valid = false; uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; ModuleSP temporary_module_sp = temporary_pc.GetModule(); if (temporary_module_sp && temporary_module_sp->ResolveSymbolContextForAddress( temporary_pc, resolve_scope, m_sym_ctx) & resolve_scope) { if (m_sym_ctx.GetAddressRange(resolve_scope, 0, false, addr_range)) m_sym_ctx_valid = true; } UnwindLogMsg("Symbol is now %s", GetSymbolOrFunctionName(m_sym_ctx).AsCString("")); } // If we were able to find a symbol/function, set addr_range_ptr to the bounds // of that symbol/function. // else treat the current pc value as the start_pc and record no offset. if (addr_range.GetBaseAddress().IsValid()) { m_start_pc = addr_range.GetBaseAddress(); m_current_offset = pc - m_start_pc.GetLoadAddress(&process->GetTarget()); m_current_offset_backed_up_one = m_current_offset; if (decr_pc_and_recompute_addr_range && m_current_offset_backed_up_one > 0) { m_current_offset_backed_up_one--; if (m_sym_ctx_valid) { m_current_pc.SetLoadAddress(pc - 1, &process->GetTarget()); } } } else { m_start_pc = m_current_pc; m_current_offset = -1; m_current_offset_backed_up_one = -1; } if (IsTrapHandlerSymbol(process, m_sym_ctx)) { m_frame_type = eTrapHandlerFrame; } else { // FIXME: Detect eDebuggerFrame here. if (m_frame_type != eSkipFrame) // don't override eSkipFrame { m_frame_type = eNormalFrame; } } // We've set m_frame_type and m_sym_ctx before this call. m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame(); UnwindPlan::RowSP active_row; RegisterKind row_register_kind = eRegisterKindGeneric; // Try to get by with just the fast UnwindPlan if possible - the full // UnwindPlan may be expensive to get // (e.g. if we have to parse the entire eh_frame section of an ObjectFile for // the first time.) if (m_fast_unwind_plan_sp && m_fast_unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); row_register_kind = m_fast_unwind_plan_sp->GetRegisterKind(); if (active_row.get() && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_fast_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr())); UnwindLogMsg("active row: %s", active_row_strm.GetData()); } } else { m_full_unwind_plan_sp = GetFullUnwindPlanForFrame(); int valid_offset = -1; if (IsUnwindPlanValidForCurrentPC(m_full_unwind_plan_sp, valid_offset)) { active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(valid_offset); row_register_kind = m_full_unwind_plan_sp->GetRegisterKind(); if (active_row.get() && log) { StreamString active_row_strm; active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr())); UnwindLogMsg("active row: %s", active_row_strm.GetData()); } } } if (!active_row.get()) { m_frame_type = eNotAValidFrame; UnwindLogMsg("could not find unwind row for this pc"); return; } if (!ReadCFAValueForRow(row_register_kind, active_row, m_cfa)) { UnwindLogMsg("failed to get cfa"); m_frame_type = eNotAValidFrame; return; } UnwindLogMsg("m_cfa = 0x%" PRIx64, m_cfa); if (CheckIfLoopingStack()) { TryFallbackUnwindPlan(); if (CheckIfLoopingStack()) { UnwindLogMsg("same CFA address as next frame, assuming the unwind is " "looping - stopping"); m_frame_type = eNotAValidFrame; return; } } UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64, (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()), (uint64_t)m_cfa); } bool RegisterContextLLDB::CheckIfLoopingStack() { // If we have a bad stack setup, we can get the same CFA value multiple times // -- or even // more devious, we can actually oscillate between two CFA values. Detect that // here and // break out to avoid a possible infinite loop in lldb trying to unwind the // stack. // To detect when we have the same CFA value multiple times, we compare the // CFA of the current // frame with the 2nd next frame because in some specail case (e.g. signal // hanlders, hand // written assembly without ABI compiance) we can have 2 frames with the same // CFA (in theory we // can have arbitrary number of frames with the same CFA, but more then 2 is // very very unlikely) RegisterContextLLDB::SharedPtr next_frame = GetNextFrame(); if (next_frame) { RegisterContextLLDB::SharedPtr next_next_frame = next_frame->GetNextFrame(); addr_t next_next_frame_cfa = LLDB_INVALID_ADDRESS; if (next_next_frame && next_next_frame->GetCFA(next_next_frame_cfa)) { if (next_next_frame_cfa == m_cfa) { // We have a loop in the stack unwind return true; } } } return false; } bool RegisterContextLLDB::IsFrameZero() const { return m_frame_number == 0; } // Find a fast unwind plan for this frame, if possible. // // On entry to this method, // // 1. m_frame_type should already be set to eTrapHandlerFrame/eDebuggerFrame // if either of those are correct, // 2. m_sym_ctx should already be filled in, and // 3. m_current_pc should have the current pc value for this frame // 4. m_current_offset_backed_up_one should have the current byte offset into // the function, maybe backed up by 1, -1 if unknown UnwindPlanSP RegisterContextLLDB::GetFastUnwindPlanForFrame() { UnwindPlanSP unwind_plan_sp; ModuleSP pc_module_sp(m_current_pc.GetModule()); if (!m_current_pc.IsValid() || !pc_module_sp || pc_module_sp->GetObjectFile() == NULL) return unwind_plan_sp; if (IsFrameZero()) return unwind_plan_sp; FuncUnwindersSP func_unwinders_sp( pc_module_sp->GetObjectFile() ->GetUnwindTable() .GetFuncUnwindersContainingAddress(m_current_pc, m_sym_ctx)); if (!func_unwinders_sp) return unwind_plan_sp; // If we're in _sigtramp(), unwinding past this frame requires special // knowledge. if (m_frame_type == eTrapHandlerFrame || m_frame_type == eDebuggerFrame) return unwind_plan_sp; unwind_plan_sp = func_unwinders_sp->GetUnwindPlanFastUnwind( *m_thread.CalculateTarget(), m_thread); if (unwind_plan_sp) { if (unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (log && log->GetVerbose()) { if (m_fast_unwind_plan_sp) UnwindLogMsgVerbose("frame, and has a fast UnwindPlan"); else UnwindLogMsgVerbose("frame"); } m_frame_type = eNormalFrame; return unwind_plan_sp; } else { unwind_plan_sp.reset(); } } return unwind_plan_sp; } // On entry to this method, // // 1. m_frame_type should already be set to eTrapHandlerFrame/eDebuggerFrame // if either of those are correct, // 2. m_sym_ctx should already be filled in, and // 3. m_current_pc should have the current pc value for this frame // 4. m_current_offset_backed_up_one should have the current byte offset into // the function, maybe backed up by 1, -1 if unknown UnwindPlanSP RegisterContextLLDB::GetFullUnwindPlanForFrame() { UnwindPlanSP unwind_plan_sp; UnwindPlanSP arch_default_unwind_plan_sp; ExecutionContext exe_ctx(m_thread.shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); ABI *abi = process ? process->GetABI().get() : NULL; if (abi) { arch_default_unwind_plan_sp.reset( new UnwindPlan(lldb::eRegisterKindGeneric)); abi->CreateDefaultUnwindPlan(*arch_default_unwind_plan_sp); } else { UnwindLogMsg( "unable to get architectural default UnwindPlan from ABI plugin"); } bool behaves_like_zeroth_frame = false; if (IsFrameZero() || GetNextFrame()->m_frame_type == eTrapHandlerFrame || GetNextFrame()->m_frame_type == eDebuggerFrame) { behaves_like_zeroth_frame = true; // If this frame behaves like a 0th frame (currently executing or // interrupted asynchronously), all registers can be retrieved. m_all_registers_available = true; } // If we've done a jmp 0x0 / bl 0x0 (called through a null function pointer) // so the pc is 0x0 // in the zeroth frame, we need to use the "unwind at first instruction" arch // default UnwindPlan // Also, if this Process can report on memory region attributes, any // non-executable region means // we jumped through a bad function pointer - handle the same way as 0x0. // Note, if we have a symbol context & a symbol, we don't want to follow this // code path. This is // for jumping to memory regions without any information available. if ((!m_sym_ctx_valid || (m_sym_ctx.function == NULL && m_sym_ctx.symbol == NULL)) && behaves_like_zeroth_frame && m_current_pc.IsValid()) { uint32_t permissions; addr_t current_pc_addr = m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()); if (current_pc_addr == 0 || (process && process->GetLoadAddressPermissions(current_pc_addr, permissions) && (permissions & ePermissionsExecutable) == 0)) { if (abi) { unwind_plan_sp.reset(new UnwindPlan(lldb::eRegisterKindGeneric)); abi->CreateFunctionEntryUnwindPlan(*unwind_plan_sp); m_frame_type = eNormalFrame; return unwind_plan_sp; } } } // No Module for the current pc, try using the architecture default unwind. ModuleSP pc_module_sp(m_current_pc.GetModule()); if (!m_current_pc.IsValid() || !pc_module_sp || pc_module_sp->GetObjectFile() == NULL) { m_frame_type = eNormalFrame; return arch_default_unwind_plan_sp; } FuncUnwindersSP func_unwinders_sp; if (m_sym_ctx_valid) { func_unwinders_sp = pc_module_sp->GetObjectFile() ->GetUnwindTable() .GetFuncUnwindersContainingAddress(m_current_pc, m_sym_ctx); } // No FuncUnwinders available for this pc (stripped function symbols, lldb // could not augment its // function table with another source, like LC_FUNCTION_STARTS or eh_frame in // ObjectFileMachO). // See if eh_frame or the .ARM.exidx tables have unwind information for this // address, else fall // back to the architectural default unwind. if (!func_unwinders_sp) { m_frame_type = eNormalFrame; if (!pc_module_sp || !pc_module_sp->GetObjectFile() || !m_current_pc.IsValid()) return arch_default_unwind_plan_sp; // Even with -fomit-frame-pointer, we can try eh_frame to get back on track. DWARFCallFrameInfo *eh_frame = pc_module_sp->GetObjectFile()->GetUnwindTable().GetEHFrameInfo(); if (eh_frame) { unwind_plan_sp.reset(new UnwindPlan(lldb::eRegisterKindGeneric)); if (eh_frame->GetUnwindPlan(m_current_pc, *unwind_plan_sp)) return unwind_plan_sp; else unwind_plan_sp.reset(); } ArmUnwindInfo *arm_exidx = pc_module_sp->GetObjectFile()->GetUnwindTable().GetArmUnwindInfo(); if (arm_exidx) { unwind_plan_sp.reset(new UnwindPlan(lldb::eRegisterKindGeneric)); if (arm_exidx->GetUnwindPlan(exe_ctx.GetTargetRef(), m_current_pc, *unwind_plan_sp)) return unwind_plan_sp; else unwind_plan_sp.reset(); } return arch_default_unwind_plan_sp; } // If we're in _sigtramp(), unwinding past this frame requires special // knowledge. On Mac OS X this knowledge // is properly encoded in the eh_frame section, so prefer that if available. // On other platforms we may need to provide a platform-specific UnwindPlan // which encodes the details of // how to unwind out of sigtramp. if (m_frame_type == eTrapHandlerFrame && process) { m_fast_unwind_plan_sp.reset(); unwind_plan_sp = func_unwinders_sp->GetEHFrameUnwindPlan( process->GetTarget(), m_current_offset_backed_up_one); if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc) && unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes) { return unwind_plan_sp; } } // Ask the DynamicLoader if the eh_frame CFI should be trusted in this frame // even when it's frame zero // This comes up if we have hand-written functions in a Module and // hand-written eh_frame. The assembly // instruction inspection may fail and the eh_frame CFI were probably written // with some care to do the // right thing. It'd be nice if there was a way to ask the eh_frame directly // if it is asynchronous // (can be trusted at every instruction point) or synchronous (the normal case // - only at call sites). // But there is not. if (process && process->GetDynamicLoader() && process->GetDynamicLoader()->AlwaysRelyOnEHUnwindInfo(m_sym_ctx)) { // We must specifically call the GetEHFrameUnwindPlan() method here -- // normally we would // call GetUnwindPlanAtCallSite() -- because CallSite may return an unwind // plan sourced from // either eh_frame (that's what we intend) or compact unwind (this won't // work) unwind_plan_sp = func_unwinders_sp->GetEHFrameUnwindPlan( process->GetTarget(), m_current_offset_backed_up_one); if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because the " "DynamicLoader suggested we prefer it", unwind_plan_sp->GetSourceName().GetCString()); return unwind_plan_sp; } } // Typically the NonCallSite UnwindPlan is the unwind created by inspecting // the assembly language instructions if (behaves_like_zeroth_frame && process) { unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite( process->GetTarget(), m_thread, m_current_offset_backed_up_one); if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { if (unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) { // We probably have an UnwindPlan created by inspecting assembly // instructions. The // assembly profilers work really well with compiler-generated functions // but hand- // written assembly can be problematic. We set the eh_frame based unwind // plan as our // fallback unwind plan if instruction emulation doesn't work out even // for non call // sites if it is available and use the architecture default unwind plan // if it is // not available. The eh_frame unwind plan is more reliable even on non // call sites // then the architecture default plan and for hand written assembly code // it is often // written in a way that it valid at all location what helps in the most // common // cases when the instruction emulation fails. UnwindPlanSP call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite( process->GetTarget(), m_current_offset_backed_up_one); if (call_site_unwind_plan && call_site_unwind_plan.get() != unwind_plan_sp.get() && call_site_unwind_plan->GetSourceName() != unwind_plan_sp->GetSourceName()) { m_fallback_unwind_plan_sp = call_site_unwind_plan; } else { m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp; } } UnwindLogMsgVerbose("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString()); return unwind_plan_sp; } } // Typically this is unwind info from an eh_frame section intended for // exception handling; only valid at call sites if (process) { unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite( process->GetTarget(), m_current_offset_backed_up_one); } int valid_offset = -1; if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp, valid_offset)) { UnwindLogMsgVerbose("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString()); return unwind_plan_sp; } // We'd prefer to use an UnwindPlan intended for call sites when we're at a // call site but if we've // struck out on that, fall back to using the non-call-site assembly // inspection UnwindPlan if possible. if (process) { unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite( process->GetTarget(), m_thread, m_current_offset_backed_up_one); } if (unwind_plan_sp && unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) { // We probably have an UnwindPlan created by inspecting assembly // instructions. The assembly // profilers work really well with compiler-generated functions but hand- // written assembly // can be problematic. We set the eh_frame based unwind plan as our fallback // unwind plan if // instruction emulation doesn't work out even for non call sites if it is // available and use // the architecture default unwind plan if it is not available. The eh_frame // unwind plan is // more reliable even on non call sites then the architecture default plan // and for hand // written assembly code it is often written in a way that it valid at all // location what // helps in the most common cases when the instruction emulation fails. UnwindPlanSP call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite( process->GetTarget(), m_current_offset_backed_up_one); if (call_site_unwind_plan && call_site_unwind_plan.get() != unwind_plan_sp.get() && call_site_unwind_plan->GetSourceName() != unwind_plan_sp->GetSourceName()) { m_fallback_unwind_plan_sp = call_site_unwind_plan; } else { m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp; } } if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp, valid_offset)) { UnwindLogMsgVerbose("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString()); return unwind_plan_sp; } // If we're on the first instruction of a function, and we have an // architectural default UnwindPlan // for the initial instruction of a function, use that. if (m_current_offset_backed_up_one == 0) { unwind_plan_sp = func_unwinders_sp->GetUnwindPlanArchitectureDefaultAtFunctionEntry( m_thread); if (unwind_plan_sp) { UnwindLogMsgVerbose("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString()); return unwind_plan_sp; } } // If nothing else, use the architectural default UnwindPlan and hope that // does the job. if (arch_default_unwind_plan_sp) UnwindLogMsgVerbose( "frame uses %s for full UnwindPlan", arch_default_unwind_plan_sp->GetSourceName().GetCString()); else UnwindLogMsg( "Unable to find any UnwindPlan for full unwind of this frame."); return arch_default_unwind_plan_sp; } void RegisterContextLLDB::InvalidateAllRegisters() { m_frame_type = eNotAValidFrame; } size_t RegisterContextLLDB::GetRegisterCount() { return m_thread.GetRegisterContext()->GetRegisterCount(); } const RegisterInfo *RegisterContextLLDB::GetRegisterInfoAtIndex(size_t reg) { return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg); } size_t RegisterContextLLDB::GetRegisterSetCount() { return m_thread.GetRegisterContext()->GetRegisterSetCount(); } const RegisterSet *RegisterContextLLDB::GetRegisterSet(size_t reg_set) { return m_thread.GetRegisterContext()->GetRegisterSet(reg_set); } uint32_t RegisterContextLLDB::ConvertRegisterKindToRegisterNumber( lldb::RegisterKind kind, uint32_t num) { return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber( kind, num); } bool RegisterContextLLDB::ReadRegisterValueFromRegisterLocation( lldb_private::UnwindLLDB::RegisterLocation regloc, const RegisterInfo *reg_info, RegisterValue &value) { if (!IsValid()) return false; bool success = false; switch (regloc.type) { case UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext: { const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); if (!other_reg_info) return false; success = m_thread.GetRegisterContext()->ReadRegister(other_reg_info, value); } break; case UnwindLLDB::RegisterLocation::eRegisterInRegister: { const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); if (!other_reg_info) return false; if (IsFrameZero()) { success = m_thread.GetRegisterContext()->ReadRegister(other_reg_info, value); } else { success = GetNextFrame()->ReadRegister(other_reg_info, value); } } break; case UnwindLLDB::RegisterLocation::eRegisterValueInferred: success = value.SetUInt(regloc.location.inferred_value, reg_info->byte_size); break; case UnwindLLDB::RegisterLocation::eRegisterNotSaved: break; case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation: - assert("FIXME debugger inferior function call unwind"); - break; + llvm_unreachable("FIXME debugger inferior function call unwind"); case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation: { Error error(ReadRegisterValueFromMemory( reg_info, regloc.location.target_memory_location, reg_info->byte_size, value)); success = error.Success(); } break; default: - assert("Unknown RegisterLocation type."); - break; + llvm_unreachable("Unknown RegisterLocation type."); } return success; } bool RegisterContextLLDB::WriteRegisterValueToRegisterLocation( lldb_private::UnwindLLDB::RegisterLocation regloc, const RegisterInfo *reg_info, const RegisterValue &value) { if (!IsValid()) return false; bool success = false; switch (regloc.type) { case UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext: { const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); success = m_thread.GetRegisterContext()->WriteRegister(other_reg_info, value); } break; case UnwindLLDB::RegisterLocation::eRegisterInRegister: { const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); if (IsFrameZero()) { success = m_thread.GetRegisterContext()->WriteRegister(other_reg_info, value); } else { success = GetNextFrame()->WriteRegister(other_reg_info, value); } } break; case UnwindLLDB::RegisterLocation::eRegisterValueInferred: case UnwindLLDB::RegisterLocation::eRegisterNotSaved: break; case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation: - assert("FIXME debugger inferior function call unwind"); - break; + llvm_unreachable("FIXME debugger inferior function call unwind"); case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation: { Error error(WriteRegisterValueToMemory( reg_info, regloc.location.target_memory_location, reg_info->byte_size, value)); success = error.Success(); } break; default: - assert("Unknown RegisterLocation type."); - break; + llvm_unreachable("Unknown RegisterLocation type."); } return success; } bool RegisterContextLLDB::IsValid() const { return m_frame_type != eNotAValidFrame; } // After the final stack frame in a stack walk we'll get one invalid // (eNotAValidFrame) stack frame -- // one past the end of the stack walk. But higher-level code will need to tell // the differnece between // "the unwind plan below this frame failed" versus "we successfully completed // the stack walk" so // this method helps to disambiguate that. bool RegisterContextLLDB::IsTrapHandlerFrame() const { return m_frame_type == eTrapHandlerFrame; } // A skip frame is a bogus frame on the stack -- but one where we're likely to // find a real frame farther // up the stack if we keep looking. It's always the second frame in an unwind // (i.e. the first frame after // frame zero) where unwinding can be the trickiest. Ideally we'll mark up this // frame in some way so the // user knows we're displaying bad data and we may have skipped one frame of // their real program in the // process of getting back on track. bool RegisterContextLLDB::IsSkipFrame() const { return m_frame_type == eSkipFrame; } bool RegisterContextLLDB::IsTrapHandlerSymbol( lldb_private::Process *process, const lldb_private::SymbolContext &m_sym_ctx) const { PlatformSP platform_sp(process->GetTarget().GetPlatform()); if (platform_sp) { const std::vector trap_handler_names( platform_sp->GetTrapHandlerSymbolNames()); for (ConstString name : trap_handler_names) { if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) || (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name)) { return true; } } } const std::vector user_specified_trap_handler_names( m_parent_unwind.GetUserSpecifiedTrapHandlerFunctionNames()); for (ConstString name : user_specified_trap_handler_names) { if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) || (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name)) { return true; } } return false; } // Answer the question: Where did THIS frame save the CALLER frame ("previous" // frame)'s register value? enum UnwindLLDB::RegisterSearchResult RegisterContextLLDB::SavedLocationForRegister( uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc) { RegisterNumber regnum(m_thread, eRegisterKindLLDB, lldb_regnum); // Have we already found this register location? if (!m_registers.empty()) { std::map::const_iterator iterator; iterator = m_registers.find(regnum.GetAsKind(eRegisterKindLLDB)); if (iterator != m_registers.end()) { regloc = iterator->second; UnwindLogMsg("supplying caller's saved %s (%d)'s location, cached", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } } // Look through the available UnwindPlans for the register location. UnwindPlan::Row::RegisterLocation unwindplan_regloc; bool have_unwindplan_regloc = false; RegisterKind unwindplan_registerkind = kNumRegisterKinds; if (m_fast_unwind_plan_sp) { UnwindPlan::RowSP active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); unwindplan_registerkind = m_fast_unwind_plan_sp->GetRegisterKind(); if (regnum.GetAsKind(unwindplan_registerkind) == LLDB_INVALID_REGNUM) { UnwindLogMsg("could not convert lldb regnum %s (%d) into %d RegisterKind " "reg numbering scheme", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), (int)unwindplan_registerkind); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } if (active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { UnwindLogMsg( "supplying caller's saved %s (%d)'s location using FastUnwindPlan", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); have_unwindplan_regloc = true; } } if (!have_unwindplan_regloc) { // m_full_unwind_plan_sp being NULL means that we haven't tried to find a // full UnwindPlan yet if (!m_full_unwind_plan_sp) m_full_unwind_plan_sp = GetFullUnwindPlanForFrame(); if (m_full_unwind_plan_sp) { RegisterNumber pc_regnum(m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind(); RegisterNumber return_address_reg; // If we're fetching the saved pc and this UnwindPlan defines a // ReturnAddress register (e.g. lr on arm), // look for the return address register number in the UnwindPlan's row. if (pc_regnum.IsValid() && pc_regnum == regnum && m_full_unwind_plan_sp->GetReturnAddressRegister() != LLDB_INVALID_REGNUM) { return_address_reg.init( m_thread, m_full_unwind_plan_sp->GetRegisterKind(), m_full_unwind_plan_sp->GetReturnAddressRegister()); regnum = return_address_reg; UnwindLogMsg("requested caller's saved PC but this UnwindPlan uses a " "RA reg; getting %s (%d) instead", return_address_reg.GetName(), return_address_reg.GetAsKind(eRegisterKindLLDB)); } else { if (regnum.GetAsKind(unwindplan_registerkind) == LLDB_INVALID_REGNUM) { if (unwindplan_registerkind == eRegisterKindGeneric) { UnwindLogMsg("could not convert lldb regnum %s (%d) into " "eRegisterKindGeneric reg numbering scheme", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); } else { UnwindLogMsg("could not convert lldb regnum %s (%d) into %d " "RegisterKind reg numbering scheme", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), (int)unwindplan_registerkind); } return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } } if (regnum.IsValid() && active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { have_unwindplan_regloc = true; UnwindLogMsg( "supplying caller's saved %s (%d)'s location using %s UnwindPlan", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), m_full_unwind_plan_sp->GetSourceName().GetCString()); } // This is frame 0 and we're retrieving the PC and it's saved in a Return // Address register and // it hasn't been saved anywhere yet -- that is, it's still live in the // actual register. // Handle this specially. if (have_unwindplan_regloc == false && return_address_reg.IsValid() && IsFrameZero()) { if (return_address_reg.GetAsKind(eRegisterKindLLDB) != LLDB_INVALID_REGNUM) { lldb_private::UnwindLLDB::RegisterLocation new_regloc; new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext; new_regloc.location.register_number = return_address_reg.GetAsKind(eRegisterKindLLDB); m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc; regloc = new_regloc; UnwindLogMsg("supplying caller's register %s (%d) from the live " "RegisterContext at frame 0, saved in %d", return_address_reg.GetName(), return_address_reg.GetAsKind(eRegisterKindLLDB), return_address_reg.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } } // If this architecture stores the return address in a register (it // defines a Return Address register) // and we're on a non-zero stack frame and the Full UnwindPlan says that // the pc is stored in the // RA registers (e.g. lr on arm), then we know that the full unwindplan is // not trustworthy -- this // is an impossible situation and the instruction emulation code has // likely been misled. // If this stack frame meets those criteria, we need to throw away the // Full UnwindPlan that the // instruction emulation came up with and fall back to the architecture's // Default UnwindPlan so // the stack walk can get past this point. // Special note: If the Full UnwindPlan was generated from the compiler, // don't second-guess it // when we're at a call site location. // arch_default_ra_regnum is the return address register # in the Full // UnwindPlan register numbering RegisterNumber arch_default_ra_regnum(m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); if (arch_default_ra_regnum.GetAsKind(unwindplan_registerkind) != LLDB_INVALID_REGNUM && pc_regnum == regnum && unwindplan_regloc.IsInOtherRegister() && unwindplan_regloc.GetRegisterNumber() == arch_default_ra_regnum.GetAsKind(unwindplan_registerkind) && m_full_unwind_plan_sp->GetSourcedFromCompiler() != eLazyBoolYes && !m_all_registers_available) { UnwindLogMsg("%s UnwindPlan tried to restore the pc from the link " "register but this is a non-zero frame", m_full_unwind_plan_sp->GetSourceName().GetCString()); // Throw away the full unwindplan; install the arch default unwindplan if (ForceSwitchToFallbackUnwindPlan()) { // Update for the possibly new unwind plan unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind(); UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); // Sanity check: Verify that we can fetch a pc value and CFA value // with this unwind plan RegisterNumber arch_default_pc_reg(m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); bool can_fetch_pc_value = false; bool can_fetch_cfa = false; addr_t cfa_value; if (active_row) { if (arch_default_pc_reg.GetAsKind(unwindplan_registerkind) != LLDB_INVALID_REGNUM && active_row->GetRegisterInfo( arch_default_pc_reg.GetAsKind(unwindplan_registerkind), unwindplan_regloc)) { can_fetch_pc_value = true; } if (ReadCFAValueForRow(unwindplan_registerkind, active_row, cfa_value)) { can_fetch_cfa = true; } } if (can_fetch_pc_value && can_fetch_cfa) { have_unwindplan_regloc = true; } else { have_unwindplan_regloc = false; } } else { // We were unable to fall back to another unwind plan have_unwindplan_regloc = false; } } } } ExecutionContext exe_ctx(m_thread.shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); if (have_unwindplan_regloc == false) { // If the UnwindPlan failed to give us an unwind location for this register, // we may be able to fall back // to some ABI-defined default. For example, some ABIs allow to determine // the caller's SP via the CFA. // Also, the ABI may set volatile registers to the undefined state. ABI *abi = process ? process->GetABI().get() : NULL; if (abi) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(regnum.GetAsKind(eRegisterKindLLDB)); if (reg_info && abi->GetFallbackRegisterLocation(reg_info, unwindplan_regloc)) { UnwindLogMsg( "supplying caller's saved %s (%d)'s location using ABI default", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); have_unwindplan_regloc = true; } } } if (have_unwindplan_regloc == false) { if (IsFrameZero()) { // This is frame 0 - we should return the actual live register context // value lldb_private::UnwindLLDB::RegisterLocation new_regloc; new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext; new_regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB); m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc; regloc = new_regloc; UnwindLogMsg("supplying caller's register %s (%d) from the live " "RegisterContext at frame 0", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } else { std::string unwindplan_name(""); if (m_full_unwind_plan_sp) { unwindplan_name += "via '"; unwindplan_name += m_full_unwind_plan_sp->GetSourceName().AsCString(); unwindplan_name += "'"; } UnwindLogMsg("no save location for %s (%d) %s", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), unwindplan_name.c_str()); } return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } // unwindplan_regloc has valid contents about where to retrieve the register if (unwindplan_regloc.IsUnspecified()) { lldb_private::UnwindLLDB::RegisterLocation new_regloc; new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterNotSaved; m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc; UnwindLogMsg("save location for %s (%d) is unspecified, continue searching", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } if (unwindplan_regloc.IsUndefined()) { UnwindLogMsg( "did not supply reg location for %s (%d) because it is volatile", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile; } if (unwindplan_regloc.IsSame()) { if (IsFrameZero() == false && (regnum.GetAsKind(eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_PC || regnum.GetAsKind(eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_RA)) { UnwindLogMsg("register %s (%d) is marked as 'IsSame' - it is a pc or " "return address reg on a non-zero frame -- treat as if we " "have no information", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } else { regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister; regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB); m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; UnwindLogMsg( "supplying caller's register %s (%d), saved in register %s (%d)", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } } if (unwindplan_regloc.IsCFAPlusOffset()) { int offset = unwindplan_regloc.GetOffset(); regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred; regloc.location.inferred_value = m_cfa + offset; m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; UnwindLogMsg("supplying caller's register %s (%d), value is CFA plus " "offset %d [value is 0x%" PRIx64 "]", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset, regloc.location.inferred_value); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } if (unwindplan_regloc.IsAtCFAPlusOffset()) { int offset = unwindplan_regloc.GetOffset(); regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation; regloc.location.target_memory_location = m_cfa + offset; m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; UnwindLogMsg("supplying caller's register %s (%d) from the stack, saved at " "CFA plus offset %d [saved at 0x%" PRIx64 "]", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset, regloc.location.target_memory_location); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } if (unwindplan_regloc.IsInOtherRegister()) { uint32_t unwindplan_regnum = unwindplan_regloc.GetRegisterNumber(); RegisterNumber row_regnum(m_thread, unwindplan_registerkind, unwindplan_regnum); if (row_regnum.GetAsKind(eRegisterKindLLDB) == LLDB_INVALID_REGNUM) { UnwindLogMsg("could not supply caller's %s (%d) location - was saved in " "another reg but couldn't convert that regnum", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister; regloc.location.register_number = row_regnum.GetAsKind(eRegisterKindLLDB); m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; UnwindLogMsg( "supplying caller's register %s (%d), saved in register %s (%d)", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), row_regnum.GetName(), row_regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } if (unwindplan_regloc.IsDWARFExpression() || unwindplan_regloc.IsAtDWARFExpression()) { DataExtractor dwarfdata(unwindplan_regloc.GetDWARFExpressionBytes(), unwindplan_regloc.GetDWARFExpressionLength(), process->GetByteOrder(), process->GetAddressByteSize()); ModuleSP opcode_ctx; DWARFExpression dwarfexpr(opcode_ctx, dwarfdata, nullptr, 0, unwindplan_regloc.GetDWARFExpressionLength()); dwarfexpr.SetRegisterKind(unwindplan_registerkind); Value result; Error error; if (dwarfexpr.Evaluate(&exe_ctx, nullptr, nullptr, this, 0, nullptr, nullptr, result, &error)) { addr_t val; val = result.GetScalar().ULongLong(); if (unwindplan_regloc.IsDWARFExpression()) { regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred; regloc.location.inferred_value = val; m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; UnwindLogMsg("supplying caller's register %s (%d) via DWARF expression " "(IsDWARFExpression)", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } else { regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation; regloc.location.target_memory_location = val; m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; UnwindLogMsg("supplying caller's register %s (%d) via DWARF expression " "(IsAtDWARFExpression)", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } } UnwindLogMsg("tried to use IsDWARFExpression or IsAtDWARFExpression for %s " "(%d) but failed", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } UnwindLogMsg("no save location for %s (%d) in this stack frame", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); // FIXME UnwindPlan::Row types atDWARFExpression and isDWARFExpression are // unsupported. return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } // TryFallbackUnwindPlan() -- this method is a little tricky. // // When this is called, the frame above -- the caller frame, the "previous" // frame -- // is invalid or bad. // // Instead of stopping the stack walk here, we'll try a different UnwindPlan and // see // if we can get a valid frame above us. // // This most often happens when an unwind plan based on assembly instruction // inspection // is not correct -- mostly with hand-written assembly functions or functions // where the // stack frame is set up "out of band", e.g. the kernel saved the register // context and // then called an asynchronous trap handler like _sigtramp. // // Often in these cases, if we just do a dumb stack walk we'll get past this // tricky // frame and our usual techniques can continue to be used. bool RegisterContextLLDB::TryFallbackUnwindPlan() { if (m_fallback_unwind_plan_sp.get() == nullptr) return false; if (m_full_unwind_plan_sp.get() == nullptr) return false; if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() || m_full_unwind_plan_sp->GetSourceName() == m_fallback_unwind_plan_sp->GetSourceName()) { return false; } // If a compiler generated unwind plan failed, trying the arch default // unwindplan // isn't going to do any better. if (m_full_unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes) return false; // Get the caller's pc value and our own CFA value. // Swap in the fallback unwind plan, re-fetch the caller's pc value and CFA // value. // If they're the same, then the fallback unwind plan provides no benefit. RegisterNumber pc_regnum(m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); addr_t old_caller_pc_value = LLDB_INVALID_ADDRESS; addr_t new_caller_pc_value = LLDB_INVALID_ADDRESS; addr_t old_this_frame_cfa_value = m_cfa; UnwindLLDB::RegisterLocation regloc; if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB), regloc) == UnwindLLDB::RegisterSearchResult::eRegisterFound) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(pc_regnum.GetAsKind(eRegisterKindLLDB)); if (reg_info) { RegisterValue reg_value; if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, reg_value)) { old_caller_pc_value = reg_value.GetAsUInt64(); } } } // This is a tricky wrinkle! If SavedLocationForRegister() detects a really // impossible // register location for the full unwind plan, it may call // ForceSwitchToFallbackUnwindPlan() // which in turn replaces the full unwindplan with the fallback... in short, // we're done, // we're using the fallback UnwindPlan. // We checked if m_fallback_unwind_plan_sp was nullptr at the top -- the only // way it // became nullptr since then is via SavedLocationForRegister(). if (m_fallback_unwind_plan_sp.get() == nullptr) return true; // Switch the full UnwindPlan to be the fallback UnwindPlan. If we decide // this isn't // working, we need to restore. // We'll also need to save & restore the value of the m_cfa ivar. Save is // down below a bit in 'old_cfa'. UnwindPlanSP original_full_unwind_plan_sp = m_full_unwind_plan_sp; addr_t old_cfa = m_cfa; m_registers.clear(); m_full_unwind_plan_sp = m_fallback_unwind_plan_sp; UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); if (active_row && active_row->GetCFAValue().GetValueType() != UnwindPlan::Row::CFAValue::unspecified) { addr_t new_cfa; if (!ReadCFAValueForRow(m_fallback_unwind_plan_sp->GetRegisterKind(), active_row, new_cfa) || new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) { UnwindLogMsg("failed to get cfa with fallback unwindplan"); m_fallback_unwind_plan_sp.reset(); m_full_unwind_plan_sp = original_full_unwind_plan_sp; m_cfa = old_cfa; return false; } m_cfa = new_cfa; if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB), regloc) == UnwindLLDB::RegisterSearchResult::eRegisterFound) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(pc_regnum.GetAsKind(eRegisterKindLLDB)); if (reg_info) { RegisterValue reg_value; if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, reg_value)) { new_caller_pc_value = reg_value.GetAsUInt64(); } } } if (new_caller_pc_value == LLDB_INVALID_ADDRESS) { UnwindLogMsg("failed to get a pc value for the caller frame with the " "fallback unwind plan"); m_fallback_unwind_plan_sp.reset(); m_full_unwind_plan_sp = original_full_unwind_plan_sp; m_cfa = old_cfa; return false; } if (old_caller_pc_value != LLDB_INVALID_ADDRESS) { if (old_caller_pc_value == new_caller_pc_value && new_cfa == old_this_frame_cfa_value) { UnwindLogMsg("fallback unwind plan got the same values for this frame " "CFA and caller frame pc, not using"); m_fallback_unwind_plan_sp.reset(); m_full_unwind_plan_sp = original_full_unwind_plan_sp; m_cfa = old_cfa; return false; } } UnwindLogMsg("trying to unwind from this function with the UnwindPlan '%s' " "because UnwindPlan '%s' failed.", m_fallback_unwind_plan_sp->GetSourceName().GetCString(), original_full_unwind_plan_sp->GetSourceName().GetCString()); // We've copied the fallback unwind plan into the full - now clear the // fallback. m_fallback_unwind_plan_sp.reset(); } return true; } bool RegisterContextLLDB::ForceSwitchToFallbackUnwindPlan() { if (m_fallback_unwind_plan_sp.get() == NULL) return false; if (m_full_unwind_plan_sp.get() == NULL) return false; if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() || m_full_unwind_plan_sp->GetSourceName() == m_fallback_unwind_plan_sp->GetSourceName()) { return false; } UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); if (active_row && active_row->GetCFAValue().GetValueType() != UnwindPlan::Row::CFAValue::unspecified) { addr_t new_cfa; if (!ReadCFAValueForRow(m_fallback_unwind_plan_sp->GetRegisterKind(), active_row, new_cfa) || new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) { UnwindLogMsg("failed to get cfa with fallback unwindplan"); m_fallback_unwind_plan_sp.reset(); return false; } m_full_unwind_plan_sp = m_fallback_unwind_plan_sp; m_fallback_unwind_plan_sp.reset(); m_registers.clear(); m_cfa = new_cfa; UnwindLogMsg("switched unconditionally to the fallback unwindplan %s", m_full_unwind_plan_sp->GetSourceName().GetCString()); return true; } return false; } bool RegisterContextLLDB::ReadCFAValueForRow( lldb::RegisterKind row_register_kind, const UnwindPlan::RowSP &row, addr_t &cfa_value) { RegisterValue reg_value; cfa_value = LLDB_INVALID_ADDRESS; addr_t cfa_reg_contents; switch (row->GetCFAValue().GetValueType()) { case UnwindPlan::Row::CFAValue::isRegisterDereferenced: { RegisterNumber cfa_reg(m_thread, row_register_kind, row->GetCFAValue().GetRegisterNumber()); if (ReadGPRValue(cfa_reg, cfa_reg_contents)) { const RegisterInfo *reg_info = GetRegisterInfoAtIndex(cfa_reg.GetAsKind(eRegisterKindLLDB)); RegisterValue reg_value; if (reg_info) { Error error = ReadRegisterValueFromMemory( reg_info, cfa_reg_contents, reg_info->byte_size, reg_value); if (error.Success()) { cfa_value = reg_value.GetAsUInt64(); UnwindLogMsg( "CFA value via dereferencing reg %s (%d): reg has val 0x%" PRIx64 ", CFA value is 0x%" PRIx64, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB), cfa_reg_contents, cfa_value); return true; } else { UnwindLogMsg("Tried to deref reg %s (%d) [0x%" PRIx64 "] but memory read failed.", cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB), cfa_reg_contents); } } } break; } case UnwindPlan::Row::CFAValue::isRegisterPlusOffset: { RegisterNumber cfa_reg(m_thread, row_register_kind, row->GetCFAValue().GetRegisterNumber()); if (ReadGPRValue(cfa_reg, cfa_reg_contents)) { if (cfa_reg_contents == LLDB_INVALID_ADDRESS || cfa_reg_contents == 0 || cfa_reg_contents == 1) { UnwindLogMsg( "Got an invalid CFA register value - reg %s (%d), value 0x%" PRIx64, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB), cfa_reg_contents); cfa_reg_contents = LLDB_INVALID_ADDRESS; return false; } cfa_value = cfa_reg_contents + row->GetCFAValue().GetOffset(); UnwindLogMsg( "CFA is 0x%" PRIx64 ": Register %s (%d) contents are 0x%" PRIx64 ", offset is %d", cfa_value, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB), cfa_reg_contents, row->GetCFAValue().GetOffset()); return true; } break; } case UnwindPlan::Row::CFAValue::isDWARFExpression: { ExecutionContext exe_ctx(m_thread.shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); DataExtractor dwarfdata(row->GetCFAValue().GetDWARFExpressionBytes(), row->GetCFAValue().GetDWARFExpressionLength(), process->GetByteOrder(), process->GetAddressByteSize()); ModuleSP opcode_ctx; DWARFExpression dwarfexpr(opcode_ctx, dwarfdata, nullptr, 0, row->GetCFAValue().GetDWARFExpressionLength()); dwarfexpr.SetRegisterKind(row_register_kind); Value result; Error error; if (dwarfexpr.Evaluate(&exe_ctx, nullptr, nullptr, this, 0, nullptr, nullptr, result, &error)) { cfa_value = result.GetScalar().ULongLong(); UnwindLogMsg("CFA value set by DWARF expression is 0x%" PRIx64, cfa_value); return true; } UnwindLogMsg("Failed to set CFA value via DWARF expression: %s", error.AsCString()); break; } default: return false; } return false; } // Retrieve a general purpose register value for THIS frame, as saved by the // NEXT frame, i.e. the frame that // this frame called. e.g. // // foo () { } // bar () { foo (); } // main () { bar (); } // // stopped in foo() so // frame 0 - foo // frame 1 - bar // frame 2 - main // and this RegisterContext is for frame 1 (bar) - if we want to get the pc // value for frame 1, we need to ask // where frame 0 (the "next" frame) saved that and retrieve the value. bool RegisterContextLLDB::ReadGPRValue(lldb::RegisterKind register_kind, uint32_t regnum, addr_t &value) { if (!IsValid()) return false; uint32_t lldb_regnum; if (register_kind == eRegisterKindLLDB) { lldb_regnum = regnum; } else if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds( register_kind, regnum, eRegisterKindLLDB, lldb_regnum)) { return false; } const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum); RegisterValue reg_value; // if this is frame 0 (currently executing frame), get the requested reg // contents from the actual thread registers if (IsFrameZero()) { if (m_thread.GetRegisterContext()->ReadRegister(reg_info, reg_value)) { value = reg_value.GetAsUInt64(); return true; } return false; } bool pc_register = false; uint32_t generic_regnum; if (register_kind == eRegisterKindGeneric && (regnum == LLDB_REGNUM_GENERIC_PC || regnum == LLDB_REGNUM_GENERIC_RA)) { pc_register = true; } else if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds( register_kind, regnum, eRegisterKindGeneric, generic_regnum) && (generic_regnum == LLDB_REGNUM_GENERIC_PC || generic_regnum == LLDB_REGNUM_GENERIC_RA)) { pc_register = true; } lldb_private::UnwindLLDB::RegisterLocation regloc; if (!m_parent_unwind.SearchForSavedLocationForRegister( lldb_regnum, regloc, m_frame_number - 1, pc_register)) { return false; } if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, reg_value)) { value = reg_value.GetAsUInt64(); return true; } return false; } bool RegisterContextLLDB::ReadGPRValue(const RegisterNumber ®num, addr_t &value) { return ReadGPRValue(regnum.GetRegisterKind(), regnum.GetRegisterNumber(), value); } // Find the value of a register in THIS frame bool RegisterContextLLDB::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) { if (!IsValid()) return false; const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB]; UnwindLogMsgVerbose("looking for register saved location for reg %d", lldb_regnum); // If this is the 0th frame, hand this over to the live register context if (IsFrameZero()) { UnwindLogMsgVerbose("passing along to the live register context for reg %d", lldb_regnum); return m_thread.GetRegisterContext()->ReadRegister(reg_info, value); } bool is_pc_regnum = false; if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC || reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_RA) { is_pc_regnum = true; } lldb_private::UnwindLLDB::RegisterLocation regloc; // Find out where the NEXT frame saved THIS frame's register contents if (!m_parent_unwind.SearchForSavedLocationForRegister( lldb_regnum, regloc, m_frame_number - 1, is_pc_regnum)) return false; return ReadRegisterValueFromRegisterLocation(regloc, reg_info, value); } bool RegisterContextLLDB::WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value) { if (!IsValid()) return false; const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB]; UnwindLogMsgVerbose("looking for register saved location for reg %d", lldb_regnum); // If this is the 0th frame, hand this over to the live register context if (IsFrameZero()) { UnwindLogMsgVerbose("passing along to the live register context for reg %d", lldb_regnum); return m_thread.GetRegisterContext()->WriteRegister(reg_info, value); } lldb_private::UnwindLLDB::RegisterLocation regloc; // Find out where the NEXT frame saved THIS frame's register contents if (!m_parent_unwind.SearchForSavedLocationForRegister( lldb_regnum, regloc, m_frame_number - 1, false)) return false; return WriteRegisterValueToRegisterLocation(regloc, reg_info, value); } // Don't need to implement this one bool RegisterContextLLDB::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) { return false; } // Don't need to implement this one bool RegisterContextLLDB::WriteAllRegisterValues( const lldb::DataBufferSP &data_sp) { return false; } // Retrieve the pc value for THIS from bool RegisterContextLLDB::GetCFA(addr_t &cfa) { if (!IsValid()) { return false; } if (m_cfa == LLDB_INVALID_ADDRESS) { return false; } cfa = m_cfa; return true; } RegisterContextLLDB::SharedPtr RegisterContextLLDB::GetNextFrame() const { RegisterContextLLDB::SharedPtr regctx; if (m_frame_number == 0) return regctx; return m_parent_unwind.GetRegisterContextForFrameNum(m_frame_number - 1); } RegisterContextLLDB::SharedPtr RegisterContextLLDB::GetPrevFrame() const { RegisterContextLLDB::SharedPtr regctx; return m_parent_unwind.GetRegisterContextForFrameNum(m_frame_number + 1); } // Retrieve the address of the start of the function of THIS frame bool RegisterContextLLDB::GetStartPC(addr_t &start_pc) { if (!IsValid()) return false; if (!m_start_pc.IsValid()) { return ReadPC(start_pc); } start_pc = m_start_pc.GetLoadAddress(CalculateTarget().get()); return true; } // Retrieve the current pc value for THIS frame, as saved by the NEXT frame. bool RegisterContextLLDB::ReadPC(addr_t &pc) { if (!IsValid()) return false; bool above_trap_handler = false; if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsTrapHandlerFrame()) above_trap_handler = true; if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) { // A pc value of 0 or 1 is impossible in the middle of the stack -- it // indicates the end of a stack walk. // On the currently executing frame (or such a frame interrupted // asynchronously by sigtramp et al) this may // occur if code has jumped through a NULL pointer -- we want to be able to // unwind past that frame to help // find the bug. if (m_all_registers_available == false && above_trap_handler == false && (pc == 0 || pc == 1)) { return false; } else { return true; } } else { return false; } } void RegisterContextLLDB::UnwindLogMsg(const char *fmt, ...) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (log) { va_list args; va_start(args, fmt); char *logmsg; if (vasprintf(&logmsg, fmt, args) == -1 || logmsg == NULL) { if (logmsg) free(logmsg); va_end(args); return; } va_end(args); log->Printf("%*sth%d/fr%u %s", m_frame_number < 100 ? m_frame_number : 100, "", m_thread.GetIndexID(), m_frame_number, logmsg); free(logmsg); } } void RegisterContextLLDB::UnwindLogMsgVerbose(const char *fmt, ...) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (log && log->GetVerbose()) { va_list args; va_start(args, fmt); char *logmsg; if (vasprintf(&logmsg, fmt, args) == -1 || logmsg == NULL) { if (logmsg) free(logmsg); va_end(args); return; } va_end(args); log->Printf("%*sth%d/fr%u %s", m_frame_number < 100 ? m_frame_number : 100, "", m_thread.GetIndexID(), m_frame_number, logmsg); free(logmsg); } } Index: vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp (revision 311542) @@ -1,3633 +1,3630 @@ //===-- GDBRemoteCommunicationClient.cpp ------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "GDBRemoteCommunicationClient.h" // C Includes #include #include // C++ Includes #include #include // Other libraries and framework includes #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Log.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamGDBRemote.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/StringConvert.h" #include "lldb/Interpreter/Args.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/MemoryRegionInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/JSON.h" #include "lldb/Utility/LLDBAssert.h" // Project includes #include "ProcessGDBRemote.h" #include "ProcessGDBRemoteLog.h" #include "Utility/StringExtractorGDBRemote.h" #include "lldb/Host/Config.h" #include "llvm/ADT/StringSwitch.h" #if defined(HAVE_LIBCOMPRESSION) #include #endif using namespace lldb; using namespace lldb_private; using namespace lldb_private::process_gdb_remote; using namespace std::chrono; //---------------------------------------------------------------------- // GDBRemoteCommunicationClient constructor //---------------------------------------------------------------------- GDBRemoteCommunicationClient::GDBRemoteCommunicationClient() : GDBRemoteClientBase("gdb-remote.client", "gdb-remote.client.rx_packet"), m_supports_not_sending_acks(eLazyBoolCalculate), m_supports_thread_suffix(eLazyBoolCalculate), m_supports_threads_in_stop_reply(eLazyBoolCalculate), m_supports_vCont_all(eLazyBoolCalculate), m_supports_vCont_any(eLazyBoolCalculate), m_supports_vCont_c(eLazyBoolCalculate), m_supports_vCont_C(eLazyBoolCalculate), m_supports_vCont_s(eLazyBoolCalculate), m_supports_vCont_S(eLazyBoolCalculate), m_qHostInfo_is_valid(eLazyBoolCalculate), m_curr_pid_is_valid(eLazyBoolCalculate), m_qProcessInfo_is_valid(eLazyBoolCalculate), m_qGDBServerVersion_is_valid(eLazyBoolCalculate), m_supports_alloc_dealloc_memory(eLazyBoolCalculate), m_supports_memory_region_info(eLazyBoolCalculate), m_supports_watchpoint_support_info(eLazyBoolCalculate), m_supports_detach_stay_stopped(eLazyBoolCalculate), m_watchpoints_trigger_after_instruction(eLazyBoolCalculate), m_attach_or_wait_reply(eLazyBoolCalculate), m_prepare_for_reg_writing_reply(eLazyBoolCalculate), m_supports_p(eLazyBoolCalculate), m_supports_x(eLazyBoolCalculate), m_avoid_g_packets(eLazyBoolCalculate), m_supports_QSaveRegisterState(eLazyBoolCalculate), m_supports_qXfer_auxv_read(eLazyBoolCalculate), m_supports_qXfer_libraries_read(eLazyBoolCalculate), m_supports_qXfer_libraries_svr4_read(eLazyBoolCalculate), m_supports_qXfer_features_read(eLazyBoolCalculate), m_supports_augmented_libraries_svr4_read(eLazyBoolCalculate), m_supports_jThreadExtendedInfo(eLazyBoolCalculate), m_supports_jLoadedDynamicLibrariesInfos(eLazyBoolCalculate), m_supports_jGetSharedCacheInfo(eLazyBoolCalculate), m_supports_qProcessInfoPID(true), m_supports_qfProcessInfo(true), m_supports_qUserName(true), m_supports_qGroupName(true), m_supports_qThreadStopInfo(true), m_supports_z0(true), m_supports_z1(true), m_supports_z2(true), m_supports_z3(true), m_supports_z4(true), m_supports_QEnvironment(true), m_supports_QEnvironmentHexEncoded(true), m_supports_qSymbol(true), m_qSymbol_requests_done(false), m_supports_qModuleInfo(true), m_supports_jThreadsInfo(true), m_supports_jModulesInfo(true), m_curr_pid(LLDB_INVALID_PROCESS_ID), m_curr_tid(LLDB_INVALID_THREAD_ID), m_curr_tid_run(LLDB_INVALID_THREAD_ID), m_num_supported_hardware_watchpoints(0), m_host_arch(), m_process_arch(), m_os_version_major(UINT32_MAX), m_os_version_minor(UINT32_MAX), m_os_version_update(UINT32_MAX), m_os_build(), m_os_kernel(), m_hostname(), m_gdb_server_name(), m_gdb_server_version(UINT32_MAX), m_default_packet_timeout(0), m_max_packet_size(0), m_qSupported_response(), m_supported_async_json_packets_is_valid(false), m_supported_async_json_packets_sp() {} //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient() { if (IsConnected()) Disconnect(); } bool GDBRemoteCommunicationClient::HandshakeWithServer(Error *error_ptr) { ResetDiscoverableSettings(false); // Start the read thread after we send the handshake ack since if we // fail to send the handshake ack, there is no reason to continue... if (SendAck()) { // Wait for any responses that might have been queued up in the remote // GDB server and flush them all StringExtractorGDBRemote response; PacketResult packet_result = PacketResult::Success; while (packet_result == PacketResult::Success) packet_result = ReadPacket(response, milliseconds(10), false); // The return value from QueryNoAckModeSupported() is true if the packet // was sent and _any_ response (including UNIMPLEMENTED) was received), // or false if no response was received. This quickly tells us if we have // a live connection to a remote GDB server... if (QueryNoAckModeSupported()) { return true; } else { if (error_ptr) error_ptr->SetErrorString("failed to get reply to handshake packet"); } } else { if (error_ptr) error_ptr->SetErrorString("failed to send the handshake ack"); } return false; } bool GDBRemoteCommunicationClient::GetEchoSupported() { if (m_supports_qEcho == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qEcho == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetAugmentedLibrariesSVR4ReadSupported() { if (m_supports_augmented_libraries_svr4_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_augmented_libraries_svr4_read == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetQXferLibrariesSVR4ReadSupported() { if (m_supports_qXfer_libraries_svr4_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qXfer_libraries_svr4_read == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetQXferLibrariesReadSupported() { if (m_supports_qXfer_libraries_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qXfer_libraries_read == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetQXferAuxvReadSupported() { if (m_supports_qXfer_auxv_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qXfer_auxv_read == eLazyBoolYes; } bool GDBRemoteCommunicationClient::GetQXferFeaturesReadSupported() { if (m_supports_qXfer_features_read == eLazyBoolCalculate) { GetRemoteQSupported(); } return m_supports_qXfer_features_read == eLazyBoolYes; } uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() { if (m_max_packet_size == 0) { GetRemoteQSupported(); } return m_max_packet_size; } bool GDBRemoteCommunicationClient::QueryNoAckModeSupported() { if (m_supports_not_sending_acks == eLazyBoolCalculate) { m_send_acks = true; m_supports_not_sending_acks = eLazyBoolNo; // This is the first real packet that we'll send in a debug session and it // may take a little // longer than normal to receive a reply. Wait at least 6 seconds for a // reply to this packet. ScopedTimeout timeout(*this, std::max(GetPacketTimeout(), seconds(6))); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("QStartNoAckMode", response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_send_acks = false; m_supports_not_sending_acks = eLazyBoolYes; } return true; } } return false; } void GDBRemoteCommunicationClient::GetListThreadsInStopReplySupported() { if (m_supports_threads_in_stop_reply == eLazyBoolCalculate) { m_supports_threads_in_stop_reply = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("QListThreadsInStopReply", response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_supports_threads_in_stop_reply = eLazyBoolYes; } } } bool GDBRemoteCommunicationClient::GetVAttachOrWaitSupported() { if (m_attach_or_wait_reply == eLazyBoolCalculate) { m_attach_or_wait_reply = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qVAttachOrWaitSupported", response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_attach_or_wait_reply = eLazyBoolYes; } } if (m_attach_or_wait_reply == eLazyBoolYes) return true; else return false; } bool GDBRemoteCommunicationClient::GetSyncThreadStateSupported() { if (m_prepare_for_reg_writing_reply == eLazyBoolCalculate) { m_prepare_for_reg_writing_reply = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qSyncThreadStateSupported", response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_prepare_for_reg_writing_reply = eLazyBoolYes; } } if (m_prepare_for_reg_writing_reply == eLazyBoolYes) return true; else return false; } void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) { if (did_exec == false) { // Hard reset everything, this is when we first connect to a GDB server m_supports_not_sending_acks = eLazyBoolCalculate; m_supports_thread_suffix = eLazyBoolCalculate; m_supports_threads_in_stop_reply = eLazyBoolCalculate; m_supports_vCont_c = eLazyBoolCalculate; m_supports_vCont_C = eLazyBoolCalculate; m_supports_vCont_s = eLazyBoolCalculate; m_supports_vCont_S = eLazyBoolCalculate; m_supports_p = eLazyBoolCalculate; m_supports_x = eLazyBoolCalculate; m_supports_QSaveRegisterState = eLazyBoolCalculate; m_qHostInfo_is_valid = eLazyBoolCalculate; m_curr_pid_is_valid = eLazyBoolCalculate; m_qGDBServerVersion_is_valid = eLazyBoolCalculate; m_supports_alloc_dealloc_memory = eLazyBoolCalculate; m_supports_memory_region_info = eLazyBoolCalculate; m_prepare_for_reg_writing_reply = eLazyBoolCalculate; m_attach_or_wait_reply = eLazyBoolCalculate; m_avoid_g_packets = eLazyBoolCalculate; m_supports_qXfer_auxv_read = eLazyBoolCalculate; m_supports_qXfer_libraries_read = eLazyBoolCalculate; m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate; m_supports_qXfer_features_read = eLazyBoolCalculate; m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate; m_supports_qProcessInfoPID = true; m_supports_qfProcessInfo = true; m_supports_qUserName = true; m_supports_qGroupName = true; m_supports_qThreadStopInfo = true; m_supports_z0 = true; m_supports_z1 = true; m_supports_z2 = true; m_supports_z3 = true; m_supports_z4 = true; m_supports_QEnvironment = true; m_supports_QEnvironmentHexEncoded = true; m_supports_qSymbol = true; m_qSymbol_requests_done = false; m_supports_qModuleInfo = true; m_host_arch.Clear(); m_os_version_major = UINT32_MAX; m_os_version_minor = UINT32_MAX; m_os_version_update = UINT32_MAX; m_os_build.clear(); m_os_kernel.clear(); m_hostname.clear(); m_gdb_server_name.clear(); m_gdb_server_version = UINT32_MAX; m_default_packet_timeout = seconds(0); m_max_packet_size = 0; m_qSupported_response.clear(); m_supported_async_json_packets_is_valid = false; m_supported_async_json_packets_sp.reset(); m_supports_jModulesInfo = true; } // These flags should be reset when we first connect to a GDB server // and when our inferior process execs m_qProcessInfo_is_valid = eLazyBoolCalculate; m_process_arch.Clear(); } void GDBRemoteCommunicationClient::GetRemoteQSupported() { // Clear out any capabilities we expect to see in the qSupported response m_supports_qXfer_auxv_read = eLazyBoolNo; m_supports_qXfer_libraries_read = eLazyBoolNo; m_supports_qXfer_libraries_svr4_read = eLazyBoolNo; m_supports_augmented_libraries_svr4_read = eLazyBoolNo; m_supports_qXfer_features_read = eLazyBoolNo; m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if // not, we assume no limit // build the qSupported packet std::vector features = {"xmlRegisters=i386,arm,mips"}; StreamString packet; packet.PutCString("qSupported"); for (uint32_t i = 0; i < features.size(); ++i) { packet.PutCString(i == 0 ? ":" : ";"); packet.PutCString(features[i]); } StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, /*send_async=*/false) == PacketResult::Success) { const char *response_cstr = response.GetStringRef().c_str(); // Hang on to the qSupported packet, so that platforms can do custom // configuration of the transport before attaching/launching the // process. m_qSupported_response = response_cstr; if (::strstr(response_cstr, "qXfer:auxv:read+")) m_supports_qXfer_auxv_read = eLazyBoolYes; if (::strstr(response_cstr, "qXfer:libraries-svr4:read+")) m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; if (::strstr(response_cstr, "augmented-libraries-svr4-read")) { m_supports_qXfer_libraries_svr4_read = eLazyBoolYes; // implied m_supports_augmented_libraries_svr4_read = eLazyBoolYes; } if (::strstr(response_cstr, "qXfer:libraries:read+")) m_supports_qXfer_libraries_read = eLazyBoolYes; if (::strstr(response_cstr, "qXfer:features:read+")) m_supports_qXfer_features_read = eLazyBoolYes; // Look for a list of compressions in the features list e.g. // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-deflate,lzma const char *features_list = ::strstr(response_cstr, "qXfer:features:"); if (features_list) { const char *compressions = ::strstr(features_list, "SupportedCompressions="); if (compressions) { std::vector supported_compressions; compressions += sizeof("SupportedCompressions=") - 1; const char *end_of_compressions = strchr(compressions, ';'); if (end_of_compressions == NULL) { end_of_compressions = strchr(compressions, '\0'); } const char *current_compression = compressions; while (current_compression < end_of_compressions) { const char *next_compression_name = strchr(current_compression, ','); const char *end_of_this_word = next_compression_name; if (next_compression_name == NULL || end_of_compressions < next_compression_name) { end_of_this_word = end_of_compressions; } if (end_of_this_word) { if (end_of_this_word == current_compression) { current_compression++; } else { std::string this_compression( current_compression, end_of_this_word - current_compression); supported_compressions.push_back(this_compression); current_compression = end_of_this_word + 1; } } else { supported_compressions.push_back(current_compression); current_compression = end_of_compressions; } } if (supported_compressions.size() > 0) { MaybeEnableCompression(supported_compressions); } } } if (::strstr(response_cstr, "qEcho")) m_supports_qEcho = eLazyBoolYes; else m_supports_qEcho = eLazyBoolNo; const char *packet_size_str = ::strstr(response_cstr, "PacketSize="); if (packet_size_str) { StringExtractorGDBRemote packet_response(packet_size_str + strlen("PacketSize=")); m_max_packet_size = packet_response.GetHexMaxU64(/*little_endian=*/false, UINT64_MAX); if (m_max_packet_size == 0) { m_max_packet_size = UINT64_MAX; // Must have been a garbled response Log *log( ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); if (log) log->Printf("Garbled PacketSize spec in qSupported response"); } } } } bool GDBRemoteCommunicationClient::GetThreadSuffixSupported() { if (m_supports_thread_suffix == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_thread_suffix = eLazyBoolNo; if (SendPacketAndWaitForResponse("QThreadSuffixSupported", response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_supports_thread_suffix = eLazyBoolYes; } } return m_supports_thread_suffix; } bool GDBRemoteCommunicationClient::GetVContSupported(char flavor) { if (m_supports_vCont_c == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_vCont_any = eLazyBoolNo; m_supports_vCont_all = eLazyBoolNo; m_supports_vCont_c = eLazyBoolNo; m_supports_vCont_C = eLazyBoolNo; m_supports_vCont_s = eLazyBoolNo; m_supports_vCont_S = eLazyBoolNo; if (SendPacketAndWaitForResponse("vCont?", response, false) == PacketResult::Success) { const char *response_cstr = response.GetStringRef().c_str(); if (::strstr(response_cstr, ";c")) m_supports_vCont_c = eLazyBoolYes; if (::strstr(response_cstr, ";C")) m_supports_vCont_C = eLazyBoolYes; if (::strstr(response_cstr, ";s")) m_supports_vCont_s = eLazyBoolYes; if (::strstr(response_cstr, ";S")) m_supports_vCont_S = eLazyBoolYes; if (m_supports_vCont_c == eLazyBoolYes && m_supports_vCont_C == eLazyBoolYes && m_supports_vCont_s == eLazyBoolYes && m_supports_vCont_S == eLazyBoolYes) { m_supports_vCont_all = eLazyBoolYes; } if (m_supports_vCont_c == eLazyBoolYes || m_supports_vCont_C == eLazyBoolYes || m_supports_vCont_s == eLazyBoolYes || m_supports_vCont_S == eLazyBoolYes) { m_supports_vCont_any = eLazyBoolYes; } } } switch (flavor) { case 'a': return m_supports_vCont_any; case 'A': return m_supports_vCont_all; case 'c': return m_supports_vCont_c; case 'C': return m_supports_vCont_C; case 's': return m_supports_vCont_s; case 'S': return m_supports_vCont_S; default: break; } return false; } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationClient::SendThreadSpecificPacketAndWaitForResponse( lldb::tid_t tid, StreamString &&payload, StringExtractorGDBRemote &response, bool send_async) { Lock lock(*this, send_async); if (!lock) { if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet( GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)) log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex " "for %s packet.", __FUNCTION__, payload.GetData()); return PacketResult::ErrorNoSequenceLock; } if (GetThreadSuffixSupported()) payload.Printf(";thread:%4.4" PRIx64 ";", tid); else { if (!SetCurrentThread(tid)) return PacketResult::ErrorSendFailed; } return SendPacketAndWaitForResponseNoLock(payload.GetString(), response); } // Check if the target supports 'p' packet. It sends out a 'p' // packet and checks the response. A normal packet will tell us // that support is available. // // Takes a valid thread ID because p needs to apply to a thread. bool GDBRemoteCommunicationClient::GetpPacketSupported(lldb::tid_t tid) { if (m_supports_p == eLazyBoolCalculate) { m_supports_p = eLazyBoolNo; StreamString payload; payload.PutCString("p0"); StringExtractorGDBRemote response; if (SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload), response, false) == PacketResult::Success && response.IsNormalResponse()) { m_supports_p = eLazyBoolYes; } } return m_supports_p; } StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() { // Get information on all threads at one using the "jThreadsInfo" packet StructuredData::ObjectSP object_sp; if (m_supports_jThreadsInfo) { StringExtractorGDBRemote response; response.SetResponseValidatorToJSON(); if (SendPacketAndWaitForResponse("jThreadsInfo", response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) { m_supports_jThreadsInfo = false; } else if (!response.Empty()) { object_sp = StructuredData::ParseJSON(response.GetStringRef()); } } } return object_sp; } bool GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported() { if (m_supports_jThreadExtendedInfo == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_jThreadExtendedInfo = eLazyBoolNo; if (SendPacketAndWaitForResponse("jThreadExtendedInfo:", response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_supports_jThreadExtendedInfo = eLazyBoolYes; } } } return m_supports_jThreadExtendedInfo; } bool GDBRemoteCommunicationClient::GetLoadedDynamicLibrariesInfosSupported() { if (m_supports_jLoadedDynamicLibrariesInfos == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolNo; if (SendPacketAndWaitForResponse("jGetLoadedDynamicLibrariesInfos:", response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_supports_jLoadedDynamicLibrariesInfos = eLazyBoolYes; } } } return m_supports_jLoadedDynamicLibrariesInfos; } bool GDBRemoteCommunicationClient::GetSharedCacheInfoSupported() { if (m_supports_jGetSharedCacheInfo == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_jGetSharedCacheInfo = eLazyBoolNo; if (SendPacketAndWaitForResponse("jGetSharedCacheInfo:", response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_supports_jGetSharedCacheInfo = eLazyBoolYes; } } } return m_supports_jGetSharedCacheInfo; } bool GDBRemoteCommunicationClient::GetxPacketSupported() { if (m_supports_x == eLazyBoolCalculate) { StringExtractorGDBRemote response; m_supports_x = eLazyBoolNo; char packet[256]; snprintf(packet, sizeof(packet), "x0,0"); if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsOKResponse()) m_supports_x = eLazyBoolYes; } } return m_supports_x; } GDBRemoteCommunicationClient::PacketResult GDBRemoteCommunicationClient::SendPacketsAndConcatenateResponses( const char *payload_prefix, std::string &response_string) { Lock lock(*this, false); if (!lock) { Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); if (log) log->Printf("error: failed to get packet sequence mutex, not sending " "packets with prefix '%s'", payload_prefix); return PacketResult::ErrorNoSequenceLock; } response_string = ""; std::string payload_prefix_str(payload_prefix); unsigned int response_size = 0x1000; if (response_size > GetRemoteMaxPacketSize()) { // May send qSupported packet response_size = GetRemoteMaxPacketSize(); } for (unsigned int offset = 0; true; offset += response_size) { StringExtractorGDBRemote this_response; // Construct payload char sizeDescriptor[128]; snprintf(sizeDescriptor, sizeof(sizeDescriptor), "%x,%x", offset, response_size); PacketResult result = SendPacketAndWaitForResponseNoLock( payload_prefix_str + sizeDescriptor, this_response); if (result != PacketResult::Success) return result; const std::string &this_string = this_response.GetStringRef(); // Check for m or l as first character; l seems to mean this is the last // chunk char first_char = *this_string.c_str(); if (first_char != 'm' && first_char != 'l') { return PacketResult::ErrorReplyInvalid; } // Concatenate the result so far (skipping 'm' or 'l') response_string.append(this_string, 1, std::string::npos); if (first_char == 'l') // We're done return PacketResult::Success; } } lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID(bool allow_lazy) { if (allow_lazy && m_curr_pid_is_valid == eLazyBoolYes) return m_curr_pid; // First try to retrieve the pid via the qProcessInfo request. GetCurrentProcessInfo(allow_lazy); if (m_curr_pid_is_valid == eLazyBoolYes) { // We really got it. return m_curr_pid; } else { // If we don't get a response for qProcessInfo, check if $qC gives us a // result. // $qC only returns a real process id on older debugserver and lldb-platform // stubs. // The gdb remote protocol documents $qC as returning the thread id, which // newer // debugserver and lldb-gdbserver stubs return correctly. StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qC", response, false) == PacketResult::Success) { if (response.GetChar() == 'Q') { if (response.GetChar() == 'C') { m_curr_pid = response.GetHexMaxU32(false, LLDB_INVALID_PROCESS_ID); if (m_curr_pid != LLDB_INVALID_PROCESS_ID) { m_curr_pid_is_valid = eLazyBoolYes; return m_curr_pid; } } } } // If we don't get a response for $qC, check if $qfThreadID gives us a // result. if (m_curr_pid == LLDB_INVALID_PROCESS_ID) { std::vector thread_ids; bool sequence_mutex_unavailable; size_t size; size = GetCurrentThreadIDs(thread_ids, sequence_mutex_unavailable); if (size && sequence_mutex_unavailable == false) { m_curr_pid = thread_ids.front(); m_curr_pid_is_valid = eLazyBoolYes; return m_curr_pid; } } } return LLDB_INVALID_PROCESS_ID; } bool GDBRemoteCommunicationClient::GetLaunchSuccess(std::string &error_str) { error_str.clear(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qLaunchSuccess", response, false) == PacketResult::Success) { if (response.IsOKResponse()) return true; if (response.GetChar() == 'E') { // A string the describes what failed when launching... error_str = response.GetStringRef().substr(1); } else { error_str.assign("unknown error occurred launching process"); } } else { error_str.assign("timed out waiting for app to launch"); } return false; } int GDBRemoteCommunicationClient::SendArgumentsPacket( const ProcessLaunchInfo &launch_info) { // Since we don't get the send argv0 separate from the executable path, we // need to // make sure to use the actual executable path found in the launch_info... std::vector argv; FileSpec exe_file = launch_info.GetExecutableFile(); std::string exe_path; const char *arg = NULL; const Args &launch_args = launch_info.GetArguments(); if (exe_file) exe_path = exe_file.GetPath(false); else { arg = launch_args.GetArgumentAtIndex(0); if (arg) exe_path = arg; } if (!exe_path.empty()) { argv.push_back(exe_path.c_str()); for (uint32_t i = 1; (arg = launch_args.GetArgumentAtIndex(i)) != NULL; ++i) { if (arg) argv.push_back(arg); } } if (!argv.empty()) { StreamString packet; packet.PutChar('A'); for (size_t i = 0, n = argv.size(); i < n; ++i) { arg = argv[i]; const int arg_len = strlen(arg); if (i > 0) packet.PutChar(','); packet.Printf("%i,%i,", arg_len * 2, (int)i); packet.PutBytesAsRawHex8(arg, arg_len); } StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SendEnvironmentPacket( char const *name_equal_value) { if (name_equal_value && name_equal_value[0]) { StreamString packet; bool send_hex_encoding = false; for (const char *p = name_equal_value; *p != '\0' && send_hex_encoding == false; ++p) { if (isprint(*p)) { switch (*p) { case '$': case '#': case '*': case '}': send_hex_encoding = true; break; default: break; } } else { // We have non printable characters, lets hex encode this... send_hex_encoding = true; } } StringExtractorGDBRemote response; if (send_hex_encoding) { if (m_supports_QEnvironmentHexEncoded) { packet.PutCString("QEnvironmentHexEncoded:"); packet.PutBytesAsRawHex8(name_equal_value, strlen(name_equal_value)); if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; if (response.IsUnsupportedResponse()) m_supports_QEnvironmentHexEncoded = false; } } } else if (m_supports_QEnvironment) { packet.Printf("QEnvironment:%s", name_equal_value); if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; if (response.IsUnsupportedResponse()) m_supports_QEnvironment = false; } } } return -1; } int GDBRemoteCommunicationClient::SendLaunchArchPacket(char const *arch) { if (arch && arch[0]) { StreamString packet; packet.Printf("QLaunchArch:%s", arch); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SendLaunchEventDataPacket( char const *data, bool *was_supported) { if (data && *data != '\0') { StreamString packet; packet.Printf("QSetProcessEvent:%s", data); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) { if (was_supported) *was_supported = true; return 0; } else if (response.IsUnsupportedResponse()) { if (was_supported) *was_supported = false; return -1; } else { uint8_t error = response.GetError(); if (was_supported) *was_supported = true; if (error) return error; } } } return -1; } bool GDBRemoteCommunicationClient::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) { if (GetHostInfo()) { if (m_os_version_major != UINT32_MAX) { major = m_os_version_major; minor = m_os_version_minor; update = m_os_version_update; return true; } } return false; } bool GDBRemoteCommunicationClient::GetOSBuildString(std::string &s) { if (GetHostInfo()) { if (!m_os_build.empty()) { s = m_os_build; return true; } } s.clear(); return false; } bool GDBRemoteCommunicationClient::GetOSKernelDescription(std::string &s) { if (GetHostInfo()) { if (!m_os_kernel.empty()) { s = m_os_kernel; return true; } } s.clear(); return false; } bool GDBRemoteCommunicationClient::GetHostname(std::string &s) { if (GetHostInfo()) { if (!m_hostname.empty()) { s = m_hostname; return true; } } s.clear(); return false; } ArchSpec GDBRemoteCommunicationClient::GetSystemArchitecture() { if (GetHostInfo()) return m_host_arch; return ArchSpec(); } const lldb_private::ArchSpec & GDBRemoteCommunicationClient::GetProcessArchitecture() { if (m_qProcessInfo_is_valid == eLazyBoolCalculate) GetCurrentProcessInfo(); return m_process_arch; } bool GDBRemoteCommunicationClient::GetGDBServerVersion() { if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) { m_gdb_server_name.clear(); m_gdb_server_version = 0; m_qGDBServerVersion_is_valid = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qGDBServerVersion", response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { llvm::StringRef name, value; bool success = false; while (response.GetNameColonValue(name, value)) { if (name.equals("name")) { success = true; m_gdb_server_name = value; } else if (name.equals("version")) { llvm::StringRef major, minor; std::tie(major, minor) = value.split('.'); if (!major.getAsInteger(0, m_gdb_server_version)) success = true; } } if (success) m_qGDBServerVersion_is_valid = eLazyBoolYes; } } } return m_qGDBServerVersion_is_valid == eLazyBoolYes; } void GDBRemoteCommunicationClient::MaybeEnableCompression( std::vector supported_compressions) { CompressionType avail_type = CompressionType::None; std::string avail_name; #if defined(HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is // available if (compression_decode_buffer != NULL && avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "lzfse") { avail_type = CompressionType::LZFSE; avail_name = compression; break; } } } #endif #if defined(HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is // available if (compression_decode_buffer != NULL && avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "zlib-deflate") { avail_type = CompressionType::ZlibDeflate; avail_name = compression; break; } } } #endif #if defined(HAVE_LIBZ) if (avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "zlib-deflate") { avail_type = CompressionType::ZlibDeflate; avail_name = compression; break; } } } #endif #if defined(HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is // available if (compression_decode_buffer != NULL && avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "lz4") { avail_type = CompressionType::LZ4; avail_name = compression; break; } } } #endif #if defined(HAVE_LIBCOMPRESSION) // libcompression is weak linked so test if compression_decode_buffer() is // available if (compression_decode_buffer != NULL && avail_type == CompressionType::None) { for (auto compression : supported_compressions) { if (compression == "lzma") { avail_type = CompressionType::LZMA; avail_name = compression; break; } } } #endif if (avail_type != CompressionType::None) { StringExtractorGDBRemote response; std::string packet = "QEnableCompression:type:" + avail_name + ";"; if (SendPacketAndWaitForResponse(packet, response, false) != PacketResult::Success) return; if (response.IsOKResponse()) { m_compression_type = avail_type; } } } const char *GDBRemoteCommunicationClient::GetGDBServerProgramName() { if (GetGDBServerVersion()) { if (!m_gdb_server_name.empty()) return m_gdb_server_name.c_str(); } return NULL; } uint32_t GDBRemoteCommunicationClient::GetGDBServerProgramVersion() { if (GetGDBServerVersion()) return m_gdb_server_version; return 0; } bool GDBRemoteCommunicationClient::GetDefaultThreadId(lldb::tid_t &tid) { StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qC", response, false) != PacketResult::Success) return false; if (!response.IsNormalResponse()) return false; if (response.GetChar() == 'Q' && response.GetChar() == 'C') tid = response.GetHexMaxU32(true, -1); return true; } bool GDBRemoteCommunicationClient::GetHostInfo(bool force) { Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS)); if (force || m_qHostInfo_is_valid == eLazyBoolCalculate) { m_qHostInfo_is_valid = eLazyBoolNo; StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qHostInfo", response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { llvm::StringRef name; llvm::StringRef value; uint32_t cpu = LLDB_INVALID_CPUTYPE; uint32_t sub = 0; std::string arch_name; std::string os_name; std::string vendor_name; std::string triple; std::string distribution_id; uint32_t pointer_byte_size = 0; ByteOrder byte_order = eByteOrderInvalid; uint32_t num_keys_decoded = 0; while (response.GetNameColonValue(name, value)) { if (name.equals("cputype")) { // exception type in big endian hex if (!value.getAsInteger(0, cpu)) ++num_keys_decoded; } else if (name.equals("cpusubtype")) { // exception count in big endian hex if (!value.getAsInteger(0, sub)) ++num_keys_decoded; } else if (name.equals("arch")) { arch_name = value; ++num_keys_decoded; } else if (name.equals("triple")) { StringExtractor extractor(value); extractor.GetHexByteString(triple); ++num_keys_decoded; } else if (name.equals("distribution_id")) { StringExtractor extractor(value); extractor.GetHexByteString(distribution_id); ++num_keys_decoded; } else if (name.equals("os_build")) { StringExtractor extractor(value); extractor.GetHexByteString(m_os_build); ++num_keys_decoded; } else if (name.equals("hostname")) { StringExtractor extractor(value); extractor.GetHexByteString(m_hostname); ++num_keys_decoded; } else if (name.equals("os_kernel")) { StringExtractor extractor(value); extractor.GetHexByteString(m_os_kernel); ++num_keys_decoded; } else if (name.equals("ostype")) { os_name = value; ++num_keys_decoded; } else if (name.equals("vendor")) { vendor_name = value; ++num_keys_decoded; } else if (name.equals("endian")) { byte_order = llvm::StringSwitch(value) .Case("little", eByteOrderLittle) .Case("big", eByteOrderBig) .Case("pdp", eByteOrderPDP) .Default(eByteOrderInvalid); if (byte_order != eByteOrderInvalid) ++num_keys_decoded; } else if (name.equals("ptrsize")) { if (!value.getAsInteger(0, pointer_byte_size)) ++num_keys_decoded; } else if (name.equals("os_version") || name.equals( "version")) // Older debugserver binaries used the // "version" key instead of // "os_version"... { Args::StringToVersion(value, m_os_version_major, m_os_version_minor, m_os_version_update); if (m_os_version_major != UINT32_MAX) ++num_keys_decoded; } else if (name.equals("watchpoint_exceptions_received")) { m_watchpoints_trigger_after_instruction = llvm::StringSwitch(value) .Case("before", eLazyBoolNo) .Case("after", eLazyBoolYes) .Default(eLazyBoolCalculate); if (m_watchpoints_trigger_after_instruction != eLazyBoolCalculate) ++num_keys_decoded; } else if (name.equals("default_packet_timeout")) { uint32_t timeout_seconds; if (!value.getAsInteger(0, timeout_seconds)) { m_default_packet_timeout = seconds(timeout_seconds); SetPacketTimeout(m_default_packet_timeout); ++num_keys_decoded; } } } if (num_keys_decoded > 0) m_qHostInfo_is_valid = eLazyBoolYes; if (triple.empty()) { if (arch_name.empty()) { if (cpu != LLDB_INVALID_CPUTYPE) { m_host_arch.SetArchitecture(eArchTypeMachO, cpu, sub); if (pointer_byte_size) { assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); } if (byte_order != eByteOrderInvalid) { assert(byte_order == m_host_arch.GetByteOrder()); } if (!vendor_name.empty()) m_host_arch.GetTriple().setVendorName( llvm::StringRef(vendor_name)); if (!os_name.empty()) m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name)); } } else { std::string triple; triple += arch_name; if (!vendor_name.empty() || !os_name.empty()) { triple += '-'; if (vendor_name.empty()) triple += "unknown"; else triple += vendor_name; triple += '-'; if (os_name.empty()) triple += "unknown"; else triple += os_name; } m_host_arch.SetTriple(triple.c_str()); llvm::Triple &host_triple = m_host_arch.GetTriple(); if (host_triple.getVendor() == llvm::Triple::Apple && host_triple.getOS() == llvm::Triple::Darwin) { switch (m_host_arch.GetMachine()) { case llvm::Triple::aarch64: case llvm::Triple::arm: case llvm::Triple::thumb: host_triple.setOS(llvm::Triple::IOS); break; default: host_triple.setOS(llvm::Triple::MacOSX); break; } } if (pointer_byte_size) { assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); } if (byte_order != eByteOrderInvalid) { assert(byte_order == m_host_arch.GetByteOrder()); } } } else { m_host_arch.SetTriple(triple.c_str()); if (pointer_byte_size) { assert(pointer_byte_size == m_host_arch.GetAddressByteSize()); } if (byte_order != eByteOrderInvalid) { assert(byte_order == m_host_arch.GetByteOrder()); } if (log) log->Printf("GDBRemoteCommunicationClient::%s parsed host " "architecture as %s, triple as %s from triple text %s", __FUNCTION__, m_host_arch.GetArchitectureName() ? m_host_arch.GetArchitectureName() : "", m_host_arch.GetTriple().getTriple().c_str(), triple.c_str()); } if (!distribution_id.empty()) m_host_arch.SetDistributionId(distribution_id.c_str()); } } } return m_qHostInfo_is_valid == eLazyBoolYes; } int GDBRemoteCommunicationClient::SendAttach( lldb::pid_t pid, StringExtractorGDBRemote &response) { if (pid != LLDB_INVALID_PROCESS_ID) { char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "vAttach;%" PRIx64, pid); UNUSED_IF_ASSERT_DISABLED(packet_len); assert(packet_len < (int)sizeof(packet)); if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsErrorResponse()) return response.GetError(); return 0; } } return -1; } int GDBRemoteCommunicationClient::SendStdinNotification(const char *data, size_t data_len) { StreamString packet; packet.PutCString("I"); packet.PutBytesAsRawHex8(data, data_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { return 0; } return response.GetError(); } const lldb_private::ArchSpec & GDBRemoteCommunicationClient::GetHostArchitecture() { if (m_qHostInfo_is_valid == eLazyBoolCalculate) GetHostInfo(); return m_host_arch; } seconds GDBRemoteCommunicationClient::GetHostDefaultPacketTimeout() { if (m_qHostInfo_is_valid == eLazyBoolCalculate) GetHostInfo(); return m_default_packet_timeout; } addr_t GDBRemoteCommunicationClient::AllocateMemory(size_t size, uint32_t permissions) { if (m_supports_alloc_dealloc_memory != eLazyBoolNo) { m_supports_alloc_dealloc_memory = eLazyBoolYes; char packet[64]; const int packet_len = ::snprintf( packet, sizeof(packet), "_M%" PRIx64 ",%s%s%s", (uint64_t)size, permissions & lldb::ePermissionsReadable ? "r" : "", permissions & lldb::ePermissionsWritable ? "w" : "", permissions & lldb::ePermissionsExecutable ? "x" : ""); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) m_supports_alloc_dealloc_memory = eLazyBoolNo; else if (!response.IsErrorResponse()) return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); } else { m_supports_alloc_dealloc_memory = eLazyBoolNo; } } return LLDB_INVALID_ADDRESS; } bool GDBRemoteCommunicationClient::DeallocateMemory(addr_t addr) { if (m_supports_alloc_dealloc_memory != eLazyBoolNo) { m_supports_alloc_dealloc_memory = eLazyBoolYes; char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "_m%" PRIx64, (uint64_t)addr); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) m_supports_alloc_dealloc_memory = eLazyBoolNo; else if (response.IsOKResponse()) return true; } else { m_supports_alloc_dealloc_memory = eLazyBoolNo; } } return false; } Error GDBRemoteCommunicationClient::Detach(bool keep_stopped) { Error error; if (keep_stopped) { if (m_supports_detach_stay_stopped == eLazyBoolCalculate) { char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "qSupportsDetachAndStayStopped:"); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success && response.IsOKResponse()) { m_supports_detach_stay_stopped = eLazyBoolYes; } else { m_supports_detach_stay_stopped = eLazyBoolNo; } } if (m_supports_detach_stay_stopped == eLazyBoolNo) { error.SetErrorString("Stays stopped not supported by this target."); return error; } else { StringExtractorGDBRemote response; PacketResult packet_result = SendPacketAndWaitForResponse("D1", response, false); if (packet_result != PacketResult::Success) error.SetErrorString("Sending extended disconnect packet failed."); } } else { StringExtractorGDBRemote response; PacketResult packet_result = SendPacketAndWaitForResponse("D", response, false); if (packet_result != PacketResult::Success) error.SetErrorString("Sending disconnect packet failed."); } return error; } Error GDBRemoteCommunicationClient::GetMemoryRegionInfo( lldb::addr_t addr, lldb_private::MemoryRegionInfo ®ion_info) { Error error; region_info.Clear(); if (m_supports_memory_region_info != eLazyBoolNo) { m_supports_memory_region_info = eLazyBoolYes; char packet[64]; const int packet_len = ::snprintf( packet, sizeof(packet), "qMemoryRegionInfo:%" PRIx64, (uint64_t)addr); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { llvm::StringRef name; llvm::StringRef value; addr_t addr_value = LLDB_INVALID_ADDRESS; bool success = true; bool saw_permissions = false; while (success && response.GetNameColonValue(name, value)) { if (name.equals("start")) { if (!value.getAsInteger(16, addr_value)) region_info.GetRange().SetRangeBase(addr_value); } else if (name.equals("size")) { if (!value.getAsInteger(16, addr_value)) region_info.GetRange().SetByteSize(addr_value); } else if (name.equals("permissions") && region_info.GetRange().IsValid()) { saw_permissions = true; if (region_info.GetRange().Contains(addr)) { if (value.find('r') != llvm::StringRef::npos) region_info.SetReadable(MemoryRegionInfo::eYes); else region_info.SetReadable(MemoryRegionInfo::eNo); if (value.find('w') != llvm::StringRef::npos) region_info.SetWritable(MemoryRegionInfo::eYes); else region_info.SetWritable(MemoryRegionInfo::eNo); if (value.find('x') != llvm::StringRef::npos) region_info.SetExecutable(MemoryRegionInfo::eYes); else region_info.SetExecutable(MemoryRegionInfo::eNo); region_info.SetMapped(MemoryRegionInfo::eYes); } else { // The reported region does not contain this address -- we're // looking at an unmapped page region_info.SetReadable(MemoryRegionInfo::eNo); region_info.SetWritable(MemoryRegionInfo::eNo); region_info.SetExecutable(MemoryRegionInfo::eNo); region_info.SetMapped(MemoryRegionInfo::eNo); } } else if (name.equals("name")) { StringExtractorGDBRemote name_extractor(value); std::string name; name_extractor.GetHexByteString(name); region_info.SetName(name.c_str()); } else if (name.equals("error")) { StringExtractorGDBRemote error_extractor(value); std::string error_string; // Now convert the HEX bytes into a string value error_extractor.GetHexByteString(error_string); error.SetErrorString(error_string.c_str()); } } // We got a valid address range back but no permissions -- which means // this is an unmapped page if (region_info.GetRange().IsValid() && saw_permissions == false) { region_info.SetReadable(MemoryRegionInfo::eNo); region_info.SetWritable(MemoryRegionInfo::eNo); region_info.SetExecutable(MemoryRegionInfo::eNo); region_info.SetMapped(MemoryRegionInfo::eNo); } } else { m_supports_memory_region_info = eLazyBoolNo; } } if (m_supports_memory_region_info == eLazyBoolNo) { error.SetErrorString("qMemoryRegionInfo is not supported"); } if (error.Fail()) region_info.Clear(); return error; } Error GDBRemoteCommunicationClient::GetWatchpointSupportInfo(uint32_t &num) { Error error; if (m_supports_watchpoint_support_info == eLazyBoolYes) { num = m_num_supported_hardware_watchpoints; return error; } // Set num to 0 first. num = 0; if (m_supports_watchpoint_support_info != eLazyBoolNo) { char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "qWatchpointSupportInfo:"); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { m_supports_watchpoint_support_info = eLazyBoolYes; llvm::StringRef name; llvm::StringRef value; while (response.GetNameColonValue(name, value)) { if (name.equals("num")) { value.getAsInteger(0, m_num_supported_hardware_watchpoints); num = m_num_supported_hardware_watchpoints; } } } else { m_supports_watchpoint_support_info = eLazyBoolNo; } } if (m_supports_watchpoint_support_info == eLazyBoolNo) { error.SetErrorString("qWatchpointSupportInfo is not supported"); } return error; } lldb_private::Error GDBRemoteCommunicationClient::GetWatchpointSupportInfo( uint32_t &num, bool &after, const ArchSpec &arch) { Error error(GetWatchpointSupportInfo(num)); if (error.Success()) error = GetWatchpointsTriggerAfterInstruction(after, arch); return error; } lldb_private::Error GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction( bool &after, const ArchSpec &arch) { Error error; llvm::Triple::ArchType atype = arch.GetMachine(); // we assume watchpoints will happen after running the relevant opcode // and we only want to override this behavior if we have explicitly // received a qHostInfo telling us otherwise if (m_qHostInfo_is_valid != eLazyBoolYes) { // On targets like MIPS, watchpoint exceptions are always generated // before the instruction is executed. The connected target may not // support qHostInfo or qWatchpointSupportInfo packets. if (atype == llvm::Triple::mips || atype == llvm::Triple::mipsel || atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el) after = false; else after = true; } else { // For MIPS, set m_watchpoints_trigger_after_instruction to eLazyBoolNo // if it is not calculated before. if (m_watchpoints_trigger_after_instruction == eLazyBoolCalculate && (atype == llvm::Triple::mips || atype == llvm::Triple::mipsel || atype == llvm::Triple::mips64 || atype == llvm::Triple::mips64el)) m_watchpoints_trigger_after_instruction = eLazyBoolNo; after = (m_watchpoints_trigger_after_instruction != eLazyBoolNo); } return error; } int GDBRemoteCommunicationClient::SetSTDIN(const FileSpec &file_spec) { if (file_spec) { std::string path{file_spec.GetPath(false)}; StreamString packet; packet.PutCString("QSetSTDIN:"); packet.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SetSTDOUT(const FileSpec &file_spec) { if (file_spec) { std::string path{file_spec.GetPath(false)}; StreamString packet; packet.PutCString("QSetSTDOUT:"); packet.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SetSTDERR(const FileSpec &file_spec) { if (file_spec) { std::string path{file_spec.GetPath(false)}; StreamString packet; packet.PutCString("QSetSTDERR:"); packet.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } bool GDBRemoteCommunicationClient::GetWorkingDir(FileSpec &working_dir) { StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qGetWorkingDir", response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) return false; if (response.IsErrorResponse()) return false; std::string cwd; response.GetHexByteString(cwd); working_dir.SetFile(cwd, false, GetHostArchitecture()); return !cwd.empty(); } return false; } int GDBRemoteCommunicationClient::SetWorkingDir(const FileSpec &working_dir) { if (working_dir) { std::string path{working_dir.GetPath(false)}; StreamString packet; packet.PutCString("QSetWorkingDir:"); packet.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } } return -1; } int GDBRemoteCommunicationClient::SetDisableASLR(bool enable) { char packet[32]; const int packet_len = ::snprintf(packet, sizeof(packet), "QSetDisableASLR:%i", enable ? 1 : 0); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } return -1; } int GDBRemoteCommunicationClient::SetDetachOnError(bool enable) { char packet[32]; const int packet_len = ::snprintf(packet, sizeof(packet), "QSetDetachOnError:%i", enable ? 1 : 0); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsOKResponse()) return 0; uint8_t error = response.GetError(); if (error) return error; } return -1; } bool GDBRemoteCommunicationClient::DecodeProcessInfoResponse( StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info) { if (response.IsNormalResponse()) { llvm::StringRef name; llvm::StringRef value; StringExtractor extractor; uint32_t cpu = LLDB_INVALID_CPUTYPE; uint32_t sub = 0; std::string vendor; std::string os_type; while (response.GetNameColonValue(name, value)) { if (name.equals("pid")) { lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; value.getAsInteger(0, pid); process_info.SetProcessID(pid); } else if (name.equals("ppid")) { lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; value.getAsInteger(0, pid); process_info.SetParentProcessID(pid); } else if (name.equals("uid")) { uint32_t uid = UINT32_MAX; value.getAsInteger(0, uid); process_info.SetUserID(uid); } else if (name.equals("euid")) { uint32_t uid = UINT32_MAX; value.getAsInteger(0, uid); process_info.SetEffectiveGroupID(uid); } else if (name.equals("gid")) { uint32_t gid = UINT32_MAX; value.getAsInteger(0, gid); process_info.SetGroupID(gid); } else if (name.equals("egid")) { uint32_t gid = UINT32_MAX; value.getAsInteger(0, gid); process_info.SetEffectiveGroupID(gid); } else if (name.equals("triple")) { StringExtractor extractor(value); std::string triple; extractor.GetHexByteString(triple); process_info.GetArchitecture().SetTriple(triple.c_str()); } else if (name.equals("name")) { StringExtractor extractor(value); // The process name from ASCII hex bytes since we can't // control the characters in a process name std::string name; extractor.GetHexByteString(name); process_info.GetExecutableFile().SetFile(name, false); } else if (name.equals("cputype")) { value.getAsInteger(0, cpu); } else if (name.equals("cpusubtype")) { value.getAsInteger(0, sub); } else if (name.equals("vendor")) { vendor = value; } else if (name.equals("ostype")) { os_type = value; } } if (cpu != LLDB_INVALID_CPUTYPE && !vendor.empty() && !os_type.empty()) { if (vendor == "apple") { process_info.GetArchitecture().SetArchitecture(eArchTypeMachO, cpu, sub); process_info.GetArchitecture().GetTriple().setVendorName( llvm::StringRef(vendor)); process_info.GetArchitecture().GetTriple().setOSName( llvm::StringRef(os_type)); } } if (process_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) return true; } return false; } bool GDBRemoteCommunicationClient::GetProcessInfo( lldb::pid_t pid, ProcessInstanceInfo &process_info) { process_info.Clear(); if (m_supports_qProcessInfoPID) { char packet[32]; const int packet_len = ::snprintf(packet, sizeof(packet), "qProcessInfoPID:%" PRIu64, pid); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { return DecodeProcessInfoResponse(response, process_info); } else { m_supports_qProcessInfoPID = false; return false; } } return false; } bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) { Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); if (allow_lazy) { if (m_qProcessInfo_is_valid == eLazyBoolYes) return true; if (m_qProcessInfo_is_valid == eLazyBoolNo) return false; } GetHostInfo(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qProcessInfo", response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { llvm::StringRef name; llvm::StringRef value; uint32_t cpu = LLDB_INVALID_CPUTYPE; uint32_t sub = 0; std::string arch_name; std::string os_name; std::string vendor_name; std::string triple; std::string elf_abi; uint32_t pointer_byte_size = 0; StringExtractor extractor; ByteOrder byte_order = eByteOrderInvalid; uint32_t num_keys_decoded = 0; lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; while (response.GetNameColonValue(name, value)) { if (name.equals("cputype")) { if (!value.getAsInteger(16, cpu)) ++num_keys_decoded; } else if (name.equals("cpusubtype")) { if (!value.getAsInteger(16, sub)) ++num_keys_decoded; } else if (name.equals("triple")) { StringExtractor extractor(value); extractor.GetHexByteString(triple); ++num_keys_decoded; } else if (name.equals("ostype")) { os_name = value; ++num_keys_decoded; } else if (name.equals("vendor")) { vendor_name = value; ++num_keys_decoded; } else if (name.equals("endian")) { byte_order = llvm::StringSwitch(value) .Case("little", eByteOrderLittle) .Case("big", eByteOrderBig) .Case("pdp", eByteOrderPDP) .Default(eByteOrderInvalid); if (byte_order != eByteOrderInvalid) ++num_keys_decoded; } else if (name.equals("ptrsize")) { if (!value.getAsInteger(16, pointer_byte_size)) ++num_keys_decoded; } else if (name.equals("pid")) { if (!value.getAsInteger(16, pid)) ++num_keys_decoded; } else if (name.equals("elf_abi")) { elf_abi = value; ++num_keys_decoded; } } if (num_keys_decoded > 0) m_qProcessInfo_is_valid = eLazyBoolYes; if (pid != LLDB_INVALID_PROCESS_ID) { m_curr_pid_is_valid = eLazyBoolYes; m_curr_pid = pid; } // Set the ArchSpec from the triple if we have it. if (!triple.empty()) { m_process_arch.SetTriple(triple.c_str()); m_process_arch.SetFlags(elf_abi); if (pointer_byte_size) { assert(pointer_byte_size == m_process_arch.GetAddressByteSize()); } } else if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() && !vendor_name.empty()) { llvm::Triple triple(llvm::Twine("-") + vendor_name + "-" + os_name); assert(triple.getObjectFormat() != llvm::Triple::UnknownObjectFormat); switch (triple.getObjectFormat()) { case llvm::Triple::MachO: m_process_arch.SetArchitecture(eArchTypeMachO, cpu, sub); break; case llvm::Triple::ELF: m_process_arch.SetArchitecture(eArchTypeELF, cpu, sub); break; case llvm::Triple::COFF: m_process_arch.SetArchitecture(eArchTypeCOFF, cpu, sub); break; case llvm::Triple::UnknownObjectFormat: if (log) log->Printf("error: failed to determine target architecture"); return false; } if (pointer_byte_size) { assert(pointer_byte_size == m_process_arch.GetAddressByteSize()); } if (byte_order != eByteOrderInvalid) { assert(byte_order == m_process_arch.GetByteOrder()); } m_process_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name)); m_process_arch.GetTriple().setOSName(llvm::StringRef(os_name)); m_host_arch.GetTriple().setVendorName(llvm::StringRef(vendor_name)); m_host_arch.GetTriple().setOSName(llvm::StringRef(os_name)); } return true; } } else { m_qProcessInfo_is_valid = eLazyBoolNo; } return false; } uint32_t GDBRemoteCommunicationClient::FindProcesses( const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { process_infos.Clear(); if (m_supports_qfProcessInfo) { StreamString packet; packet.PutCString("qfProcessInfo"); if (!match_info.MatchAllProcesses()) { packet.PutChar(':'); const char *name = match_info.GetProcessInfo().GetName(); bool has_name_match = false; if (name && name[0]) { has_name_match = true; NameMatchType name_match_type = match_info.GetNameMatchType(); switch (name_match_type) { case eNameMatchIgnore: has_name_match = false; break; case eNameMatchEquals: packet.PutCString("name_match:equals;"); break; case eNameMatchContains: packet.PutCString("name_match:contains;"); break; case eNameMatchStartsWith: packet.PutCString("name_match:starts_with;"); break; case eNameMatchEndsWith: packet.PutCString("name_match:ends_with;"); break; case eNameMatchRegularExpression: packet.PutCString("name_match:regex;"); break; } if (has_name_match) { packet.PutCString("name:"); packet.PutBytesAsRawHex8(name, ::strlen(name)); packet.PutChar(';'); } } if (match_info.GetProcessInfo().ProcessIDIsValid()) packet.Printf("pid:%" PRIu64 ";", match_info.GetProcessInfo().GetProcessID()); if (match_info.GetProcessInfo().ParentProcessIDIsValid()) packet.Printf("parent_pid:%" PRIu64 ";", match_info.GetProcessInfo().GetParentProcessID()); if (match_info.GetProcessInfo().UserIDIsValid()) packet.Printf("uid:%u;", match_info.GetProcessInfo().GetUserID()); if (match_info.GetProcessInfo().GroupIDIsValid()) packet.Printf("gid:%u;", match_info.GetProcessInfo().GetGroupID()); if (match_info.GetProcessInfo().EffectiveUserIDIsValid()) packet.Printf("euid:%u;", match_info.GetProcessInfo().GetEffectiveUserID()); if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) packet.Printf("egid:%u;", match_info.GetProcessInfo().GetEffectiveGroupID()); if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) packet.Printf("all_users:%u;", match_info.GetMatchAllUsers() ? 1 : 0); if (match_info.GetProcessInfo().GetArchitecture().IsValid()) { const ArchSpec &match_arch = match_info.GetProcessInfo().GetArchitecture(); const llvm::Triple &triple = match_arch.GetTriple(); packet.PutCString("triple:"); packet.PutCString(triple.getTriple()); packet.PutChar(';'); } } StringExtractorGDBRemote response; // Increase timeout as the first qfProcessInfo packet takes a long time // on Android. The value of 1min was arrived at empirically. ScopedTimeout timeout(*this, minutes(1)); if (SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success) { do { ProcessInstanceInfo process_info; if (!DecodeProcessInfoResponse(response, process_info)) break; process_infos.Append(process_info); response.GetStringRef().clear(); response.SetFilePos(0); } while (SendPacketAndWaitForResponse("qsProcessInfo", response, false) == PacketResult::Success); } else { m_supports_qfProcessInfo = false; return 0; } } return process_infos.GetSize(); } bool GDBRemoteCommunicationClient::GetUserName(uint32_t uid, std::string &name) { if (m_supports_qUserName) { char packet[32]; const int packet_len = ::snprintf(packet, sizeof(packet), "qUserName:%i", uid); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { // Make sure we parsed the right number of characters. The response is // the hex encoded user name and should make up the entire packet. // If there are any non-hex ASCII bytes, the length won't match below.. if (response.GetHexByteString(name) * 2 == response.GetStringRef().size()) return true; } } else { m_supports_qUserName = false; return false; } } return false; } bool GDBRemoteCommunicationClient::GetGroupName(uint32_t gid, std::string &name) { if (m_supports_qGroupName) { char packet[32]; const int packet_len = ::snprintf(packet, sizeof(packet), "qGroupName:%i", gid); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsNormalResponse()) { // Make sure we parsed the right number of characters. The response is // the hex encoded group name and should make up the entire packet. // If there are any non-hex ASCII bytes, the length won't match below.. if (response.GetHexByteString(name) * 2 == response.GetStringRef().size()) return true; } } else { m_supports_qGroupName = false; return false; } } return false; } bool GDBRemoteCommunicationClient::SetNonStopMode(const bool enable) { // Form non-stop packet request char packet[32]; const int packet_len = ::snprintf(packet, sizeof(packet), "QNonStop:%1d", (int)enable); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; // Send to target if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) if (response.IsOKResponse()) return true; // Failed or not supported return false; } static void MakeSpeedTestPacket(StreamString &packet, uint32_t send_size, uint32_t recv_size) { packet.Clear(); packet.Printf("qSpeedTest:response_size:%i;data:", recv_size); uint32_t bytes_left = send_size; while (bytes_left > 0) { if (bytes_left >= 26) { packet.PutCString("abcdefghijklmnopqrstuvwxyz"); bytes_left -= 26; } else { packet.Printf("%*.*s;", bytes_left, bytes_left, "abcdefghijklmnopqrstuvwxyz"); bytes_left = 0; } } } duration calculate_standard_deviation(const std::vector> &v) { using Dur = duration; Dur sum = std::accumulate(std::begin(v), std::end(v), Dur()); Dur mean = sum / v.size(); float accum = 0; for (auto d : v) { float delta = (d - mean).count(); accum += delta * delta; }; return Dur(sqrtf(accum / (v.size() - 1))); } void GDBRemoteCommunicationClient::TestPacketSpeed(const uint32_t num_packets, uint32_t max_send, uint32_t max_recv, uint64_t recv_amount, bool json, Stream &strm) { uint32_t i; if (SendSpeedTestPacket(0, 0)) { StreamString packet; if (json) strm.Printf("{ \"packet_speeds\" : {\n \"num_packets\" : %u,\n " "\"results\" : [", num_packets); else strm.Printf("Testing sending %u packets of various sizes:\n", num_packets); strm.Flush(); uint32_t result_idx = 0; uint32_t send_size; std::vector> packet_times; for (send_size = 0; send_size <= max_send; send_size ? send_size *= 2 : send_size = 4) { for (uint32_t recv_size = 0; recv_size <= max_recv; recv_size ? recv_size *= 2 : recv_size = 4) { MakeSpeedTestPacket(packet, send_size, recv_size); packet_times.clear(); // Test how long it takes to send 'num_packets' packets const auto start_time = steady_clock::now(); for (i = 0; i < num_packets; ++i) { const auto packet_start_time = steady_clock::now(); StringExtractorGDBRemote response; SendPacketAndWaitForResponse(packet.GetString(), response, false); const auto packet_end_time = steady_clock::now(); packet_times.push_back(packet_end_time - packet_start_time); } const auto end_time = steady_clock::now(); const auto total_time = end_time - start_time; float packets_per_second = ((float)num_packets) / duration(total_time).count(); auto average_per_packet = total_time / num_packets; const duration standard_deviation = calculate_standard_deviation(packet_times); if (json) { strm.Printf("%s\n {\"send_size\" : %6" PRIu32 ", \"recv_size\" : %6" PRIu32 ", \"total_time_nsec\" : %12" PRIu64 ", \"standard_deviation_nsec\" : %9" PRIu64 " }", result_idx > 0 ? "," : "", send_size, recv_size, duration_cast(total_time).count(), duration_cast(standard_deviation).count()); ++result_idx; } else { strm.Printf( "qSpeedTest(send=%-7u, recv=%-7u) in %.9f" " sec for %9.2f packets/sec (%10.6f ms per packet) with standard " "deviation of %10.6f ms\n", send_size, recv_size, duration(total_time).count(), packets_per_second, duration(average_per_packet).count(), duration(standard_deviation).count()); } strm.Flush(); } } const float k_recv_amount_mb = (float)recv_amount / (1024.0f * 1024.0f); if (json) strm.Printf("\n ]\n },\n \"download_speed\" : {\n \"byte_size\" " ": %" PRIu64 ",\n \"results\" : [", recv_amount); else strm.Printf("Testing receiving %2.1fMB of data using varying receive " "packet sizes:\n", k_recv_amount_mb); strm.Flush(); send_size = 0; result_idx = 0; for (uint32_t recv_size = 32; recv_size <= max_recv; recv_size *= 2) { MakeSpeedTestPacket(packet, send_size, recv_size); // If we have a receive size, test how long it takes to receive 4MB of // data if (recv_size > 0) { const auto start_time = steady_clock::now(); uint32_t bytes_read = 0; uint32_t packet_count = 0; while (bytes_read < recv_amount) { StringExtractorGDBRemote response; SendPacketAndWaitForResponse(packet.GetString(), response, false); bytes_read += recv_size; ++packet_count; } const auto end_time = steady_clock::now(); const auto total_time = end_time - start_time; float mb_second = ((float)recv_amount) / duration(total_time).count() / (1024.0 * 1024.0); float packets_per_second = ((float)packet_count) / duration(total_time).count(); const auto average_per_packet = total_time / packet_count; if (json) { strm.Printf("%s\n {\"send_size\" : %6" PRIu32 ", \"recv_size\" : %6" PRIu32 ", \"total_time_nsec\" : %12" PRIu64 " }", result_idx > 0 ? "," : "", send_size, recv_size, duration_cast(total_time).count()); ++result_idx; } else { strm.Printf("qSpeedTest(send=%-7u, recv=%-7u) %6u packets needed to " "receive %2.1fMB in %.9f" " sec for %f MB/sec for %9.2f packets/sec (%10.6f ms per " "packet)\n", send_size, recv_size, packet_count, k_recv_amount_mb, duration(total_time).count(), mb_second, packets_per_second, duration(average_per_packet).count()); } strm.Flush(); } } if (json) strm.Printf("\n ]\n }\n}\n"); else strm.EOL(); } } bool GDBRemoteCommunicationClient::SendSpeedTestPacket(uint32_t send_size, uint32_t recv_size) { StreamString packet; packet.Printf("qSpeedTest:response_size:%i;data:", recv_size); uint32_t bytes_left = send_size; while (bytes_left > 0) { if (bytes_left >= 26) { packet.PutCString("abcdefghijklmnopqrstuvwxyz"); bytes_left -= 26; } else { packet.Printf("%*.*s;", bytes_left, bytes_left, "abcdefghijklmnopqrstuvwxyz"); bytes_left = 0; } } StringExtractorGDBRemote response; return SendPacketAndWaitForResponse(packet.GetString(), response, false) == PacketResult::Success; } bool GDBRemoteCommunicationClient::LaunchGDBServer( const char *remote_accept_hostname, lldb::pid_t &pid, uint16_t &port, std::string &socket_name) { pid = LLDB_INVALID_PROCESS_ID; port = 0; socket_name.clear(); StringExtractorGDBRemote response; StreamString stream; stream.PutCString("qLaunchGDBServer;"); std::string hostname; if (remote_accept_hostname && remote_accept_hostname[0]) hostname = remote_accept_hostname; else { if (HostInfo::GetHostname(hostname)) { // Make the GDB server we launch only accept connections from this host stream.Printf("host:%s;", hostname.c_str()); } else { // Make the GDB server we launch accept connections from any host since we // can't figure out the hostname stream.Printf("host:*;"); } } // give the process a few seconds to startup ScopedTimeout timeout(*this, seconds(10)); if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { llvm::StringRef name; llvm::StringRef value; while (response.GetNameColonValue(name, value)) { if (name.equals("port")) value.getAsInteger(0, port); else if (name.equals("pid")) value.getAsInteger(0, pid); else if (name.compare("socket_name") == 0) { StringExtractor extractor(value); extractor.GetHexByteString(socket_name); } } return true; } return false; } size_t GDBRemoteCommunicationClient::QueryGDBServer( std::vector> &connection_urls) { connection_urls.clear(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qQueryGDBServer", response, false) != PacketResult::Success) return 0; StructuredData::ObjectSP data = StructuredData::ParseJSON(response.GetStringRef()); if (!data) return 0; StructuredData::Array *array = data->GetAsArray(); if (!array) return 0; for (size_t i = 0, count = array->GetSize(); i < count; ++i) { StructuredData::Dictionary *element = nullptr; if (!array->GetItemAtIndexAsDictionary(i, element)) continue; uint16_t port = 0; if (StructuredData::ObjectSP port_osp = element->GetValueForKey(llvm::StringRef("port"))) port = port_osp->GetIntegerValue(0); std::string socket_name; if (StructuredData::ObjectSP socket_name_osp = element->GetValueForKey(llvm::StringRef("socket_name"))) socket_name = socket_name_osp->GetStringValue(); if (port != 0 || !socket_name.empty()) connection_urls.emplace_back(port, socket_name); } return connection_urls.size(); } bool GDBRemoteCommunicationClient::KillSpawnedProcess(lldb::pid_t pid) { StreamString stream; stream.Printf("qKillSpawnedProcess:%" PRId64, pid); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.IsOKResponse()) return true; } return false; } bool GDBRemoteCommunicationClient::SetCurrentThread(uint64_t tid) { if (m_curr_tid == tid) return true; char packet[32]; int packet_len; if (tid == UINT64_MAX) packet_len = ::snprintf(packet, sizeof(packet), "Hg-1"); else packet_len = ::snprintf(packet, sizeof(packet), "Hg%" PRIx64, tid); assert(packet_len + 1 < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_curr_tid = tid; return true; } /* * Connected bare-iron target (like YAMON gdb-stub) may not have support for * Hg packet. * The reply from '?' packet could be as simple as 'S05'. There is no packet * which can * give us pid and/or tid. Assume pid=tid=1 in such cases. */ if (response.IsUnsupportedResponse() && IsConnected()) { m_curr_tid = 1; return true; } } return false; } bool GDBRemoteCommunicationClient::SetCurrentThreadForRun(uint64_t tid) { if (m_curr_tid_run == tid) return true; char packet[32]; int packet_len; if (tid == UINT64_MAX) packet_len = ::snprintf(packet, sizeof(packet), "Hc-1"); else packet_len = ::snprintf(packet, sizeof(packet), "Hc%" PRIx64, tid); assert(packet_len + 1 < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsOKResponse()) { m_curr_tid_run = tid; return true; } /* * Connected bare-iron target (like YAMON gdb-stub) may not have support for * Hc packet. * The reply from '?' packet could be as simple as 'S05'. There is no packet * which can * give us pid and/or tid. Assume pid=tid=1 in such cases. */ if (response.IsUnsupportedResponse() && IsConnected()) { m_curr_tid_run = 1; return true; } } return false; } bool GDBRemoteCommunicationClient::GetStopReply( StringExtractorGDBRemote &response) { if (SendPacketAndWaitForResponse("?", response, false) == PacketResult::Success) return response.IsNormalResponse(); return false; } bool GDBRemoteCommunicationClient::GetThreadStopInfo( lldb::tid_t tid, StringExtractorGDBRemote &response) { if (m_supports_qThreadStopInfo) { char packet[256]; int packet_len = ::snprintf(packet, sizeof(packet), "qThreadStopInfo%" PRIx64, tid); assert(packet_len < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); if (SendPacketAndWaitForResponse(packet, response, false) == PacketResult::Success) { if (response.IsUnsupportedResponse()) m_supports_qThreadStopInfo = false; else if (response.IsNormalResponse()) return true; else return false; } else { m_supports_qThreadStopInfo = false; } } return false; } uint8_t GDBRemoteCommunicationClient::SendGDBStoppointTypePacket( GDBStoppointType type, bool insert, addr_t addr, uint32_t length) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf("GDBRemoteCommunicationClient::%s() %s at addr = 0x%" PRIx64, __FUNCTION__, insert ? "add" : "remove", addr); // Check if the stub is known not to support this breakpoint type if (!SupportsGDBStoppointPacket(type)) return UINT8_MAX; // Construct the breakpoint packet char packet[64]; const int packet_len = ::snprintf(packet, sizeof(packet), "%c%i,%" PRIx64 ",%x", insert ? 'Z' : 'z', type, addr, length); // Check we haven't overwritten the end of the packet buffer assert(packet_len + 1 < (int)sizeof(packet)); UNUSED_IF_ASSERT_DISABLED(packet_len); StringExtractorGDBRemote response; // Make sure the response is either "OK", "EXX" where XX are two hex digits, // or "" (unsupported) response.SetResponseValidatorToOKErrorNotSupported(); // Try to send the breakpoint packet, and check that it was correctly sent if (SendPacketAndWaitForResponse(packet, response, true) == PacketResult::Success) { // Receive and OK packet when the breakpoint successfully placed if (response.IsOKResponse()) return 0; // Error while setting breakpoint, send back specific error if (response.IsErrorResponse()) return response.GetError(); // Empty packet informs us that breakpoint is not supported if (response.IsUnsupportedResponse()) { // Disable this breakpoint type since it is unsupported switch (type) { case eBreakpointSoftware: m_supports_z0 = false; break; case eBreakpointHardware: m_supports_z1 = false; break; case eWatchpointWrite: m_supports_z2 = false; break; case eWatchpointRead: m_supports_z3 = false; break; case eWatchpointReadWrite: m_supports_z4 = false; break; case eStoppointInvalid: return UINT8_MAX; } } } // Signal generic failure return UINT8_MAX; } size_t GDBRemoteCommunicationClient::GetCurrentThreadIDs( std::vector &thread_ids, bool &sequence_mutex_unavailable) { thread_ids.clear(); Lock lock(*this, false); if (lock) { sequence_mutex_unavailable = false; StringExtractorGDBRemote response; PacketResult packet_result; for (packet_result = SendPacketAndWaitForResponseNoLock("qfThreadInfo", response); packet_result == PacketResult::Success && response.IsNormalResponse(); packet_result = SendPacketAndWaitForResponseNoLock("qsThreadInfo", response)) { char ch = response.GetChar(); if (ch == 'l') break; if (ch == 'm') { do { tid_t tid = response.GetHexMaxU64(false, LLDB_INVALID_THREAD_ID); if (tid != LLDB_INVALID_THREAD_ID) { thread_ids.push_back(tid); } ch = response.GetChar(); // Skip the command separator } while (ch == ','); // Make sure we got a comma separator } } /* * Connected bare-iron target (like YAMON gdb-stub) may not have support for * qProcessInfo, qC and qfThreadInfo packets. The reply from '?' packet * could * be as simple as 'S05'. There is no packet which can give us pid and/or * tid. * Assume pid=tid=1 in such cases. */ if (response.IsUnsupportedResponse() && thread_ids.size() == 0 && IsConnected()) { thread_ids.push_back(1); } } else { -#if defined(LLDB_CONFIGURATION_DEBUG) -// assert(!"ProcessGDBRemote::UpdateThreadList() failed due to not getting the -// sequence mutex"); -#else +#if !defined(LLDB_CONFIGURATION_DEBUG) Log *log(ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); if (log) log->Printf("error: failed to get packet sequence mutex, not sending " "packet 'qfThreadInfo'"); #endif sequence_mutex_unavailable = true; } return thread_ids.size(); } lldb::addr_t GDBRemoteCommunicationClient::GetShlibInfoAddr() { StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse("qShlibInfoAddr", response, false) != PacketResult::Success || !response.IsNormalResponse()) return LLDB_INVALID_ADDRESS; return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); } lldb_private::Error GDBRemoteCommunicationClient::RunShellCommand( const char *command, // Shouldn't be NULL const FileSpec & working_dir, // Pass empty FileSpec to use the current working directory int *status_ptr, // Pass NULL if you don't want the process exit status int *signo_ptr, // Pass NULL if you don't want the signal that caused the // process to exit std::string *command_output, // Pass NULL if you don't want the command output uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish { lldb_private::StreamString stream; stream.PutCString("qPlatform_shell:"); stream.PutBytesAsRawHex8(command, strlen(command)); stream.PutChar(','); stream.PutHex32(timeout_sec); if (working_dir) { std::string path{working_dir.GetPath(false)}; stream.PutChar(','); stream.PutCStringAsRawHex8(path.c_str()); } StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return Error("malformed reply"); if (response.GetChar() != ',') return Error("malformed reply"); uint32_t exitcode = response.GetHexMaxU32(false, UINT32_MAX); if (exitcode == UINT32_MAX) return Error("unable to run remote process"); else if (status_ptr) *status_ptr = exitcode; if (response.GetChar() != ',') return Error("malformed reply"); uint32_t signo = response.GetHexMaxU32(false, UINT32_MAX); if (signo_ptr) *signo_ptr = signo; if (response.GetChar() != ',') return Error("malformed reply"); std::string output; response.GetEscapedBinaryData(output); if (command_output) command_output->assign(output); return Error(); } return Error("unable to send packet"); } Error GDBRemoteCommunicationClient::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions) { std::string path{file_spec.GetPath(false)}; lldb_private::StreamString stream; stream.PutCString("qPlatform_mkdir:"); stream.PutHex32(file_permissions); stream.PutChar(','); stream.PutCStringAsRawHex8(path.c_str()); llvm::StringRef packet = stream.GetString(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) != PacketResult::Success) return Error("failed to send '%s' packet", packet.str().c_str()); if (response.GetChar() != 'F') return Error("invalid response to '%s' packet", packet.str().c_str()); return Error(response.GetU32(UINT32_MAX), eErrorTypePOSIX); } Error GDBRemoteCommunicationClient::SetFilePermissions( const FileSpec &file_spec, uint32_t file_permissions) { std::string path{file_spec.GetPath(false)}; lldb_private::StreamString stream; stream.PutCString("qPlatform_chmod:"); stream.PutHex32(file_permissions); stream.PutChar(','); stream.PutCStringAsRawHex8(path.c_str()); llvm::StringRef packet = stream.GetString(); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet, response, false) != PacketResult::Success) return Error("failed to send '%s' packet", stream.GetData()); if (response.GetChar() != 'F') return Error("invalid response to '%s' packet", stream.GetData()); return Error(response.GetU32(UINT32_MAX), eErrorTypePOSIX); } static uint64_t ParseHostIOPacketResponse(StringExtractorGDBRemote &response, uint64_t fail_result, Error &error) { response.SetFilePos(0); if (response.GetChar() != 'F') return fail_result; int32_t result = response.GetS32(-2); if (result == -2) return fail_result; if (response.GetChar() == ',') { int result_errno = response.GetS32(-2); if (result_errno != -2) error.SetError(result_errno, eErrorTypePOSIX); else error.SetError(-1, eErrorTypeGeneric); } else error.Clear(); return result; } lldb::user_id_t GDBRemoteCommunicationClient::OpenFile(const lldb_private::FileSpec &file_spec, uint32_t flags, mode_t mode, Error &error) { std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:open:"); if (path.empty()) return UINT64_MAX; stream.PutCStringAsRawHex8(path.c_str()); stream.PutChar(','); stream.PutHex32(flags); stream.PutChar(','); stream.PutHex32(mode); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { return ParseHostIOPacketResponse(response, UINT64_MAX, error); } return UINT64_MAX; } bool GDBRemoteCommunicationClient::CloseFile(lldb::user_id_t fd, Error &error) { lldb_private::StreamString stream; stream.Printf("vFile:close:%i", (int)fd); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { return ParseHostIOPacketResponse(response, -1, error) == 0; } return false; } // Extension of host I/O packets to get the file size. lldb::user_id_t GDBRemoteCommunicationClient::GetFileSize( const lldb_private::FileSpec &file_spec) { std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:size:"); stream.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return UINT64_MAX; uint32_t retcode = response.GetHexMaxU64(false, UINT64_MAX); return retcode; } return UINT64_MAX; } Error GDBRemoteCommunicationClient::GetFilePermissions( const FileSpec &file_spec, uint32_t &file_permissions) { std::string path{file_spec.GetPath(false)}; Error error; lldb_private::StreamString stream; stream.PutCString("vFile:mode:"); stream.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') { error.SetErrorStringWithFormat("invalid response to '%s' packet", stream.GetData()); } else { const uint32_t mode = response.GetS32(-1); if (static_cast(mode) == -1) { if (response.GetChar() == ',') { int response_errno = response.GetS32(-1); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); else error.SetErrorToGenericError(); } else error.SetErrorToGenericError(); } else { file_permissions = mode & (S_IRWXU | S_IRWXG | S_IRWXO); } } } else { error.SetErrorStringWithFormat("failed to send '%s' packet", stream.GetData()); } return error; } uint64_t GDBRemoteCommunicationClient::ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, Error &error) { lldb_private::StreamString stream; stream.Printf("vFile:pread:%i,%" PRId64 ",%" PRId64, (int)fd, dst_len, offset); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return 0; uint32_t retcode = response.GetHexMaxU32(false, UINT32_MAX); if (retcode == UINT32_MAX) return retcode; const char next = (response.Peek() ? *response.Peek() : 0); if (next == ',') return 0; if (next == ';') { response.GetChar(); // skip the semicolon std::string buffer; if (response.GetEscapedBinaryData(buffer)) { const uint64_t data_to_write = std::min(dst_len, buffer.size()); if (data_to_write > 0) memcpy(dst, &buffer[0], data_to_write); return data_to_write; } } } return 0; } uint64_t GDBRemoteCommunicationClient::WriteFile(lldb::user_id_t fd, uint64_t offset, const void *src, uint64_t src_len, Error &error) { lldb_private::StreamGDBRemote stream; stream.Printf("vFile:pwrite:%i,%" PRId64 ",", (int)fd, offset); stream.PutEscapedBytes(src, src_len); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') { error.SetErrorStringWithFormat("write file failed"); return 0; } uint64_t bytes_written = response.GetU64(UINT64_MAX); if (bytes_written == UINT64_MAX) { error.SetErrorToGenericError(); if (response.GetChar() == ',') { int response_errno = response.GetS32(-1); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); } return 0; } return bytes_written; } else { error.SetErrorString("failed to send vFile:pwrite packet"); } return 0; } Error GDBRemoteCommunicationClient::CreateSymlink(const FileSpec &src, const FileSpec &dst) { std::string src_path{src.GetPath(false)}, dst_path{dst.GetPath(false)}; Error error; lldb_private::StreamGDBRemote stream; stream.PutCString("vFile:symlink:"); // the unix symlink() command reverses its parameters where the dst if first, // so we follow suit here stream.PutCStringAsRawHex8(dst_path.c_str()); stream.PutChar(','); stream.PutCStringAsRawHex8(src_path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() == 'F') { uint32_t result = response.GetU32(UINT32_MAX); if (result != 0) { error.SetErrorToGenericError(); if (response.GetChar() == ',') { int response_errno = response.GetS32(-1); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); } } } else { // Should have returned with 'F[,]' error.SetErrorStringWithFormat("symlink failed"); } } else { error.SetErrorString("failed to send vFile:symlink packet"); } return error; } Error GDBRemoteCommunicationClient::Unlink(const FileSpec &file_spec) { std::string path{file_spec.GetPath(false)}; Error error; lldb_private::StreamGDBRemote stream; stream.PutCString("vFile:unlink:"); // the unix symlink() command reverses its parameters where the dst if first, // so we follow suit here stream.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() == 'F') { uint32_t result = response.GetU32(UINT32_MAX); if (result != 0) { error.SetErrorToGenericError(); if (response.GetChar() == ',') { int response_errno = response.GetS32(-1); if (response_errno > 0) error.SetError(response_errno, lldb::eErrorTypePOSIX); } } } else { // Should have returned with 'F[,]' error.SetErrorStringWithFormat("unlink failed"); } } else { error.SetErrorString("failed to send vFile:unlink packet"); } return error; } // Extension of host I/O packets to get whether a file exists. bool GDBRemoteCommunicationClient::GetFileExists( const lldb_private::FileSpec &file_spec) { std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:exists:"); stream.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return false; if (response.GetChar() != ',') return false; bool retcode = (response.GetChar() != '0'); return retcode; } return false; } bool GDBRemoteCommunicationClient::CalculateMD5( const lldb_private::FileSpec &file_spec, uint64_t &high, uint64_t &low) { std::string path(file_spec.GetPath(false)); lldb_private::StreamString stream; stream.PutCString("vFile:MD5:"); stream.PutCStringAsRawHex8(path.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == PacketResult::Success) { if (response.GetChar() != 'F') return false; if (response.GetChar() != ',') return false; if (response.Peek() && *response.Peek() == 'x') return false; low = response.GetHexMaxU64(false, UINT64_MAX); high = response.GetHexMaxU64(false, UINT64_MAX); return true; } return false; } bool GDBRemoteCommunicationClient::AvoidGPackets(ProcessGDBRemote *process) { // Some targets have issues with g/G packets and we need to avoid using them if (m_avoid_g_packets == eLazyBoolCalculate) { if (process) { m_avoid_g_packets = eLazyBoolNo; const ArchSpec &arch = process->GetTarget().GetArchitecture(); if (arch.IsValid() && arch.GetTriple().getVendor() == llvm::Triple::Apple && arch.GetTriple().getOS() == llvm::Triple::IOS && arch.GetTriple().getArch() == llvm::Triple::aarch64) { m_avoid_g_packets = eLazyBoolYes; uint32_t gdb_server_version = GetGDBServerProgramVersion(); if (gdb_server_version != 0) { const char *gdb_server_name = GetGDBServerProgramName(); if (gdb_server_name && strcmp(gdb_server_name, "debugserver") == 0) { if (gdb_server_version >= 310) m_avoid_g_packets = eLazyBoolNo; } } } } } return m_avoid_g_packets == eLazyBoolYes; } DataBufferSP GDBRemoteCommunicationClient::ReadRegister(lldb::tid_t tid, uint32_t reg) { StreamString payload; payload.Printf("p%x", reg); StringExtractorGDBRemote response; if (SendThreadSpecificPacketAndWaitForResponse( tid, std::move(payload), response, false) != PacketResult::Success || !response.IsNormalResponse()) return nullptr; DataBufferSP buffer_sp( new DataBufferHeap(response.GetStringRef().size() / 2, 0)); response.GetHexBytes(buffer_sp->GetData(), '\xcc'); return buffer_sp; } DataBufferSP GDBRemoteCommunicationClient::ReadAllRegisters(lldb::tid_t tid) { StreamString payload; payload.PutChar('g'); StringExtractorGDBRemote response; if (SendThreadSpecificPacketAndWaitForResponse( tid, std::move(payload), response, false) != PacketResult::Success || !response.IsNormalResponse()) return nullptr; DataBufferSP buffer_sp( new DataBufferHeap(response.GetStringRef().size() / 2, 0)); response.GetHexBytes(buffer_sp->GetData(), '\xcc'); return buffer_sp; } bool GDBRemoteCommunicationClient::WriteRegister(lldb::tid_t tid, uint32_t reg_num, llvm::ArrayRef data) { StreamString payload; payload.Printf("P%x=", reg_num); payload.PutBytesAsRawHex8(data.data(), data.size(), endian::InlHostByteOrder(), endian::InlHostByteOrder()); StringExtractorGDBRemote response; return SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload), response, false) == PacketResult::Success && response.IsOKResponse(); } bool GDBRemoteCommunicationClient::WriteAllRegisters( lldb::tid_t tid, llvm::ArrayRef data) { StreamString payload; payload.PutChar('G'); payload.PutBytesAsRawHex8(data.data(), data.size(), endian::InlHostByteOrder(), endian::InlHostByteOrder()); StringExtractorGDBRemote response; return SendThreadSpecificPacketAndWaitForResponse(tid, std::move(payload), response, false) == PacketResult::Success && response.IsOKResponse(); } bool GDBRemoteCommunicationClient::SaveRegisterState(lldb::tid_t tid, uint32_t &save_id) { save_id = 0; // Set to invalid save ID if (m_supports_QSaveRegisterState == eLazyBoolNo) return false; m_supports_QSaveRegisterState = eLazyBoolYes; StreamString payload; payload.PutCString("QSaveRegisterState"); StringExtractorGDBRemote response; if (SendThreadSpecificPacketAndWaitForResponse( tid, std::move(payload), response, false) != PacketResult::Success) return false; if (response.IsUnsupportedResponse()) m_supports_QSaveRegisterState = eLazyBoolNo; const uint32_t response_save_id = response.GetU32(0); if (response_save_id == 0) return false; save_id = response_save_id; return true; } bool GDBRemoteCommunicationClient::RestoreRegisterState(lldb::tid_t tid, uint32_t save_id) { // We use the "m_supports_QSaveRegisterState" variable here because the // QSaveRegisterState and QRestoreRegisterState packets must both be supported // in // order to be useful if (m_supports_QSaveRegisterState == eLazyBoolNo) return false; StreamString payload; payload.Printf("QRestoreRegisterState:%u", save_id); StringExtractorGDBRemote response; if (SendThreadSpecificPacketAndWaitForResponse( tid, std::move(payload), response, false) != PacketResult::Success) return false; if (response.IsOKResponse()) return true; if (response.IsUnsupportedResponse()) m_supports_QSaveRegisterState = eLazyBoolNo; return false; } bool GDBRemoteCommunicationClient::SyncThreadState(lldb::tid_t tid) { if (!GetSyncThreadStateSupported()) return false; StreamString packet; StringExtractorGDBRemote response; packet.Printf("QSyncThreadState:%4.4" PRIx64 ";", tid); return SendPacketAndWaitForResponse(packet.GetString(), response, false) == GDBRemoteCommunication::PacketResult::Success && response.IsOKResponse(); } bool GDBRemoteCommunicationClient::GetModuleInfo( const FileSpec &module_file_spec, const lldb_private::ArchSpec &arch_spec, ModuleSpec &module_spec) { if (!m_supports_qModuleInfo) return false; std::string module_path = module_file_spec.GetPath(false); if (module_path.empty()) return false; StreamString packet; packet.PutCString("qModuleInfo:"); packet.PutCStringAsRawHex8(module_path.c_str()); packet.PutCString(";"); const auto &triple = arch_spec.GetTriple().getTriple(); packet.PutCStringAsRawHex8(triple.c_str()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(packet.GetString(), response, false) != PacketResult::Success) return false; if (response.IsErrorResponse()) return false; if (response.IsUnsupportedResponse()) { m_supports_qModuleInfo = false; return false; } llvm::StringRef name; llvm::StringRef value; module_spec.Clear(); module_spec.GetFileSpec() = module_file_spec; while (response.GetNameColonValue(name, value)) { if (name == "uuid" || name == "md5") { StringExtractor extractor(value); std::string uuid; extractor.GetHexByteString(uuid); module_spec.GetUUID().SetFromCString(uuid.c_str(), uuid.size() / 2); } else if (name == "triple") { StringExtractor extractor(value); std::string triple; extractor.GetHexByteString(triple); module_spec.GetArchitecture().SetTriple(triple.c_str()); } else if (name == "file_offset") { uint64_t ival = 0; if (!value.getAsInteger(16, ival)) module_spec.SetObjectOffset(ival); } else if (name == "file_size") { uint64_t ival = 0; if (!value.getAsInteger(16, ival)) module_spec.SetObjectSize(ival); } else if (name == "file_path") { StringExtractor extractor(value); std::string path; extractor.GetHexByteString(path); module_spec.GetFileSpec() = FileSpec(path, false, arch_spec); } } return true; } static llvm::Optional ParseModuleSpec(StructuredData::Dictionary *dict) { ModuleSpec result; if (!dict) return llvm::None; std::string string; uint64_t integer; if (!dict->GetValueForKeyAsString("uuid", string)) return llvm::None; result.GetUUID().SetFromCString(string.c_str(), string.size()); if (!dict->GetValueForKeyAsInteger("file_offset", integer)) return llvm::None; result.SetObjectOffset(integer); if (!dict->GetValueForKeyAsInteger("file_size", integer)) return llvm::None; result.SetObjectSize(integer); if (!dict->GetValueForKeyAsString("triple", string)) return llvm::None; result.GetArchitecture().SetTriple(string.c_str()); if (!dict->GetValueForKeyAsString("file_path", string)) return llvm::None; result.GetFileSpec() = FileSpec(string, false, result.GetArchitecture()); return result; } llvm::Optional> GDBRemoteCommunicationClient::GetModulesInfo( llvm::ArrayRef module_file_specs, const llvm::Triple &triple) { if (!m_supports_jModulesInfo) return llvm::None; JSONArray::SP module_array_sp = std::make_shared(); for (const FileSpec &module_file_spec : module_file_specs) { JSONObject::SP module_sp = std::make_shared(); module_array_sp->AppendObject(module_sp); module_sp->SetObject( - "file", std::make_shared(module_file_spec.GetPath())); + "file", std::make_shared(module_file_spec.GetPath(false))); module_sp->SetObject("triple", std::make_shared(triple.getTriple())); } StreamString unescaped_payload; unescaped_payload.PutCString("jModulesInfo:"); module_array_sp->Write(unescaped_payload); StreamGDBRemote payload; payload.PutEscapedBytes(unescaped_payload.GetString().data(), unescaped_payload.GetSize()); StringExtractorGDBRemote response; if (SendPacketAndWaitForResponse(payload.GetString(), response, false) != PacketResult::Success || response.IsErrorResponse()) return llvm::None; if (response.IsUnsupportedResponse()) { m_supports_jModulesInfo = false; return llvm::None; } StructuredData::ObjectSP response_object_sp = StructuredData::ParseJSON(response.GetStringRef()); if (!response_object_sp) return llvm::None; StructuredData::Array *response_array = response_object_sp->GetAsArray(); if (!response_array) return llvm::None; std::vector result; for (size_t i = 0; i < response_array->GetSize(); ++i) { if (llvm::Optional module_spec = ParseModuleSpec( response_array->GetItemAtIndex(i)->GetAsDictionary())) result.push_back(*module_spec); } return result; } // query the target remote for extended information using the qXfer packet // // example: object='features', annex='target.xml', out= // return: 'true' on success // 'false' on failure (err set) bool GDBRemoteCommunicationClient::ReadExtFeature( const lldb_private::ConstString object, const lldb_private::ConstString annex, std::string &out, lldb_private::Error &err) { std::stringstream output; StringExtractorGDBRemote chunk; uint64_t size = GetRemoteMaxPacketSize(); if (size == 0) size = 0x1000; size = size - 1; // Leave space for the 'm' or 'l' character in the response int offset = 0; bool active = true; // loop until all data has been read while (active) { // send query extended feature packet std::stringstream packet; packet << "qXfer:" << object.AsCString("") << ":read:" << annex.AsCString("") << ":" << std::hex << offset << "," << std::hex << size; GDBRemoteCommunication::PacketResult res = SendPacketAndWaitForResponse(packet.str(), chunk, false); if (res != GDBRemoteCommunication::PacketResult::Success) { err.SetErrorString("Error sending $qXfer packet"); return false; } const std::string &str = chunk.GetStringRef(); if (str.length() == 0) { // should have some data in chunk err.SetErrorString("Empty response from $qXfer packet"); return false; } // check packet code switch (str[0]) { // last chunk case ('l'): active = false; LLVM_FALLTHROUGH; // more chunks case ('m'): if (str.length() > 1) output << &str[1]; offset += size; break; // unknown chunk default: err.SetErrorString("Invalid continuation code from $qXfer packet"); return false; } } out = output.str(); err.Success(); return true; } // Notify the target that gdb is prepared to serve symbol lookup requests. // packet: "qSymbol::" // reply: // OK The target does not need to look up any (more) symbols. // qSymbol: The target requests the value of symbol sym_name (hex // encoded). // LLDB may provide the value by sending another qSymbol // packet // in the form of"qSymbol::". // // Three examples: // // lldb sends: qSymbol:: // lldb receives: OK // Remote gdb stub does not need to know the addresses of any symbols, lldb // does not // need to ask again in this session. // // lldb sends: qSymbol:: // lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473 // lldb sends: qSymbol::64697370617463685f71756575655f6f666673657473 // lldb receives: OK // Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb does // not know // the address at this time. lldb needs to send qSymbol:: again when it has // more // solibs loaded. // // lldb sends: qSymbol:: // lldb receives: qSymbol:64697370617463685f71756575655f6f666673657473 // lldb sends: qSymbol:2bc97554:64697370617463685f71756575655f6f666673657473 // lldb receives: OK // Remote gdb stub asks for address of 'dispatch_queue_offsets'. lldb says // that it // is at address 0x2bc97554. Remote gdb stub sends 'OK' indicating that it // does not // need any more symbols. lldb does not need to ask again in this session. void GDBRemoteCommunicationClient::ServeSymbolLookups( lldb_private::Process *process) { // Set to true once we've resolved a symbol to an address for the remote stub. // If we get an 'OK' response after this, the remote stub doesn't need any // more // symbols and we can stop asking. bool symbol_response_provided = false; // Is this the initial qSymbol:: packet? bool first_qsymbol_query = true; if (m_supports_qSymbol && m_qSymbol_requests_done == false) { Lock lock(*this, false); if (lock) { StreamString packet; packet.PutCString("qSymbol::"); StringExtractorGDBRemote response; while (SendPacketAndWaitForResponseNoLock(packet.GetString(), response) == PacketResult::Success) { if (response.IsOKResponse()) { if (symbol_response_provided || first_qsymbol_query) { m_qSymbol_requests_done = true; } // We are done serving symbols requests return; } first_qsymbol_query = false; if (response.IsUnsupportedResponse()) { // qSymbol is not supported by the current GDB server we are connected // to m_supports_qSymbol = false; return; } else { llvm::StringRef response_str(response.GetStringRef()); if (response_str.startswith("qSymbol:")) { response.SetFilePos(strlen("qSymbol:")); std::string symbol_name; if (response.GetHexByteString(symbol_name)) { if (symbol_name.empty()) return; addr_t symbol_load_addr = LLDB_INVALID_ADDRESS; lldb_private::SymbolContextList sc_list; if (process->GetTarget().GetImages().FindSymbolsWithNameAndType( ConstString(symbol_name), eSymbolTypeAny, sc_list)) { const size_t num_scs = sc_list.GetSize(); for (size_t sc_idx = 0; sc_idx < num_scs && symbol_load_addr == LLDB_INVALID_ADDRESS; ++sc_idx) { SymbolContext sc; if (sc_list.GetContextAtIndex(sc_idx, sc)) { if (sc.symbol) { switch (sc.symbol->GetType()) { case eSymbolTypeInvalid: case eSymbolTypeAbsolute: case eSymbolTypeUndefined: case eSymbolTypeSourceFile: case eSymbolTypeHeaderFile: case eSymbolTypeObjectFile: case eSymbolTypeCommonBlock: case eSymbolTypeBlock: case eSymbolTypeLocal: case eSymbolTypeParam: case eSymbolTypeVariable: case eSymbolTypeVariableType: case eSymbolTypeLineEntry: case eSymbolTypeLineHeader: case eSymbolTypeScopeBegin: case eSymbolTypeScopeEnd: case eSymbolTypeAdditional: case eSymbolTypeCompiler: case eSymbolTypeInstrumentation: case eSymbolTypeTrampoline: break; case eSymbolTypeCode: case eSymbolTypeResolver: case eSymbolTypeData: case eSymbolTypeRuntime: case eSymbolTypeException: case eSymbolTypeObjCClass: case eSymbolTypeObjCMetaClass: case eSymbolTypeObjCIVar: case eSymbolTypeReExported: symbol_load_addr = sc.symbol->GetLoadAddress(&process->GetTarget()); break; } } } } } // This is the normal path where our symbol lookup was successful // and we want // to send a packet with the new symbol value and see if another // lookup needs to be // done. // Change "packet" to contain the requested symbol value and name packet.Clear(); packet.PutCString("qSymbol:"); if (symbol_load_addr != LLDB_INVALID_ADDRESS) { packet.Printf("%" PRIx64, symbol_load_addr); symbol_response_provided = true; } else { symbol_response_provided = false; } packet.PutCString(":"); packet.PutBytesAsRawHex8(symbol_name.data(), symbol_name.size()); continue; // go back to the while loop and send "packet" and wait // for another response } } } } // If we make it here, the symbol request packet response wasn't valid or // our symbol lookup failed so we must abort return; } else if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet( GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)) { log->Printf( "GDBRemoteCommunicationClient::%s: Didn't get sequence mutex.", __FUNCTION__); } } } StructuredData::Array * GDBRemoteCommunicationClient::GetSupportedStructuredDataPlugins() { if (!m_supported_async_json_packets_is_valid) { // Query the server for the array of supported asynchronous JSON // packets. m_supported_async_json_packets_is_valid = true; Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); // Poll it now. StringExtractorGDBRemote response; const bool send_async = false; if (SendPacketAndWaitForResponse("qStructuredDataPlugins", response, send_async) == PacketResult::Success) { m_supported_async_json_packets_sp = StructuredData::ParseJSON(response.GetStringRef()); if (m_supported_async_json_packets_sp && !m_supported_async_json_packets_sp->GetAsArray()) { // We were returned something other than a JSON array. This // is invalid. Clear it out. if (log) log->Printf("GDBRemoteCommunicationClient::%s(): " "QSupportedAsyncJSONPackets returned invalid " "result: %s", __FUNCTION__, response.GetStringRef().c_str()); m_supported_async_json_packets_sp.reset(); } } else { if (log) log->Printf("GDBRemoteCommunicationClient::%s(): " "QSupportedAsyncJSONPackets unsupported", __FUNCTION__); } if (log && m_supported_async_json_packets_sp) { StreamString stream; m_supported_async_json_packets_sp->Dump(stream); log->Printf("GDBRemoteCommunicationClient::%s(): supported async " "JSON packets: %s", __FUNCTION__, stream.GetData()); } } return m_supported_async_json_packets_sp ? m_supported_async_json_packets_sp->GetAsArray() : nullptr; } Error GDBRemoteCommunicationClient::ConfigureRemoteStructuredData( const ConstString &type_name, const StructuredData::ObjectSP &config_sp) { Error error; if (type_name.GetLength() == 0) { error.SetErrorString("invalid type_name argument"); return error; } // Build command: Configure{type_name}: serialized config // data. StreamGDBRemote stream; stream.PutCString("QConfigure"); stream.PutCString(type_name.AsCString()); stream.PutChar(':'); if (config_sp) { // Gather the plain-text version of the configuration data. StreamString unescaped_stream; config_sp->Dump(unescaped_stream); unescaped_stream.Flush(); // Add it to the stream in escaped fashion. stream.PutEscapedBytes(unescaped_stream.GetString().data(), unescaped_stream.GetSize()); } stream.Flush(); // Send the packet. const bool send_async = false; StringExtractorGDBRemote response; auto result = SendPacketAndWaitForResponse(stream.GetString(), response, send_async); if (result == PacketResult::Success) { // We failed if the config result comes back other than OK. if (strcmp(response.GetStringRef().c_str(), "OK") == 0) { // Okay! error.Clear(); } else { error.SetErrorStringWithFormat("configuring StructuredData feature " "%s failed with error %s", type_name.AsCString(), response.GetStringRef().c_str()); } } else { // Can we get more data here on the failure? error.SetErrorStringWithFormat("configuring StructuredData feature %s " "failed when sending packet: " "PacketResult=%d", type_name.AsCString(), (int)result); } return error; } void GDBRemoteCommunicationClient::OnRunPacketSent(bool first) { GDBRemoteClientBase::OnRunPacketSent(first); m_curr_tid = LLDB_INVALID_THREAD_ID; } Index: vendor/lldb/dist/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp (revision 311542) @@ -1,3199 +1,3194 @@ //===-- ScriptInterpreterPython.cpp -----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifdef LLDB_DISABLE_PYTHON // Python is disabled in this build #else // LLDB Python header must be included first #include "lldb-python.h" #include "PythonDataObjects.h" #include "PythonExceptionState.h" #include "ScriptInterpreterPython.h" #include #include #include #include #include "lldb/API/SBValue.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Breakpoint/WatchpointOptions.h" #include "lldb/Core/Communication.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Timer.h" #include "lldb/Core/ValueObject.h" #include "lldb/DataFormatters/TypeSummary.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/Pipe.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #if defined(_WIN32) #include "lldb/Host/windows/ConnectionGenericFileWindows.h" #endif #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" using namespace lldb; using namespace lldb_private; static ScriptInterpreterPython::SWIGInitCallback g_swig_init_callback = nullptr; static ScriptInterpreterPython::SWIGBreakpointCallbackFunction g_swig_breakpoint_callback = nullptr; static ScriptInterpreterPython::SWIGWatchpointCallbackFunction g_swig_watchpoint_callback = nullptr; static ScriptInterpreterPython::SWIGPythonTypeScriptCallbackFunction g_swig_typescript_callback = nullptr; static ScriptInterpreterPython::SWIGPythonCreateSyntheticProvider g_swig_synthetic_script = nullptr; static ScriptInterpreterPython::SWIGPythonCreateCommandObject g_swig_create_cmd = nullptr; static ScriptInterpreterPython::SWIGPythonCalculateNumChildren g_swig_calc_children = nullptr; static ScriptInterpreterPython::SWIGPythonGetChildAtIndex g_swig_get_child_index = nullptr; static ScriptInterpreterPython::SWIGPythonGetIndexOfChildWithName g_swig_get_index_child = nullptr; static ScriptInterpreterPython::SWIGPythonCastPyObjectToSBValue g_swig_cast_to_sbvalue = nullptr; static ScriptInterpreterPython::SWIGPythonGetValueObjectSPFromSBValue g_swig_get_valobj_sp_from_sbvalue = nullptr; static ScriptInterpreterPython::SWIGPythonUpdateSynthProviderInstance g_swig_update_provider = nullptr; static ScriptInterpreterPython::SWIGPythonMightHaveChildrenSynthProviderInstance g_swig_mighthavechildren_provider = nullptr; static ScriptInterpreterPython::SWIGPythonGetValueSynthProviderInstance g_swig_getvalue_provider = nullptr; static ScriptInterpreterPython::SWIGPythonCallCommand g_swig_call_command = nullptr; static ScriptInterpreterPython::SWIGPythonCallCommandObject g_swig_call_command_object = nullptr; static ScriptInterpreterPython::SWIGPythonCallModuleInit g_swig_call_module_init = nullptr; static ScriptInterpreterPython::SWIGPythonCreateOSPlugin g_swig_create_os_plugin = nullptr; static ScriptInterpreterPython::SWIGPythonScriptKeyword_Process g_swig_run_script_keyword_process = nullptr; static ScriptInterpreterPython::SWIGPythonScriptKeyword_Thread g_swig_run_script_keyword_thread = nullptr; static ScriptInterpreterPython::SWIGPythonScriptKeyword_Target g_swig_run_script_keyword_target = nullptr; static ScriptInterpreterPython::SWIGPythonScriptKeyword_Frame g_swig_run_script_keyword_frame = nullptr; static ScriptInterpreterPython::SWIGPythonScriptKeyword_Value g_swig_run_script_keyword_value = nullptr; static ScriptInterpreterPython::SWIGPython_GetDynamicSetting g_swig_plugin_get = nullptr; static ScriptInterpreterPython::SWIGPythonCreateScriptedThreadPlan g_swig_thread_plan_script = nullptr; static ScriptInterpreterPython::SWIGPythonCallThreadPlan g_swig_call_thread_plan = nullptr; static bool g_initialized = false; namespace { // Initializing Python is not a straightforward process. We cannot control what // external code may have done before getting to this point in LLDB, including // potentially having already initialized Python, so we need to do a lot of work // to ensure that the existing state of the system is maintained across our // initialization. We do this by using an RAII pattern where we save off // initial // state at the beginning, and restore it at the end struct InitializePythonRAII { public: InitializePythonRAII() : m_gil_state(PyGILState_UNLOCKED), m_was_already_initialized(false) { // Python will muck with STDIN terminal state, so save off any current TTY // settings so we can restore them. m_stdin_tty_state.Save(STDIN_FILENO, false); InitializePythonHome(); // Python < 3.2 and Python >= 3.2 reversed the ordering requirements for // calling `Py_Initialize` and `PyEval_InitThreads`. < 3.2 requires that you // call `PyEval_InitThreads` first, and >= 3.2 requires that you call it last. #if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2) || (PY_MAJOR_VERSION > 3) Py_InitializeEx(0); InitializeThreadsPrivate(); #else InitializeThreadsPrivate(); Py_InitializeEx(0); #endif } ~InitializePythonRAII() { if (m_was_already_initialized) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT | LIBLLDB_LOG_VERBOSE)); if (log) { log->Printf("Releasing PyGILState. Returning to state = %slocked\n", m_was_already_initialized == PyGILState_UNLOCKED ? "un" : ""); } PyGILState_Release(m_gil_state); } else { // We initialized the threads in this function, just unlock the GIL. PyEval_SaveThread(); } m_stdin_tty_state.Restore(); } private: void InitializePythonHome() { #if defined(LLDB_PYTHON_HOME) #if PY_MAJOR_VERSION >= 3 size_t size = 0; static wchar_t *g_python_home = Py_DecodeLocale(LLDB_PYTHON_HOME, &size); #else static char g_python_home[] = LLDB_PYTHON_HOME; #endif Py_SetPythonHome(g_python_home); #endif } void InitializeThreadsPrivate() { if (PyEval_ThreadsInitialized()) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT | LIBLLDB_LOG_VERBOSE)); m_was_already_initialized = true; m_gil_state = PyGILState_Ensure(); if (log) { log->Printf("Ensured PyGILState. Previous state = %slocked\n", m_gil_state == PyGILState_UNLOCKED ? "un" : ""); } return; } // InitThreads acquires the GIL if it hasn't been called before. PyEval_InitThreads(); } TerminalState m_stdin_tty_state; PyGILState_STATE m_gil_state; bool m_was_already_initialized; }; } ScriptInterpreterPython::Locker::Locker(ScriptInterpreterPython *py_interpreter, uint16_t on_entry, uint16_t on_leave, FILE *in, FILE *out, FILE *err) : ScriptInterpreterLocker(), m_teardown_session((on_leave & TearDownSession) == TearDownSession), m_python_interpreter(py_interpreter) { DoAcquireLock(); if ((on_entry & InitSession) == InitSession) { if (DoInitSession(on_entry, in, out, err) == false) { // Don't teardown the session if we didn't init it. m_teardown_session = false; } } } bool ScriptInterpreterPython::Locker::DoAcquireLock() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT | LIBLLDB_LOG_VERBOSE)); m_GILState = PyGILState_Ensure(); if (log) log->Printf("Ensured PyGILState. Previous state = %slocked\n", m_GILState == PyGILState_UNLOCKED ? "un" : ""); // we need to save the thread state when we first start the command // because we might decide to interrupt it while some action is taking // place outside of Python (e.g. printing to screen, waiting for the network, // ...) // in that case, _PyThreadState_Current will be NULL - and we would be unable // to set the asynchronous exception - not a desirable situation m_python_interpreter->SetThreadState(PyThreadState_Get()); m_python_interpreter->IncrementLockCount(); return true; } bool ScriptInterpreterPython::Locker::DoInitSession(uint16_t on_entry_flags, FILE *in, FILE *out, FILE *err) { if (!m_python_interpreter) return false; return m_python_interpreter->EnterSession(on_entry_flags, in, out, err); } bool ScriptInterpreterPython::Locker::DoFreeLock() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT | LIBLLDB_LOG_VERBOSE)); if (log) log->Printf("Releasing PyGILState. Returning to state = %slocked\n", m_GILState == PyGILState_UNLOCKED ? "un" : ""); PyGILState_Release(m_GILState); m_python_interpreter->DecrementLockCount(); return true; } bool ScriptInterpreterPython::Locker::DoTearDownSession() { if (!m_python_interpreter) return false; m_python_interpreter->LeaveSession(); return true; } ScriptInterpreterPython::Locker::~Locker() { if (m_teardown_session) DoTearDownSession(); DoFreeLock(); } ScriptInterpreterPython::ScriptInterpreterPython( CommandInterpreter &interpreter) : ScriptInterpreter(interpreter, eScriptLanguagePython), IOHandlerDelegateMultiline("DONE"), m_saved_stdin(), m_saved_stdout(), m_saved_stderr(), m_main_module(), m_lldb_module(), m_session_dict(PyInitialValue::Invalid), m_sys_module_dict(PyInitialValue::Invalid), m_run_one_line_function(), m_run_one_line_str_global(), m_dictionary_name( interpreter.GetDebugger().GetInstanceName().AsCString()), m_terminal_state(), m_active_io_handler(eIOHandlerNone), m_session_is_active(false), m_pty_slave_is_open(false), m_valid_session(true), m_lock_count(0), m_command_thread_state(nullptr) { InitializePrivate(); m_dictionary_name.append("_dict"); StreamString run_string; run_string.Printf("%s = dict()", m_dictionary_name.c_str()); Locker locker(this, ScriptInterpreterPython::Locker::AcquireLock, ScriptInterpreterPython::Locker::FreeAcquiredLock); PyRun_SimpleString(run_string.GetData()); run_string.Clear(); run_string.Printf( "run_one_line (%s, 'import copy, keyword, os, re, sys, uuid, lldb')", m_dictionary_name.c_str()); PyRun_SimpleString(run_string.GetData()); // Reloading modules requires a different syntax in Python 2 and Python 3. // This provides // a consistent syntax no matter what version of Python. run_string.Clear(); run_string.Printf("run_one_line (%s, 'from six.moves import reload_module')", m_dictionary_name.c_str()); PyRun_SimpleString(run_string.GetData()); // WARNING: temporary code that loads Cocoa formatters - this should be done // on a per-platform basis rather than loading the whole set // and letting the individual formatter classes exploit APIs to check whether // they can/cannot do their task run_string.Clear(); run_string.Printf( "run_one_line (%s, 'import lldb.formatters, lldb.formatters.cpp, pydoc')", m_dictionary_name.c_str()); PyRun_SimpleString(run_string.GetData()); run_string.Clear(); run_string.Printf("run_one_line (%s, 'import lldb.embedded_interpreter; from " "lldb.embedded_interpreter import run_python_interpreter; " "from lldb.embedded_interpreter import run_one_line')", m_dictionary_name.c_str()); PyRun_SimpleString(run_string.GetData()); run_string.Clear(); run_string.Printf("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64 "; pydoc.pager = pydoc.plainpager')", m_dictionary_name.c_str(), interpreter.GetDebugger().GetID()); PyRun_SimpleString(run_string.GetData()); } ScriptInterpreterPython::~ScriptInterpreterPython() { // the session dictionary may hold objects with complex state // which means that they may need to be torn down with some level of smarts // and that, in turn, requires a valid thread state // force Python to procure itself such a thread state, nuke the session // dictionary // and then release it for others to use and proceed with the rest of the // shutdown auto gil_state = PyGILState_Ensure(); m_session_dict.Reset(); PyGILState_Release(gil_state); } void ScriptInterpreterPython::Initialize() { static std::once_flag g_once_flag; std::call_once(g_once_flag, []() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), lldb::eScriptLanguagePython, CreateInstance); }); } void ScriptInterpreterPython::Terminate() {} lldb::ScriptInterpreterSP ScriptInterpreterPython::CreateInstance(CommandInterpreter &interpreter) { return std::make_shared(interpreter); } lldb_private::ConstString ScriptInterpreterPython::GetPluginNameStatic() { static ConstString g_name("script-python"); return g_name; } const char *ScriptInterpreterPython::GetPluginDescriptionStatic() { return "Embedded Python interpreter"; } lldb_private::ConstString ScriptInterpreterPython::GetPluginName() { return GetPluginNameStatic(); } uint32_t ScriptInterpreterPython::GetPluginVersion() { return 1; } void ScriptInterpreterPython::IOHandlerActivated(IOHandler &io_handler) { const char *instructions = nullptr; switch (m_active_io_handler) { case eIOHandlerNone: break; case eIOHandlerBreakpoint: instructions = R"(Enter your Python command(s). Type 'DONE' to end. def function (frame, bp_loc, internal_dict): """frame: the lldb.SBFrame for the location at which you stopped bp_loc: an lldb.SBBreakpointLocation for the breakpoint location information internal_dict: an LLDB support object not to be used""" )"; break; case eIOHandlerWatchpoint: instructions = "Enter your Python command(s). Type 'DONE' to end.\n"; break; } if (instructions) { StreamFileSP output_sp(io_handler.GetOutputStreamFile()); if (output_sp) { output_sp->PutCString(instructions); output_sp->Flush(); } } } void ScriptInterpreterPython::IOHandlerInputComplete(IOHandler &io_handler, std::string &data) { io_handler.SetIsDone(true); bool batch_mode = m_interpreter.GetBatchCommandMode(); switch (m_active_io_handler) { case eIOHandlerNone: break; case eIOHandlerBreakpoint: { std::vector *bp_options_vec = (std::vector *)io_handler.GetUserData(); for (auto bp_options : *bp_options_vec) { if (!bp_options) continue; auto data_ap = llvm::make_unique(); if (!data_ap) break; data_ap->user_source.SplitIntoLines(data); if (GenerateBreakpointCommandCallbackData(data_ap->user_source, data_ap->script_source) .Success()) { auto baton_sp = std::make_shared( std::move(data_ap)); bp_options->SetCallback( ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp); } else if (!batch_mode) { StreamFileSP error_sp = io_handler.GetErrorStreamFile(); if (error_sp) { error_sp->Printf("Warning: No command attached to breakpoint.\n"); error_sp->Flush(); } } } m_active_io_handler = eIOHandlerNone; } break; case eIOHandlerWatchpoint: { WatchpointOptions *wp_options = (WatchpointOptions *)io_handler.GetUserData(); auto data_ap = llvm::make_unique(); data_ap->user_source.SplitIntoLines(data); if (GenerateWatchpointCommandCallbackData(data_ap->user_source, data_ap->script_source)) { auto baton_sp = std::make_shared(std::move(data_ap)); wp_options->SetCallback( ScriptInterpreterPython::WatchpointCallbackFunction, baton_sp); } else if (!batch_mode) { StreamFileSP error_sp = io_handler.GetErrorStreamFile(); if (error_sp) { error_sp->Printf("Warning: No command attached to breakpoint.\n"); error_sp->Flush(); } } m_active_io_handler = eIOHandlerNone; } break; } } void ScriptInterpreterPython::ResetOutputFileHandle(FILE *fh) {} void ScriptInterpreterPython::SaveTerminalState(int fd) { // Python mucks with the terminal state of STDIN. If we can possibly avoid // this by setting the file handles up correctly prior to entering the // interpreter we should. For now we save and restore the terminal state // on the input file handle. m_terminal_state.Save(fd, false); } void ScriptInterpreterPython::RestoreTerminalState() { // Python mucks with the terminal state of STDIN. If we can possibly avoid // this by setting the file handles up correctly prior to entering the // interpreter we should. For now we save and restore the terminal state // on the input file handle. m_terminal_state.Restore(); } void ScriptInterpreterPython::LeaveSession() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT)); if (log) log->PutCString("ScriptInterpreterPython::LeaveSession()"); // checking that we have a valid thread state - since we use our own threading // and locking // in some (rare) cases during cleanup Python may end up believing we have no // thread state // and PyImport_AddModule will crash if that is the case - since that seems to // only happen // when destroying the SBDebugger, we can make do without clearing up stdout // and stderr // rdar://problem/11292882 // When the current thread state is NULL, PyThreadState_Get() issues a fatal // error. if (PyThreadState_GetDict()) { PythonDictionary &sys_module_dict = GetSysModuleDictionary(); if (sys_module_dict.IsValid()) { if (m_saved_stdin.IsValid()) { sys_module_dict.SetItemForKey(PythonString("stdin"), m_saved_stdin); m_saved_stdin.Reset(); } if (m_saved_stdout.IsValid()) { sys_module_dict.SetItemForKey(PythonString("stdout"), m_saved_stdout); m_saved_stdout.Reset(); } if (m_saved_stderr.IsValid()) { sys_module_dict.SetItemForKey(PythonString("stderr"), m_saved_stderr); m_saved_stderr.Reset(); } } } m_session_is_active = false; } bool ScriptInterpreterPython::SetStdHandle(File &file, const char *py_name, PythonFile &save_file, const char *mode) { if (file.IsValid()) { // Flush the file before giving it to python to avoid interleaved output. file.Flush(); PythonDictionary &sys_module_dict = GetSysModuleDictionary(); save_file = sys_module_dict.GetItemForKey(PythonString(py_name)) .AsType(); PythonFile new_file(file, mode); sys_module_dict.SetItemForKey(PythonString(py_name), new_file); return true; } else save_file.Reset(); return false; } bool ScriptInterpreterPython::EnterSession(uint16_t on_entry_flags, FILE *in, FILE *out, FILE *err) { // If we have already entered the session, without having officially 'left' // it, then there is no need to // 'enter' it again. Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT)); if (m_session_is_active) { if (log) log->Printf( "ScriptInterpreterPython::EnterSession(on_entry_flags=0x%" PRIx16 ") session is already active, returning without doing anything", on_entry_flags); return false; } if (log) log->Printf( "ScriptInterpreterPython::EnterSession(on_entry_flags=0x%" PRIx16 ")", on_entry_flags); m_session_is_active = true; StreamString run_string; if (on_entry_flags & Locker::InitGlobals) { run_string.Printf("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64, m_dictionary_name.c_str(), GetCommandInterpreter().GetDebugger().GetID()); run_string.Printf( "; lldb.debugger = lldb.SBDebugger.FindDebuggerWithID (%" PRIu64 ")", GetCommandInterpreter().GetDebugger().GetID()); run_string.PutCString("; lldb.target = lldb.debugger.GetSelectedTarget()"); run_string.PutCString("; lldb.process = lldb.target.GetProcess()"); run_string.PutCString("; lldb.thread = lldb.process.GetSelectedThread ()"); run_string.PutCString("; lldb.frame = lldb.thread.GetSelectedFrame ()"); run_string.PutCString("')"); } else { // If we aren't initing the globals, we should still always set the debugger // (since that is always unique.) run_string.Printf("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64, m_dictionary_name.c_str(), GetCommandInterpreter().GetDebugger().GetID()); run_string.Printf( "; lldb.debugger = lldb.SBDebugger.FindDebuggerWithID (%" PRIu64 ")", GetCommandInterpreter().GetDebugger().GetID()); run_string.PutCString("')"); } PyRun_SimpleString(run_string.GetData()); run_string.Clear(); PythonDictionary &sys_module_dict = GetSysModuleDictionary(); if (sys_module_dict.IsValid()) { File in_file(in, false); File out_file(out, false); File err_file(err, false); lldb::StreamFileSP in_sp; lldb::StreamFileSP out_sp; lldb::StreamFileSP err_sp; if (!in_file.IsValid() || !out_file.IsValid() || !err_file.IsValid()) m_interpreter.GetDebugger().AdoptTopIOHandlerFilesIfInvalid(in_sp, out_sp, err_sp); if (on_entry_flags & Locker::NoSTDIN) { m_saved_stdin.Reset(); } else { if (!SetStdHandle(in_file, "stdin", m_saved_stdin, "r")) { if (in_sp) SetStdHandle(in_sp->GetFile(), "stdin", m_saved_stdin, "r"); } } if (!SetStdHandle(out_file, "stdout", m_saved_stdout, "w")) { if (out_sp) SetStdHandle(out_sp->GetFile(), "stdout", m_saved_stdout, "w"); } if (!SetStdHandle(err_file, "stderr", m_saved_stderr, "w")) { if (err_sp) SetStdHandle(err_sp->GetFile(), "stderr", m_saved_stderr, "w"); } } if (PyErr_Occurred()) PyErr_Clear(); return true; } PythonObject &ScriptInterpreterPython::GetMainModule() { if (!m_main_module.IsValid()) m_main_module.Reset(PyRefType::Borrowed, PyImport_AddModule("__main__")); return m_main_module; } PythonDictionary &ScriptInterpreterPython::GetSessionDictionary() { if (m_session_dict.IsValid()) return m_session_dict; PythonObject &main_module = GetMainModule(); if (!main_module.IsValid()) return m_session_dict; PythonDictionary main_dict(PyRefType::Borrowed, PyModule_GetDict(main_module.get())); if (!main_dict.IsValid()) return m_session_dict; PythonObject item = main_dict.GetItemForKey(PythonString(m_dictionary_name)); m_session_dict.Reset(PyRefType::Borrowed, item.get()); return m_session_dict; } PythonDictionary &ScriptInterpreterPython::GetSysModuleDictionary() { if (m_sys_module_dict.IsValid()) return m_sys_module_dict; PythonObject sys_module(PyRefType::Borrowed, PyImport_AddModule("sys")); if (sys_module.IsValid()) m_sys_module_dict.Reset(PyRefType::Borrowed, PyModule_GetDict(sys_module.get())); return m_sys_module_dict; } static std::string GenerateUniqueName(const char *base_name_wanted, uint32_t &functions_counter, const void *name_token = nullptr) { StreamString sstr; if (!base_name_wanted) return std::string(); if (!name_token) sstr.Printf("%s_%d", base_name_wanted, functions_counter++); else sstr.Printf("%s_%p", base_name_wanted, name_token); return sstr.GetString(); } bool ScriptInterpreterPython::GetEmbeddedInterpreterModuleObjects() { if (m_run_one_line_function.IsValid()) return true; PythonObject module(PyRefType::Borrowed, PyImport_AddModule("lldb.embedded_interpreter")); if (!module.IsValid()) return false; PythonDictionary module_dict(PyRefType::Borrowed, PyModule_GetDict(module.get())); if (!module_dict.IsValid()) return false; m_run_one_line_function = module_dict.GetItemForKey(PythonString("run_one_line")); m_run_one_line_str_global = module_dict.GetItemForKey(PythonString("g_run_one_line_str")); return m_run_one_line_function.IsValid(); } static void ReadThreadBytesReceived(void *baton, const void *src, size_t src_len) { if (src && src_len) { Stream *strm = (Stream *)baton; strm->Write(src, src_len); strm->Flush(); } } bool ScriptInterpreterPython::ExecuteOneLine( const char *command, CommandReturnObject *result, const ExecuteScriptOptions &options) { if (!m_valid_session) return false; if (command && command[0]) { // We want to call run_one_line, passing in the dictionary and the command // string. We cannot do this through // PyRun_SimpleString here because the command string may contain escaped // characters, and putting it inside // another string to pass to PyRun_SimpleString messes up the escaping. So // we use the following more complicated // method to pass the command string directly down to Python. Debugger &debugger = m_interpreter.GetDebugger(); StreamFileSP input_file_sp; StreamFileSP output_file_sp; StreamFileSP error_file_sp; Communication output_comm( "lldb.ScriptInterpreterPython.ExecuteOneLine.comm"); bool join_read_thread = false; if (options.GetEnableIO()) { if (result) { input_file_sp = debugger.GetInputFile(); // Set output to a temporary file so we can forward the results on to // the result object Pipe pipe; Error pipe_result = pipe.CreateNew(false); if (pipe_result.Success()) { #if defined(_WIN32) lldb::file_t read_file = pipe.GetReadNativeHandle(); pipe.ReleaseReadFileDescriptor(); std::unique_ptr conn_ap( new ConnectionGenericFile(read_file, true)); #else std::unique_ptr conn_ap( new ConnectionFileDescriptor(pipe.ReleaseReadFileDescriptor(), true)); #endif if (conn_ap->IsConnected()) { output_comm.SetConnection(conn_ap.release()); output_comm.SetReadThreadBytesReceivedCallback( ReadThreadBytesReceived, &result->GetOutputStream()); output_comm.StartReadThread(); join_read_thread = true; FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w"); output_file_sp.reset(new StreamFile(outfile_handle, true)); error_file_sp = output_file_sp; if (outfile_handle) ::setbuf(outfile_handle, nullptr); result->SetImmediateOutputFile( debugger.GetOutputFile()->GetFile().GetStream()); result->SetImmediateErrorFile( debugger.GetErrorFile()->GetFile().GetStream()); } } } if (!input_file_sp || !output_file_sp || !error_file_sp) debugger.AdoptTopIOHandlerFilesIfInvalid(input_file_sp, output_file_sp, error_file_sp); } else { input_file_sp.reset(new StreamFile()); input_file_sp->GetFile().Open(FileSystem::DEV_NULL, File::eOpenOptionRead); output_file_sp.reset(new StreamFile()); output_file_sp->GetFile().Open(FileSystem::DEV_NULL, File::eOpenOptionWrite); error_file_sp = output_file_sp; } FILE *in_file = input_file_sp->GetFile().GetStream(); FILE *out_file = output_file_sp->GetFile().GetStream(); FILE *err_file = error_file_sp->GetFile().GetStream(); bool success = false; { // WARNING! It's imperative that this RAII scope be as tight as possible. // In particular, the // scope must end *before* we try to join the read thread. The reason for // this is that a // pre-requisite for joining the read thread is that we close the write // handle (to break the // pipe and cause it to wake up and exit). But acquiring the GIL as below // will redirect Python's // stdio to use this same handle. If we close the handle while Python is // still using it, bad // things will happen. Locker locker( this, ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | (options.GetSetLLDBGlobals() ? ScriptInterpreterPython::Locker::InitGlobals : 0) | ((result && result->GetInteractive()) ? 0 : Locker::NoSTDIN), ScriptInterpreterPython::Locker::FreeAcquiredLock | ScriptInterpreterPython::Locker::TearDownSession, in_file, out_file, err_file); // Find the correct script interpreter dictionary in the main module. PythonDictionary &session_dict = GetSessionDictionary(); if (session_dict.IsValid()) { if (GetEmbeddedInterpreterModuleObjects()) { if (PyCallable_Check(m_run_one_line_function.get())) { PythonObject pargs( PyRefType::Owned, Py_BuildValue("(Os)", session_dict.get(), command)); if (pargs.IsValid()) { PythonObject return_value( PyRefType::Owned, PyObject_CallObject(m_run_one_line_function.get(), pargs.get())); if (return_value.IsValid()) success = true; else if (options.GetMaskoutErrors() && PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } } } } } // Flush our output and error file handles ::fflush(out_file); if (out_file != err_file) ::fflush(err_file); } if (join_read_thread) { // Close the write end of the pipe since we are done with our // one line script. This should cause the read thread that // output_comm is using to exit output_file_sp->GetFile().Close(); // The close above should cause this thread to exit when it gets // to the end of file, so let it get all its data output_comm.JoinReadThread(); // Now we can close the read end of the pipe output_comm.Disconnect(); } if (success) return true; // The one-liner failed. Append the error message. if (result) result->AppendErrorWithFormat( "python failed attempting to evaluate '%s'\n", command); return false; } if (result) result->AppendError("empty command passed to python\n"); return false; } class IOHandlerPythonInterpreter : public IOHandler { public: IOHandlerPythonInterpreter(Debugger &debugger, ScriptInterpreterPython *python) : IOHandler(debugger, IOHandler::Type::PythonInterpreter), m_python(python) {} ~IOHandlerPythonInterpreter() override {} ConstString GetControlSequence(char ch) override { if (ch == 'd') return ConstString("quit()\n"); return ConstString(); } void Run() override { if (m_python) { int stdin_fd = GetInputFD(); if (stdin_fd >= 0) { Terminal terminal(stdin_fd); TerminalState terminal_state; const bool is_a_tty = terminal.IsATerminal(); if (is_a_tty) { terminal_state.Save(stdin_fd, false); terminal.SetCanonical(false); terminal.SetEcho(true); } ScriptInterpreterPython::Locker locker( m_python, ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | ScriptInterpreterPython::Locker::InitGlobals, ScriptInterpreterPython::Locker::FreeAcquiredLock | ScriptInterpreterPython::Locker::TearDownSession); // The following call drops into the embedded interpreter loop and stays // there until the // user chooses to exit from the Python interpreter. // This embedded interpreter will, as any Python code that performs I/O, // unlock the GIL before // a system call that can hang, and lock it when the syscall has // returned. // We need to surround the call to the embedded interpreter with calls // to PyGILState_Ensure and // PyGILState_Release (using the Locker above). This is because Python // has a global lock which must be held whenever we want // to touch any Python objects. Otherwise, if the user calls Python // code, the interpreter state will be off, // and things could hang (it's happened before). StreamString run_string; run_string.Printf("run_python_interpreter (%s)", m_python->GetDictionaryName()); PyRun_SimpleString(run_string.GetData()); if (is_a_tty) terminal_state.Restore(); } } SetIsDone(true); } void Cancel() override {} bool Interrupt() override { return m_python->Interrupt(); } void GotEOF() override {} protected: ScriptInterpreterPython *m_python; }; void ScriptInterpreterPython::ExecuteInterpreterLoop() { Timer scoped_timer(LLVM_PRETTY_FUNCTION, LLVM_PRETTY_FUNCTION); Debugger &debugger = GetCommandInterpreter().GetDebugger(); // At the moment, the only time the debugger does not have an input file // handle is when this is called // directly from Python, in which case it is both dangerous and unnecessary // (not to mention confusing) to // try to embed a running interpreter loop inside the already running Python // interpreter loop, so we won't // do it. if (!debugger.GetInputFile()->GetFile().IsValid()) return; IOHandlerSP io_handler_sp(new IOHandlerPythonInterpreter(debugger, this)); if (io_handler_sp) { debugger.PushIOHandler(io_handler_sp); } } bool ScriptInterpreterPython::Interrupt() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT)); if (IsExecutingPython()) { PyThreadState *state = PyThreadState_GET(); if (!state) state = GetThreadState(); if (state) { long tid = state->thread_id; PyThreadState_Swap(state); int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt); if (log) log->Printf("ScriptInterpreterPython::Interrupt() sending " "PyExc_KeyboardInterrupt (tid = %li, num_threads = %i)...", tid, num_threads); return true; } } if (log) log->Printf("ScriptInterpreterPython::Interrupt() python code not running, " "can't interrupt"); return false; } bool ScriptInterpreterPython::ExecuteOneLineWithReturn( const char *in_string, ScriptInterpreter::ScriptReturnType return_type, void *ret_value, const ExecuteScriptOptions &options) { Locker locker(this, ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | (options.GetSetLLDBGlobals() ? ScriptInterpreterPython::Locker::InitGlobals : 0) | Locker::NoSTDIN, ScriptInterpreterPython::Locker::FreeAcquiredLock | ScriptInterpreterPython::Locker::TearDownSession); PythonObject py_return; PythonObject &main_module = GetMainModule(); PythonDictionary globals(PyRefType::Borrowed, PyModule_GetDict(main_module.get())); PythonObject py_error; bool ret_success = false; int success; PythonDictionary locals = GetSessionDictionary(); if (!locals.IsValid()) { locals.Reset( PyRefType::Owned, PyObject_GetAttrString(globals.get(), m_dictionary_name.c_str())); } if (!locals.IsValid()) locals = globals; py_error.Reset(PyRefType::Borrowed, PyErr_Occurred()); if (py_error.IsValid()) PyErr_Clear(); if (in_string != nullptr) { { // scope for PythonInputReaderManager // PythonInputReaderManager py_input(options.GetEnableIO() ? this : NULL); py_return.Reset( PyRefType::Owned, PyRun_String(in_string, Py_eval_input, globals.get(), locals.get())); if (!py_return.IsValid()) { py_error.Reset(PyRefType::Borrowed, PyErr_Occurred()); if (py_error.IsValid()) PyErr_Clear(); py_return.Reset(PyRefType::Owned, PyRun_String(in_string, Py_single_input, globals.get(), locals.get())); } } if (py_return.IsValid()) { switch (return_type) { case eScriptReturnTypeCharPtr: // "char *" { const char format[3] = "s#"; success = PyArg_Parse(py_return.get(), format, (char **)ret_value); break; } case eScriptReturnTypeCharStrOrNone: // char* or NULL if py_return == // Py_None { const char format[3] = "z"; success = PyArg_Parse(py_return.get(), format, (char **)ret_value); break; } case eScriptReturnTypeBool: { const char format[2] = "b"; success = PyArg_Parse(py_return.get(), format, (bool *)ret_value); break; } case eScriptReturnTypeShortInt: { const char format[2] = "h"; success = PyArg_Parse(py_return.get(), format, (short *)ret_value); break; } case eScriptReturnTypeShortIntUnsigned: { const char format[2] = "H"; success = PyArg_Parse(py_return.get(), format, (unsigned short *)ret_value); break; } case eScriptReturnTypeInt: { const char format[2] = "i"; success = PyArg_Parse(py_return.get(), format, (int *)ret_value); break; } case eScriptReturnTypeIntUnsigned: { const char format[2] = "I"; success = PyArg_Parse(py_return.get(), format, (unsigned int *)ret_value); break; } case eScriptReturnTypeLongInt: { const char format[2] = "l"; success = PyArg_Parse(py_return.get(), format, (long *)ret_value); break; } case eScriptReturnTypeLongIntUnsigned: { const char format[2] = "k"; success = PyArg_Parse(py_return.get(), format, (unsigned long *)ret_value); break; } case eScriptReturnTypeLongLong: { const char format[2] = "L"; success = PyArg_Parse(py_return.get(), format, (long long *)ret_value); break; } case eScriptReturnTypeLongLongUnsigned: { const char format[2] = "K"; success = PyArg_Parse(py_return.get(), format, (unsigned long long *)ret_value); break; } case eScriptReturnTypeFloat: { const char format[2] = "f"; success = PyArg_Parse(py_return.get(), format, (float *)ret_value); break; } case eScriptReturnTypeDouble: { const char format[2] = "d"; success = PyArg_Parse(py_return.get(), format, (double *)ret_value); break; } case eScriptReturnTypeChar: { const char format[2] = "c"; success = PyArg_Parse(py_return.get(), format, (char *)ret_value); break; } case eScriptReturnTypeOpaqueObject: { success = true; PyObject *saved_value = py_return.get(); Py_XINCREF(saved_value); *((PyObject **)ret_value) = saved_value; break; } } if (success) ret_success = true; else ret_success = false; } } py_error.Reset(PyRefType::Borrowed, PyErr_Occurred()); if (py_error.IsValid()) { ret_success = false; if (options.GetMaskoutErrors()) { if (PyErr_GivenExceptionMatches(py_error.get(), PyExc_SyntaxError)) PyErr_Print(); PyErr_Clear(); } } return ret_success; } Error ScriptInterpreterPython::ExecuteMultipleLines( const char *in_string, const ExecuteScriptOptions &options) { Error error; Locker locker(this, ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | (options.GetSetLLDBGlobals() ? ScriptInterpreterPython::Locker::InitGlobals : 0) | Locker::NoSTDIN, ScriptInterpreterPython::Locker::FreeAcquiredLock | ScriptInterpreterPython::Locker::TearDownSession); PythonObject return_value; PythonObject &main_module = GetMainModule(); PythonDictionary globals(PyRefType::Borrowed, PyModule_GetDict(main_module.get())); PythonObject py_error; PythonDictionary locals = GetSessionDictionary(); if (!locals.IsValid()) locals.Reset( PyRefType::Owned, PyObject_GetAttrString(globals.get(), m_dictionary_name.c_str())); if (!locals.IsValid()) locals = globals; py_error.Reset(PyRefType::Borrowed, PyErr_Occurred()); if (py_error.IsValid()) PyErr_Clear(); if (in_string != nullptr) { PythonObject code_object; code_object.Reset(PyRefType::Owned, Py_CompileString(in_string, "temp.py", Py_file_input)); if (code_object.IsValid()) { // In Python 2.x, PyEval_EvalCode takes a PyCodeObject, but in Python 3.x, it // takes // a PyObject. They are convertible (hence the function // PyCode_Check(PyObject*), so // we have to do the cast for Python 2.x #if PY_MAJOR_VERSION >= 3 PyObject *py_code_obj = code_object.get(); #else PyCodeObject *py_code_obj = reinterpret_cast(code_object.get()); #endif return_value.Reset( PyRefType::Owned, PyEval_EvalCode(py_code_obj, globals.get(), locals.get())); } } PythonExceptionState exception_state(!options.GetMaskoutErrors()); if (exception_state.IsError()) error.SetErrorString(exception_state.Format().c_str()); return error; } void ScriptInterpreterPython::CollectDataForBreakpointCommandCallback( std::vector &bp_options_vec, CommandReturnObject &result) { m_active_io_handler = eIOHandlerBreakpoint; m_interpreter.GetPythonCommandsFromIOHandler(" ", *this, true, &bp_options_vec); } void ScriptInterpreterPython::CollectDataForWatchpointCommandCallback( WatchpointOptions *wp_options, CommandReturnObject &result) { m_active_io_handler = eIOHandlerWatchpoint; m_interpreter.GetPythonCommandsFromIOHandler(" ", *this, true, wp_options); } void ScriptInterpreterPython::SetBreakpointCommandCallbackFunction( BreakpointOptions *bp_options, const char *function_name) { // For now just cons up a oneliner that calls the provided function. std::string oneliner("return "); oneliner += function_name; oneliner += "(frame, bp_loc, internal_dict)"; m_interpreter.GetScriptInterpreter()->SetBreakpointCommandCallback( bp_options, oneliner.c_str()); } Error ScriptInterpreterPython::SetBreakpointCommandCallback( BreakpointOptions *bp_options, std::unique_ptr &cmd_data_up) { Error error; error = GenerateBreakpointCommandCallbackData(cmd_data_up->user_source, cmd_data_up->script_source); if (error.Fail()) { return error; } auto baton_sp = std::make_shared(std::move(cmd_data_up)); bp_options->SetCallback(ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp); return error; } // Set a Python one-liner as the callback for the breakpoint. Error ScriptInterpreterPython::SetBreakpointCommandCallback( BreakpointOptions *bp_options, const char *command_body_text) { auto data_ap = llvm::make_unique(); // Split the command_body_text into lines, and pass that to // GenerateBreakpointCommandCallbackData. That will // wrap the body in an auto-generated function, and return the function name // in script_source. That is what // the callback will actually invoke. data_ap->user_source.SplitIntoLines(command_body_text); Error error = GenerateBreakpointCommandCallbackData(data_ap->user_source, data_ap->script_source); if (error.Success()) { auto baton_sp = std::make_shared(std::move(data_ap)); bp_options->SetCallback(ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp); return error; } else return error; } // Set a Python one-liner as the callback for the watchpoint. void ScriptInterpreterPython::SetWatchpointCommandCallback( WatchpointOptions *wp_options, const char *oneliner) { auto data_ap = llvm::make_unique(); // It's necessary to set both user_source and script_source to the oneliner. // The former is used to generate callback description (as in watchpoint // command list) // while the latter is used for Python to interpret during the actual // callback. data_ap->user_source.AppendString(oneliner); data_ap->script_source.assign(oneliner); if (GenerateWatchpointCommandCallbackData(data_ap->user_source, data_ap->script_source)) { auto baton_sp = std::make_shared(std::move(data_ap)); wp_options->SetCallback(ScriptInterpreterPython::WatchpointCallbackFunction, baton_sp); } return; } Error ScriptInterpreterPython::ExportFunctionDefinitionToInterpreter( StringList &function_def) { // Convert StringList to one long, newline delimited, const char *. std::string function_def_string(function_def.CopyList()); Error error = ExecuteMultipleLines( function_def_string.c_str(), ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false)); return error; } Error ScriptInterpreterPython::GenerateFunction(const char *signature, const StringList &input) { Error error; int num_lines = input.GetSize(); if (num_lines == 0) { error.SetErrorString("No input data."); return error; } if (!signature || *signature == 0) { error.SetErrorString("No output function name."); return error; } StreamString sstr; StringList auto_generated_function; auto_generated_function.AppendString(signature); auto_generated_function.AppendString( " global_dict = globals()"); // Grab the global dictionary auto_generated_function.AppendString( " new_keys = internal_dict.keys()"); // Make a list of keys in the // session dict auto_generated_function.AppendString( " old_keys = global_dict.keys()"); // Save list of keys in global dict auto_generated_function.AppendString( " global_dict.update (internal_dict)"); // Add the session dictionary // to the // global dictionary. // Wrap everything up inside the function, increasing the indentation. auto_generated_function.AppendString(" if True:"); for (int i = 0; i < num_lines; ++i) { sstr.Clear(); sstr.Printf(" %s", input.GetStringAtIndex(i)); auto_generated_function.AppendString(sstr.GetData()); } auto_generated_function.AppendString( " for key in new_keys:"); // Iterate over all the keys from session // dict auto_generated_function.AppendString( " internal_dict[key] = global_dict[key]"); // Update session dict // values auto_generated_function.AppendString( " if key not in old_keys:"); // If key was not originally in // global dict auto_generated_function.AppendString( " del global_dict[key]"); // ...then remove key/value from // global dict // Verify that the results are valid Python. error = ExportFunctionDefinitionToInterpreter(auto_generated_function); return error; } bool ScriptInterpreterPython::GenerateTypeScriptFunction( StringList &user_input, std::string &output, const void *name_token) { static uint32_t num_created_functions = 0; user_input.RemoveBlankLines(); StreamString sstr; // Check to see if we have any data; if not, just return. if (user_input.GetSize() == 0) return false; // Take what the user wrote, wrap it all up inside one big auto-generated // Python function, passing in the // ValueObject as parameter to the function. std::string auto_generated_function_name( GenerateUniqueName("lldb_autogen_python_type_print_func", num_created_functions, name_token)); sstr.Printf("def %s (valobj, internal_dict):", auto_generated_function_name.c_str()); if (!GenerateFunction(sstr.GetData(), user_input).Success()) return false; // Store the name of the auto-generated function to be called. output.assign(auto_generated_function_name); return true; } bool ScriptInterpreterPython::GenerateScriptAliasFunction( StringList &user_input, std::string &output) { static uint32_t num_created_functions = 0; user_input.RemoveBlankLines(); StreamString sstr; // Check to see if we have any data; if not, just return. if (user_input.GetSize() == 0) return false; std::string auto_generated_function_name(GenerateUniqueName( "lldb_autogen_python_cmd_alias_func", num_created_functions)); sstr.Printf("def %s (debugger, args, result, internal_dict):", auto_generated_function_name.c_str()); if (!GenerateFunction(sstr.GetData(), user_input).Success()) return false; // Store the name of the auto-generated function to be called. output.assign(auto_generated_function_name); return true; } bool ScriptInterpreterPython::GenerateTypeSynthClass(StringList &user_input, std::string &output, const void *name_token) { static uint32_t num_created_classes = 0; user_input.RemoveBlankLines(); int num_lines = user_input.GetSize(); StreamString sstr; // Check to see if we have any data; if not, just return. if (user_input.GetSize() == 0) return false; // Wrap all user input into a Python class std::string auto_generated_class_name(GenerateUniqueName( "lldb_autogen_python_type_synth_class", num_created_classes, name_token)); StringList auto_generated_class; // Create the function name & definition string. sstr.Printf("class %s:", auto_generated_class_name.c_str()); auto_generated_class.AppendString(sstr.GetString()); // Wrap everything up inside the class, increasing the indentation. // we don't need to play any fancy indentation tricks here because there is no // surrounding code whose indentation we need to honor for (int i = 0; i < num_lines; ++i) { sstr.Clear(); sstr.Printf(" %s", user_input.GetStringAtIndex(i)); auto_generated_class.AppendString(sstr.GetString()); } // Verify that the results are valid Python. // (even though the method is ExportFunctionDefinitionToInterpreter, a class // will actually be exported) // (TODO: rename that method to ExportDefinitionToInterpreter) if (!ExportFunctionDefinitionToInterpreter(auto_generated_class).Success()) return false; // Store the name of the auto-generated class output.assign(auto_generated_class_name); return true; } StructuredData::GenericSP ScriptInterpreterPython::OSPlugin_CreatePluginObject( const char *class_name, lldb::ProcessSP process_sp) { if (class_name == nullptr || class_name[0] == '\0') return StructuredData::GenericSP(); if (!process_sp) return StructuredData::GenericSP(); void *ret_val; { Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); ret_val = g_swig_create_os_plugin(class_name, m_dictionary_name.c_str(), process_sp); } return StructuredData::GenericSP(new StructuredPythonObject(ret_val)); } StructuredData::DictionarySP ScriptInterpreterPython::OSPlugin_RegisterInfo( StructuredData::ObjectSP os_plugin_object_sp) { Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); static char callee_name[] = "get_register_info"; if (!os_plugin_object_sp) return StructuredData::DictionarySP(); StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric(); if (!generic) return nullptr; PythonObject implementor(PyRefType::Borrowed, (PyObject *)generic->GetValue()); if (!implementor.IsAllocated()) return StructuredData::DictionarySP(); PythonObject pmeth(PyRefType::Owned, PyObject_GetAttrString(implementor.get(), callee_name)); if (PyErr_Occurred()) PyErr_Clear(); if (!pmeth.IsAllocated()) return StructuredData::DictionarySP(); if (PyCallable_Check(pmeth.get()) == 0) { if (PyErr_Occurred()) PyErr_Clear(); return StructuredData::DictionarySP(); } if (PyErr_Occurred()) PyErr_Clear(); // right now we know this function exists and is callable.. PythonObject py_return( PyRefType::Owned, PyObject_CallMethod(implementor.get(), callee_name, nullptr)); // if it fails, print the error but otherwise go on if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } if (py_return.get()) { PythonDictionary result_dict(PyRefType::Borrowed, py_return.get()); return result_dict.CreateStructuredDictionary(); } return StructuredData::DictionarySP(); } StructuredData::ArraySP ScriptInterpreterPython::OSPlugin_ThreadsInfo( StructuredData::ObjectSP os_plugin_object_sp) { Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); static char callee_name[] = "get_thread_info"; if (!os_plugin_object_sp) return StructuredData::ArraySP(); StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric(); if (!generic) return nullptr; PythonObject implementor(PyRefType::Borrowed, (PyObject *)generic->GetValue()); if (!implementor.IsAllocated()) return StructuredData::ArraySP(); PythonObject pmeth(PyRefType::Owned, PyObject_GetAttrString(implementor.get(), callee_name)); if (PyErr_Occurred()) PyErr_Clear(); if (!pmeth.IsAllocated()) return StructuredData::ArraySP(); if (PyCallable_Check(pmeth.get()) == 0) { if (PyErr_Occurred()) PyErr_Clear(); return StructuredData::ArraySP(); } if (PyErr_Occurred()) PyErr_Clear(); // right now we know this function exists and is callable.. PythonObject py_return( PyRefType::Owned, PyObject_CallMethod(implementor.get(), callee_name, nullptr)); // if it fails, print the error but otherwise go on if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } if (py_return.get()) { PythonList result_list(PyRefType::Borrowed, py_return.get()); return result_list.CreateStructuredArray(); } return StructuredData::ArraySP(); } // GetPythonValueFormatString provides a system independent type safe way to // convert a variable's type into a python value format. Python value formats // are defined in terms of builtin C types and could change from system to // as the underlying typedef for uint* types, size_t, off_t and other values // change. -template const char *GetPythonValueFormatString(T t) { - assert(!"Unhandled type passed to GetPythonValueFormatString(T), make a " - "specialization of GetPythonValueFormatString() to support this " - "type."); - return nullptr; -} +template const char *GetPythonValueFormatString(T t); template <> const char *GetPythonValueFormatString(char *) { return "s"; } template <> const char *GetPythonValueFormatString(char) { return "b"; } template <> const char *GetPythonValueFormatString(unsigned char) { return "B"; } template <> const char *GetPythonValueFormatString(short) { return "h"; } template <> const char *GetPythonValueFormatString(unsigned short) { return "H"; } template <> const char *GetPythonValueFormatString(int) { return "i"; } template <> const char *GetPythonValueFormatString(unsigned int) { return "I"; } template <> const char *GetPythonValueFormatString(long) { return "l"; } template <> const char *GetPythonValueFormatString(unsigned long) { return "k"; } template <> const char *GetPythonValueFormatString(long long) { return "L"; } template <> const char *GetPythonValueFormatString(unsigned long long) { return "K"; } template <> const char *GetPythonValueFormatString(float t) { return "f"; } template <> const char *GetPythonValueFormatString(double t) { return "d"; } StructuredData::StringSP ScriptInterpreterPython::OSPlugin_RegisterContextData( StructuredData::ObjectSP os_plugin_object_sp, lldb::tid_t tid) { Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); static char callee_name[] = "get_register_data"; static char *param_format = const_cast(GetPythonValueFormatString(tid)); if (!os_plugin_object_sp) return StructuredData::StringSP(); StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric(); if (!generic) return nullptr; PythonObject implementor(PyRefType::Borrowed, (PyObject *)generic->GetValue()); if (!implementor.IsAllocated()) return StructuredData::StringSP(); PythonObject pmeth(PyRefType::Owned, PyObject_GetAttrString(implementor.get(), callee_name)); if (PyErr_Occurred()) PyErr_Clear(); if (!pmeth.IsAllocated()) return StructuredData::StringSP(); if (PyCallable_Check(pmeth.get()) == 0) { if (PyErr_Occurred()) PyErr_Clear(); return StructuredData::StringSP(); } if (PyErr_Occurred()) PyErr_Clear(); // right now we know this function exists and is callable.. PythonObject py_return( PyRefType::Owned, PyObject_CallMethod(implementor.get(), callee_name, param_format, tid)); // if it fails, print the error but otherwise go on if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } if (py_return.get()) { PythonBytes result(PyRefType::Borrowed, py_return.get()); return result.CreateStructuredString(); } return StructuredData::StringSP(); } StructuredData::DictionarySP ScriptInterpreterPython::OSPlugin_CreateThread( StructuredData::ObjectSP os_plugin_object_sp, lldb::tid_t tid, lldb::addr_t context) { Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); static char callee_name[] = "create_thread"; std::string param_format; param_format += GetPythonValueFormatString(tid); param_format += GetPythonValueFormatString(context); if (!os_plugin_object_sp) return StructuredData::DictionarySP(); StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric(); if (!generic) return nullptr; PythonObject implementor(PyRefType::Borrowed, (PyObject *)generic->GetValue()); if (!implementor.IsAllocated()) return StructuredData::DictionarySP(); PythonObject pmeth(PyRefType::Owned, PyObject_GetAttrString(implementor.get(), callee_name)); if (PyErr_Occurred()) PyErr_Clear(); if (!pmeth.IsAllocated()) return StructuredData::DictionarySP(); if (PyCallable_Check(pmeth.get()) == 0) { if (PyErr_Occurred()) PyErr_Clear(); return StructuredData::DictionarySP(); } if (PyErr_Occurred()) PyErr_Clear(); // right now we know this function exists and is callable.. PythonObject py_return(PyRefType::Owned, PyObject_CallMethod(implementor.get(), callee_name, ¶m_format[0], tid, context)); // if it fails, print the error but otherwise go on if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } if (py_return.get()) { PythonDictionary result_dict(PyRefType::Borrowed, py_return.get()); return result_dict.CreateStructuredDictionary(); } return StructuredData::DictionarySP(); } StructuredData::ObjectSP ScriptInterpreterPython::CreateScriptedThreadPlan( const char *class_name, lldb::ThreadPlanSP thread_plan_sp) { if (class_name == nullptr || class_name[0] == '\0') return StructuredData::ObjectSP(); if (!thread_plan_sp.get()) return StructuredData::ObjectSP(); Debugger &debugger = thread_plan_sp->GetTarget().GetDebugger(); ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); ScriptInterpreterPython *python_interpreter = static_cast(script_interpreter); if (!script_interpreter) return StructuredData::ObjectSP(); void *ret_val; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_thread_plan_script( class_name, python_interpreter->m_dictionary_name.c_str(), thread_plan_sp); } return StructuredData::ObjectSP(new StructuredPythonObject(ret_val)); } bool ScriptInterpreterPython::ScriptedThreadPlanExplainsStop( StructuredData::ObjectSP implementor_sp, Event *event, bool &script_error) { bool explains_stop = true; StructuredData::Generic *generic = nullptr; if (implementor_sp) generic = implementor_sp->GetAsGeneric(); if (generic) { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); explains_stop = g_swig_call_thread_plan( generic->GetValue(), "explains_stop", event, script_error); if (script_error) return true; } return explains_stop; } bool ScriptInterpreterPython::ScriptedThreadPlanShouldStop( StructuredData::ObjectSP implementor_sp, Event *event, bool &script_error) { bool should_stop = true; StructuredData::Generic *generic = nullptr; if (implementor_sp) generic = implementor_sp->GetAsGeneric(); if (generic) { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); should_stop = g_swig_call_thread_plan(generic->GetValue(), "should_stop", event, script_error); if (script_error) return true; } return should_stop; } bool ScriptInterpreterPython::ScriptedThreadPlanIsStale( StructuredData::ObjectSP implementor_sp, bool &script_error) { bool is_stale = true; StructuredData::Generic *generic = nullptr; if (implementor_sp) generic = implementor_sp->GetAsGeneric(); if (generic) { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); is_stale = g_swig_call_thread_plan(generic->GetValue(), "is_stale", nullptr, script_error); if (script_error) return true; } return is_stale; } lldb::StateType ScriptInterpreterPython::ScriptedThreadPlanGetRunState( StructuredData::ObjectSP implementor_sp, bool &script_error) { bool should_step = false; StructuredData::Generic *generic = nullptr; if (implementor_sp) generic = implementor_sp->GetAsGeneric(); if (generic) { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); should_step = g_swig_call_thread_plan(generic->GetValue(), "should_step", NULL, script_error); if (script_error) should_step = true; } if (should_step) return lldb::eStateStepping; else return lldb::eStateRunning; } StructuredData::ObjectSP ScriptInterpreterPython::LoadPluginModule(const FileSpec &file_spec, lldb_private::Error &error) { if (!file_spec.Exists()) { error.SetErrorString("no such file"); return StructuredData::ObjectSP(); } StructuredData::ObjectSP module_sp; if (LoadScriptingModule(file_spec.GetPath().c_str(), true, true, error, &module_sp)) return module_sp; return StructuredData::ObjectSP(); } StructuredData::DictionarySP ScriptInterpreterPython::GetDynamicSettings( StructuredData::ObjectSP plugin_module_sp, Target *target, const char *setting_name, lldb_private::Error &error) { if (!plugin_module_sp || !target || !setting_name || !setting_name[0] || !g_swig_plugin_get) return StructuredData::DictionarySP(); StructuredData::Generic *generic = plugin_module_sp->GetAsGeneric(); if (!generic) return StructuredData::DictionarySP(); PythonObject reply_pyobj; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); TargetSP target_sp(target->shared_from_this()); reply_pyobj.Reset(PyRefType::Owned, (PyObject *)g_swig_plugin_get(generic->GetValue(), setting_name, target_sp)); } PythonDictionary py_dict(PyRefType::Borrowed, reply_pyobj.get()); return py_dict.CreateStructuredDictionary(); } StructuredData::ObjectSP ScriptInterpreterPython::CreateSyntheticScriptedProvider( const char *class_name, lldb::ValueObjectSP valobj) { if (class_name == nullptr || class_name[0] == '\0') return StructuredData::ObjectSP(); if (!valobj.get()) return StructuredData::ObjectSP(); ExecutionContext exe_ctx(valobj->GetExecutionContextRef()); Target *target = exe_ctx.GetTargetPtr(); if (!target) return StructuredData::ObjectSP(); Debugger &debugger = target->GetDebugger(); ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *)script_interpreter; if (!script_interpreter) return StructuredData::ObjectSP(); void *ret_val = nullptr; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_synthetic_script( class_name, python_interpreter->m_dictionary_name.c_str(), valobj); } return StructuredData::ObjectSP(new StructuredPythonObject(ret_val)); } StructuredData::GenericSP ScriptInterpreterPython::CreateScriptCommandObject(const char *class_name) { DebuggerSP debugger_sp( GetCommandInterpreter().GetDebugger().shared_from_this()); if (class_name == nullptr || class_name[0] == '\0') return StructuredData::GenericSP(); if (!debugger_sp.get()) return StructuredData::GenericSP(); void *ret_val; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_create_cmd(class_name, m_dictionary_name.c_str(), debugger_sp); } return StructuredData::GenericSP(new StructuredPythonObject(ret_val)); } bool ScriptInterpreterPython::GenerateTypeScriptFunction( const char *oneliner, std::string &output, const void *name_token) { StringList input; input.SplitIntoLines(oneliner, strlen(oneliner)); return GenerateTypeScriptFunction(input, output, name_token); } bool ScriptInterpreterPython::GenerateTypeSynthClass(const char *oneliner, std::string &output, const void *name_token) { StringList input; input.SplitIntoLines(oneliner, strlen(oneliner)); return GenerateTypeSynthClass(input, output, name_token); } Error ScriptInterpreterPython::GenerateBreakpointCommandCallbackData( StringList &user_input, std::string &output) { static uint32_t num_created_functions = 0; user_input.RemoveBlankLines(); StreamString sstr; Error error; if (user_input.GetSize() == 0) { error.SetErrorString("No input data."); return error; } std::string auto_generated_function_name(GenerateUniqueName( "lldb_autogen_python_bp_callback_func_", num_created_functions)); sstr.Printf("def %s (frame, bp_loc, internal_dict):", auto_generated_function_name.c_str()); error = GenerateFunction(sstr.GetData(), user_input); if (!error.Success()) return error; // Store the name of the auto-generated function to be called. output.assign(auto_generated_function_name); return error; } bool ScriptInterpreterPython::GenerateWatchpointCommandCallbackData( StringList &user_input, std::string &output) { static uint32_t num_created_functions = 0; user_input.RemoveBlankLines(); StreamString sstr; if (user_input.GetSize() == 0) return false; std::string auto_generated_function_name(GenerateUniqueName( "lldb_autogen_python_wp_callback_func_", num_created_functions)); sstr.Printf("def %s (frame, wp, internal_dict):", auto_generated_function_name.c_str()); if (!GenerateFunction(sstr.GetData(), user_input).Success()) return false; // Store the name of the auto-generated function to be called. output.assign(auto_generated_function_name); return true; } bool ScriptInterpreterPython::GetScriptedSummary( const char *python_function_name, lldb::ValueObjectSP valobj, StructuredData::ObjectSP &callee_wrapper_sp, const TypeSummaryOptions &options, std::string &retval) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, LLVM_PRETTY_FUNCTION); if (!valobj.get()) { retval.assign(""); return false; } void *old_callee = nullptr; StructuredData::Generic *generic = nullptr; if (callee_wrapper_sp) { generic = callee_wrapper_sp->GetAsGeneric(); if (generic) old_callee = generic->GetValue(); } void *new_callee = old_callee; bool ret_val; if (python_function_name && *python_function_name) { { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); { TypeSummaryOptionsSP options_sp(new TypeSummaryOptions(options)); Timer scoped_timer("g_swig_typescript_callback", "g_swig_typescript_callback"); ret_val = g_swig_typescript_callback( python_function_name, GetSessionDictionary().get(), valobj, &new_callee, options_sp, retval); } } } else { retval.assign(""); return false; } if (new_callee && old_callee != new_callee) callee_wrapper_sp.reset(new StructuredPythonObject(new_callee)); return ret_val; } void ScriptInterpreterPython::Clear() { // Release any global variables that might have strong references to // LLDB objects when clearing the python script interpreter. Locker locker(this, ScriptInterpreterPython::Locker::AcquireLock, ScriptInterpreterPython::Locker::FreeAcquiredLock); // This may be called as part of Py_Finalize. In that case the modules are // destroyed in random // order and we can't guarantee that we can access these. if (Py_IsInitialized()) PyRun_SimpleString("lldb.debugger = None; lldb.target = None; lldb.process " "= None; lldb.thread = None; lldb.frame = None"); } bool ScriptInterpreterPython::BreakpointCallbackFunction( void *baton, StoppointCallbackContext *context, user_id_t break_id, user_id_t break_loc_id) { CommandDataPython *bp_option_data = (CommandDataPython *)baton; const char *python_function_name = bp_option_data->script_source.c_str(); if (!context) return true; ExecutionContext exe_ctx(context->exe_ctx_ref); Target *target = exe_ctx.GetTargetPtr(); if (!target) return true; Debugger &debugger = target->GetDebugger(); ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *)script_interpreter; if (!script_interpreter) return true; if (python_function_name && python_function_name[0]) { const StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP()); BreakpointSP breakpoint_sp = target->GetBreakpointByID(break_id); if (breakpoint_sp) { const BreakpointLocationSP bp_loc_sp( breakpoint_sp->FindLocationByID(break_loc_id)); if (stop_frame_sp && bp_loc_sp) { bool ret_val = true; { Locker py_lock(python_interpreter, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_breakpoint_callback( python_function_name, python_interpreter->m_dictionary_name.c_str(), stop_frame_sp, bp_loc_sp); } return ret_val; } } } // We currently always true so we stop in case anything goes wrong when // trying to call the script function return true; } bool ScriptInterpreterPython::WatchpointCallbackFunction( void *baton, StoppointCallbackContext *context, user_id_t watch_id) { WatchpointOptions::CommandData *wp_option_data = (WatchpointOptions::CommandData *)baton; const char *python_function_name = wp_option_data->script_source.c_str(); if (!context) return true; ExecutionContext exe_ctx(context->exe_ctx_ref); Target *target = exe_ctx.GetTargetPtr(); if (!target) return true; Debugger &debugger = target->GetDebugger(); ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *)script_interpreter; if (!script_interpreter) return true; if (python_function_name && python_function_name[0]) { const StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP()); WatchpointSP wp_sp = target->GetWatchpointList().FindByID(watch_id); if (wp_sp) { if (stop_frame_sp && wp_sp) { bool ret_val = true; { Locker py_lock(python_interpreter, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_watchpoint_callback( python_function_name, python_interpreter->m_dictionary_name.c_str(), stop_frame_sp, wp_sp); } return ret_val; } } } // We currently always true so we stop in case anything goes wrong when // trying to call the script function return true; } size_t ScriptInterpreterPython::CalculateNumChildren( const StructuredData::ObjectSP &implementor_sp, uint32_t max) { if (!implementor_sp) return 0; StructuredData::Generic *generic = implementor_sp->GetAsGeneric(); if (!generic) return 0; void *implementor = generic->GetValue(); if (!implementor) return 0; if (!g_swig_calc_children) return 0; size_t ret_val = 0; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_calc_children(implementor, max); } return ret_val; } lldb::ValueObjectSP ScriptInterpreterPython::GetChildAtIndex( const StructuredData::ObjectSP &implementor_sp, uint32_t idx) { if (!implementor_sp) return lldb::ValueObjectSP(); StructuredData::Generic *generic = implementor_sp->GetAsGeneric(); if (!generic) return lldb::ValueObjectSP(); void *implementor = generic->GetValue(); if (!implementor) return lldb::ValueObjectSP(); if (!g_swig_get_child_index || !g_swig_cast_to_sbvalue) return lldb::ValueObjectSP(); lldb::ValueObjectSP ret_val; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); void *child_ptr = g_swig_get_child_index(implementor, idx); if (child_ptr != nullptr && child_ptr != Py_None) { lldb::SBValue *sb_value_ptr = (lldb::SBValue *)g_swig_cast_to_sbvalue(child_ptr); if (sb_value_ptr == nullptr) Py_XDECREF(child_ptr); else ret_val = g_swig_get_valobj_sp_from_sbvalue(sb_value_ptr); } else { Py_XDECREF(child_ptr); } } return ret_val; } int ScriptInterpreterPython::GetIndexOfChildWithName( const StructuredData::ObjectSP &implementor_sp, const char *child_name) { if (!implementor_sp) return UINT32_MAX; StructuredData::Generic *generic = implementor_sp->GetAsGeneric(); if (!generic) return UINT32_MAX; void *implementor = generic->GetValue(); if (!implementor) return UINT32_MAX; if (!g_swig_get_index_child) return UINT32_MAX; int ret_val = UINT32_MAX; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_get_index_child(implementor, child_name); } return ret_val; } bool ScriptInterpreterPython::UpdateSynthProviderInstance( const StructuredData::ObjectSP &implementor_sp) { bool ret_val = false; if (!implementor_sp) return ret_val; StructuredData::Generic *generic = implementor_sp->GetAsGeneric(); if (!generic) return ret_val; void *implementor = generic->GetValue(); if (!implementor) return ret_val; if (!g_swig_update_provider) return ret_val; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_update_provider(implementor); } return ret_val; } bool ScriptInterpreterPython::MightHaveChildrenSynthProviderInstance( const StructuredData::ObjectSP &implementor_sp) { bool ret_val = false; if (!implementor_sp) return ret_val; StructuredData::Generic *generic = implementor_sp->GetAsGeneric(); if (!generic) return ret_val; void *implementor = generic->GetValue(); if (!implementor) return ret_val; if (!g_swig_mighthavechildren_provider) return ret_val; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_mighthavechildren_provider(implementor); } return ret_val; } lldb::ValueObjectSP ScriptInterpreterPython::GetSyntheticValue( const StructuredData::ObjectSP &implementor_sp) { lldb::ValueObjectSP ret_val(nullptr); if (!implementor_sp) return ret_val; StructuredData::Generic *generic = implementor_sp->GetAsGeneric(); if (!generic) return ret_val; void *implementor = generic->GetValue(); if (!implementor) return ret_val; if (!g_swig_getvalue_provider || !g_swig_cast_to_sbvalue || !g_swig_get_valobj_sp_from_sbvalue) return ret_val; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); void *child_ptr = g_swig_getvalue_provider(implementor); if (child_ptr != nullptr && child_ptr != Py_None) { lldb::SBValue *sb_value_ptr = (lldb::SBValue *)g_swig_cast_to_sbvalue(child_ptr); if (sb_value_ptr == nullptr) Py_XDECREF(child_ptr); else ret_val = g_swig_get_valobj_sp_from_sbvalue(sb_value_ptr); } else { Py_XDECREF(child_ptr); } } return ret_val; } ConstString ScriptInterpreterPython::GetSyntheticTypeName( const StructuredData::ObjectSP &implementor_sp) { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); static char callee_name[] = "get_type_name"; ConstString ret_val; bool got_string = false; std::string buffer; if (!implementor_sp) return ret_val; StructuredData::Generic *generic = implementor_sp->GetAsGeneric(); if (!generic) return ret_val; PythonObject implementor(PyRefType::Borrowed, (PyObject *)generic->GetValue()); if (!implementor.IsAllocated()) return ret_val; PythonObject pmeth(PyRefType::Owned, PyObject_GetAttrString(implementor.get(), callee_name)); if (PyErr_Occurred()) PyErr_Clear(); if (!pmeth.IsAllocated()) return ret_val; if (PyCallable_Check(pmeth.get()) == 0) { if (PyErr_Occurred()) PyErr_Clear(); return ret_val; } if (PyErr_Occurred()) PyErr_Clear(); // right now we know this function exists and is callable.. PythonObject py_return( PyRefType::Owned, PyObject_CallMethod(implementor.get(), callee_name, nullptr)); // if it fails, print the error but otherwise go on if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } if (py_return.IsAllocated() && PythonString::Check(py_return.get())) { PythonString py_string(PyRefType::Borrowed, py_return.get()); llvm::StringRef return_data(py_string.GetString()); if (!return_data.empty()) { buffer.assign(return_data.data(), return_data.size()); got_string = true; } } if (got_string) ret_val.SetCStringWithLength(buffer.c_str(), buffer.size()); return ret_val; } bool ScriptInterpreterPython::RunScriptFormatKeyword(const char *impl_function, Process *process, std::string &output, Error &error) { bool ret_val; if (!process) { error.SetErrorString("no process"); return false; } if (!impl_function || !impl_function[0]) { error.SetErrorString("no function to execute"); return false; } if (!g_swig_run_script_keyword_process) { error.SetErrorString("internal helper function missing"); return false; } { ProcessSP process_sp(process->shared_from_this()); Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_run_script_keyword_process( impl_function, m_dictionary_name.c_str(), process_sp, output); if (!ret_val) error.SetErrorString("python script evaluation failed"); } return ret_val; } bool ScriptInterpreterPython::RunScriptFormatKeyword(const char *impl_function, Thread *thread, std::string &output, Error &error) { bool ret_val; if (!thread) { error.SetErrorString("no thread"); return false; } if (!impl_function || !impl_function[0]) { error.SetErrorString("no function to execute"); return false; } if (!g_swig_run_script_keyword_thread) { error.SetErrorString("internal helper function missing"); return false; } { ThreadSP thread_sp(thread->shared_from_this()); Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_run_script_keyword_thread( impl_function, m_dictionary_name.c_str(), thread_sp, output); if (!ret_val) error.SetErrorString("python script evaluation failed"); } return ret_val; } bool ScriptInterpreterPython::RunScriptFormatKeyword(const char *impl_function, Target *target, std::string &output, Error &error) { bool ret_val; if (!target) { error.SetErrorString("no thread"); return false; } if (!impl_function || !impl_function[0]) { error.SetErrorString("no function to execute"); return false; } if (!g_swig_run_script_keyword_target) { error.SetErrorString("internal helper function missing"); return false; } { TargetSP target_sp(target->shared_from_this()); Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_run_script_keyword_target( impl_function, m_dictionary_name.c_str(), target_sp, output); if (!ret_val) error.SetErrorString("python script evaluation failed"); } return ret_val; } bool ScriptInterpreterPython::RunScriptFormatKeyword(const char *impl_function, StackFrame *frame, std::string &output, Error &error) { bool ret_val; if (!frame) { error.SetErrorString("no frame"); return false; } if (!impl_function || !impl_function[0]) { error.SetErrorString("no function to execute"); return false; } if (!g_swig_run_script_keyword_frame) { error.SetErrorString("internal helper function missing"); return false; } { StackFrameSP frame_sp(frame->shared_from_this()); Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_run_script_keyword_frame( impl_function, m_dictionary_name.c_str(), frame_sp, output); if (!ret_val) error.SetErrorString("python script evaluation failed"); } return ret_val; } bool ScriptInterpreterPython::RunScriptFormatKeyword(const char *impl_function, ValueObject *value, std::string &output, Error &error) { bool ret_val; if (!value) { error.SetErrorString("no value"); return false; } if (!impl_function || !impl_function[0]) { error.SetErrorString("no function to execute"); return false; } if (!g_swig_run_script_keyword_value) { error.SetErrorString("internal helper function missing"); return false; } { ValueObjectSP value_sp(value->GetSP()); Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); ret_val = g_swig_run_script_keyword_value( impl_function, m_dictionary_name.c_str(), value_sp, output); if (!ret_val) error.SetErrorString("python script evaluation failed"); } return ret_val; } uint64_t replace_all(std::string &str, const std::string &oldStr, const std::string &newStr) { size_t pos = 0; uint64_t matches = 0; while ((pos = str.find(oldStr, pos)) != std::string::npos) { matches++; str.replace(pos, oldStr.length(), newStr); pos += newStr.length(); } return matches; } bool ScriptInterpreterPython::LoadScriptingModule( const char *pathname, bool can_reload, bool init_session, lldb_private::Error &error, StructuredData::ObjectSP *module_sp) { if (!pathname || !pathname[0]) { error.SetErrorString("invalid pathname"); return false; } if (!g_swig_call_module_init) { error.SetErrorString("internal helper function missing"); return false; } lldb::DebuggerSP debugger_sp = m_interpreter.GetDebugger().shared_from_this(); { FileSpec target_file(pathname, true); std::string basename(target_file.GetFilename().GetCString()); StreamString command_stream; // Before executing Python code, lock the GIL. Locker py_lock(this, Locker::AcquireLock | (init_session ? Locker::InitSession : 0) | Locker::NoSTDIN, Locker::FreeAcquiredLock | (init_session ? Locker::TearDownSession : 0)); if (target_file.GetFileType() == FileSpec::eFileTypeInvalid || target_file.GetFileType() == FileSpec::eFileTypeUnknown) { // if not a valid file of any sort, check if it might be a filename still // dot can't be used but / and \ can, and if either is found, reject if (strchr(pathname, '\\') || strchr(pathname, '/')) { error.SetErrorString("invalid pathname"); return false; } basename = pathname; // not a filename, probably a package of some sort, // let it go through } else if (target_file.GetFileType() == FileSpec::eFileTypeDirectory || target_file.GetFileType() == FileSpec::eFileTypeRegular || target_file.GetFileType() == FileSpec::eFileTypeSymbolicLink) { std::string directory = target_file.GetDirectory().GetCString(); replace_all(directory, "\\", "\\\\"); replace_all(directory, "'", "\\'"); // now make sure that Python has "directory" in the search path StreamString command_stream; command_stream.Printf("if not (sys.path.__contains__('%s')):\n " "sys.path.insert(1,'%s');\n\n", directory.c_str(), directory.c_str()); bool syspath_retval = ExecuteMultipleLines(command_stream.GetData(), ScriptInterpreter::ExecuteScriptOptions() .SetEnableIO(false) .SetSetLLDBGlobals(false)) .Success(); if (!syspath_retval) { error.SetErrorString("Python sys.path handling failed"); return false; } // strip .py or .pyc extension ConstString extension = target_file.GetFileNameExtension(); if (extension) { if (::strcmp(extension.GetCString(), "py") == 0) basename.resize(basename.length() - 3); else if (::strcmp(extension.GetCString(), "pyc") == 0) basename.resize(basename.length() - 4); } } else { error.SetErrorString("no known way to import this module specification"); return false; } // check if the module is already import-ed command_stream.Clear(); command_stream.Printf("sys.modules.__contains__('%s')", basename.c_str()); bool does_contain = false; // this call will succeed if the module was ever imported in any Debugger in // the lifetime of the process // in which this LLDB framework is living bool was_imported_globally = (ExecuteOneLineWithReturn( command_stream.GetData(), ScriptInterpreterPython::eScriptReturnTypeBool, &does_contain, ScriptInterpreter::ExecuteScriptOptions() .SetEnableIO(false) .SetSetLLDBGlobals(false)) && does_contain); // this call will fail if the module was not imported in this Debugger // before command_stream.Clear(); command_stream.Printf("sys.getrefcount(%s)", basename.c_str()); bool was_imported_locally = GetSessionDictionary() .GetItemForKey(PythonString(basename)) .IsAllocated(); bool was_imported = (was_imported_globally || was_imported_locally); if (was_imported == true && can_reload == false) { error.SetErrorString("module already imported"); return false; } // now actually do the import command_stream.Clear(); if (was_imported) { if (!was_imported_locally) command_stream.Printf("import %s ; reload_module(%s)", basename.c_str(), basename.c_str()); else command_stream.Printf("reload_module(%s)", basename.c_str()); } else command_stream.Printf("import %s", basename.c_str()); error = ExecuteMultipleLines(command_stream.GetData(), ScriptInterpreter::ExecuteScriptOptions() .SetEnableIO(false) .SetSetLLDBGlobals(false)); if (error.Fail()) return false; // if we are here, everything worked // call __lldb_init_module(debugger,dict) if (!g_swig_call_module_init(basename.c_str(), m_dictionary_name.c_str(), debugger_sp)) { error.SetErrorString("calling __lldb_init_module failed"); return false; } if (module_sp) { // everything went just great, now set the module object command_stream.Clear(); command_stream.Printf("%s", basename.c_str()); void *module_pyobj = nullptr; if (ExecuteOneLineWithReturn( command_stream.GetData(), ScriptInterpreter::eScriptReturnTypeOpaqueObject, &module_pyobj) && module_pyobj) module_sp->reset(new StructuredPythonObject(module_pyobj)); } return true; } } bool ScriptInterpreterPython::IsReservedWord(const char *word) { if (!word || !word[0]) return false; llvm::StringRef word_sr(word); // filter out a few characters that would just confuse us // and that are clearly not keyword material anyway if (word_sr.find_first_of("'\"") != llvm::StringRef::npos) return false; StreamString command_stream; command_stream.Printf("keyword.iskeyword('%s')", word); bool result; ExecuteScriptOptions options; options.SetEnableIO(false); options.SetMaskoutErrors(true); options.SetSetLLDBGlobals(false); if (ExecuteOneLineWithReturn(command_stream.GetData(), ScriptInterpreter::eScriptReturnTypeBool, &result, options)) return result; return false; } ScriptInterpreterPython::SynchronicityHandler::SynchronicityHandler( lldb::DebuggerSP debugger_sp, ScriptedCommandSynchronicity synchro) : m_debugger_sp(debugger_sp), m_synch_wanted(synchro), m_old_asynch(debugger_sp->GetAsyncExecution()) { if (m_synch_wanted == eScriptedCommandSynchronicitySynchronous) m_debugger_sp->SetAsyncExecution(false); else if (m_synch_wanted == eScriptedCommandSynchronicityAsynchronous) m_debugger_sp->SetAsyncExecution(true); } ScriptInterpreterPython::SynchronicityHandler::~SynchronicityHandler() { if (m_synch_wanted != eScriptedCommandSynchronicityCurrentValue) m_debugger_sp->SetAsyncExecution(m_old_asynch); } bool ScriptInterpreterPython::RunScriptBasedCommand( const char *impl_function, const char *args, ScriptedCommandSynchronicity synchronicity, lldb_private::CommandReturnObject &cmd_retobj, Error &error, const lldb_private::ExecutionContext &exe_ctx) { if (!impl_function) { error.SetErrorString("no function to execute"); return false; } if (!g_swig_call_command) { error.SetErrorString("no helper function to run scripted commands"); return false; } lldb::DebuggerSP debugger_sp = m_interpreter.GetDebugger().shared_from_this(); lldb::ExecutionContextRefSP exe_ctx_ref_sp(new ExecutionContextRef(exe_ctx)); if (!debugger_sp.get()) { error.SetErrorString("invalid Debugger pointer"); return false; } bool ret_val = false; std::string err_msg; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | (cmd_retobj.GetInteractive() ? 0 : Locker::NoSTDIN), Locker::FreeLock | Locker::TearDownSession); SynchronicityHandler synch_handler(debugger_sp, synchronicity); ret_val = g_swig_call_command(impl_function, m_dictionary_name.c_str(), debugger_sp, args, cmd_retobj, exe_ctx_ref_sp); } if (!ret_val) error.SetErrorString("unable to execute script function"); else error.Clear(); return ret_val; } bool ScriptInterpreterPython::RunScriptBasedCommand( StructuredData::GenericSP impl_obj_sp, const char *args, ScriptedCommandSynchronicity synchronicity, lldb_private::CommandReturnObject &cmd_retobj, Error &error, const lldb_private::ExecutionContext &exe_ctx) { if (!impl_obj_sp || !impl_obj_sp->IsValid()) { error.SetErrorString("no function to execute"); return false; } if (!g_swig_call_command_object) { error.SetErrorString("no helper function to run scripted commands"); return false; } lldb::DebuggerSP debugger_sp = m_interpreter.GetDebugger().shared_from_this(); lldb::ExecutionContextRefSP exe_ctx_ref_sp(new ExecutionContextRef(exe_ctx)); if (!debugger_sp.get()) { error.SetErrorString("invalid Debugger pointer"); return false; } bool ret_val = false; std::string err_msg; { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | (cmd_retobj.GetInteractive() ? 0 : Locker::NoSTDIN), Locker::FreeLock | Locker::TearDownSession); SynchronicityHandler synch_handler(debugger_sp, synchronicity); ret_val = g_swig_call_command_object(impl_obj_sp->GetValue(), debugger_sp, args, cmd_retobj, exe_ctx_ref_sp); } if (!ret_val) error.SetErrorString("unable to execute script function"); else error.Clear(); return ret_val; } // in Python, a special attribute __doc__ contains the docstring // for an object (function, method, class, ...) if any is defined // Otherwise, the attribute's value is None bool ScriptInterpreterPython::GetDocumentationForItem(const char *item, std::string &dest) { dest.clear(); if (!item || !*item) return false; std::string command(item); command += ".__doc__"; char *result_ptr = nullptr; // Python is going to point this to valid data if // ExecuteOneLineWithReturn returns successfully if (ExecuteOneLineWithReturn( command.c_str(), ScriptInterpreter::eScriptReturnTypeCharStrOrNone, &result_ptr, ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false))) { if (result_ptr) dest.assign(result_ptr); return true; } else { StreamString str_stream; str_stream.Printf( "Function %s was not found. Containing module might be missing.", item); dest = str_stream.GetString(); return false; } } bool ScriptInterpreterPython::GetShortHelpForCommandObject( StructuredData::GenericSP cmd_obj_sp, std::string &dest) { bool got_string = false; dest.clear(); Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); static char callee_name[] = "get_short_help"; if (!cmd_obj_sp) return false; PythonObject implementor(PyRefType::Borrowed, (PyObject *)cmd_obj_sp->GetValue()); if (!implementor.IsAllocated()) return false; PythonObject pmeth(PyRefType::Owned, PyObject_GetAttrString(implementor.get(), callee_name)); if (PyErr_Occurred()) PyErr_Clear(); if (!pmeth.IsAllocated()) return false; if (PyCallable_Check(pmeth.get()) == 0) { if (PyErr_Occurred()) PyErr_Clear(); return false; } if (PyErr_Occurred()) PyErr_Clear(); // right now we know this function exists and is callable.. PythonObject py_return( PyRefType::Owned, PyObject_CallMethod(implementor.get(), callee_name, nullptr)); // if it fails, print the error but otherwise go on if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } if (py_return.IsAllocated() && PythonString::Check(py_return.get())) { PythonString py_string(PyRefType::Borrowed, py_return.get()); llvm::StringRef return_data(py_string.GetString()); dest.assign(return_data.data(), return_data.size()); got_string = true; } return got_string; } uint32_t ScriptInterpreterPython::GetFlagsForCommandObject( StructuredData::GenericSP cmd_obj_sp) { uint32_t result = 0; Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); static char callee_name[] = "get_flags"; if (!cmd_obj_sp) return result; PythonObject implementor(PyRefType::Borrowed, (PyObject *)cmd_obj_sp->GetValue()); if (!implementor.IsAllocated()) return result; PythonObject pmeth(PyRefType::Owned, PyObject_GetAttrString(implementor.get(), callee_name)); if (PyErr_Occurred()) PyErr_Clear(); if (!pmeth.IsAllocated()) return result; if (PyCallable_Check(pmeth.get()) == 0) { if (PyErr_Occurred()) PyErr_Clear(); return result; } if (PyErr_Occurred()) PyErr_Clear(); // right now we know this function exists and is callable.. PythonObject py_return( PyRefType::Owned, PyObject_CallMethod(implementor.get(), callee_name, nullptr)); // if it fails, print the error but otherwise go on if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } if (py_return.IsAllocated() && PythonInteger::Check(py_return.get())) { PythonInteger int_value(PyRefType::Borrowed, py_return.get()); result = int_value.GetInteger(); } return result; } bool ScriptInterpreterPython::GetLongHelpForCommandObject( StructuredData::GenericSP cmd_obj_sp, std::string &dest) { bool got_string = false; dest.clear(); Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); static char callee_name[] = "get_long_help"; if (!cmd_obj_sp) return false; PythonObject implementor(PyRefType::Borrowed, (PyObject *)cmd_obj_sp->GetValue()); if (!implementor.IsAllocated()) return false; PythonObject pmeth(PyRefType::Owned, PyObject_GetAttrString(implementor.get(), callee_name)); if (PyErr_Occurred()) PyErr_Clear(); if (!pmeth.IsAllocated()) return false; if (PyCallable_Check(pmeth.get()) == 0) { if (PyErr_Occurred()) PyErr_Clear(); return false; } if (PyErr_Occurred()) PyErr_Clear(); // right now we know this function exists and is callable.. PythonObject py_return( PyRefType::Owned, PyObject_CallMethod(implementor.get(), callee_name, nullptr)); // if it fails, print the error but otherwise go on if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } if (py_return.IsAllocated() && PythonString::Check(py_return.get())) { PythonString str(PyRefType::Borrowed, py_return.get()); llvm::StringRef str_data(str.GetString()); dest.assign(str_data.data(), str_data.size()); got_string = true; } return got_string; } std::unique_ptr ScriptInterpreterPython::AcquireInterpreterLock() { std::unique_ptr py_lock(new Locker( this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN, Locker::FreeLock | Locker::TearDownSession)); return py_lock; } void ScriptInterpreterPython::InitializeInterpreter( SWIGInitCallback swig_init_callback, SWIGBreakpointCallbackFunction swig_breakpoint_callback, SWIGWatchpointCallbackFunction swig_watchpoint_callback, SWIGPythonTypeScriptCallbackFunction swig_typescript_callback, SWIGPythonCreateSyntheticProvider swig_synthetic_script, SWIGPythonCreateCommandObject swig_create_cmd, SWIGPythonCalculateNumChildren swig_calc_children, SWIGPythonGetChildAtIndex swig_get_child_index, SWIGPythonGetIndexOfChildWithName swig_get_index_child, SWIGPythonCastPyObjectToSBValue swig_cast_to_sbvalue, SWIGPythonGetValueObjectSPFromSBValue swig_get_valobj_sp_from_sbvalue, SWIGPythonUpdateSynthProviderInstance swig_update_provider, SWIGPythonMightHaveChildrenSynthProviderInstance swig_mighthavechildren_provider, SWIGPythonGetValueSynthProviderInstance swig_getvalue_provider, SWIGPythonCallCommand swig_call_command, SWIGPythonCallCommandObject swig_call_command_object, SWIGPythonCallModuleInit swig_call_module_init, SWIGPythonCreateOSPlugin swig_create_os_plugin, SWIGPythonScriptKeyword_Process swig_run_script_keyword_process, SWIGPythonScriptKeyword_Thread swig_run_script_keyword_thread, SWIGPythonScriptKeyword_Target swig_run_script_keyword_target, SWIGPythonScriptKeyword_Frame swig_run_script_keyword_frame, SWIGPythonScriptKeyword_Value swig_run_script_keyword_value, SWIGPython_GetDynamicSetting swig_plugin_get, SWIGPythonCreateScriptedThreadPlan swig_thread_plan_script, SWIGPythonCallThreadPlan swig_call_thread_plan) { g_swig_init_callback = swig_init_callback; g_swig_breakpoint_callback = swig_breakpoint_callback; g_swig_watchpoint_callback = swig_watchpoint_callback; g_swig_typescript_callback = swig_typescript_callback; g_swig_synthetic_script = swig_synthetic_script; g_swig_create_cmd = swig_create_cmd; g_swig_calc_children = swig_calc_children; g_swig_get_child_index = swig_get_child_index; g_swig_get_index_child = swig_get_index_child; g_swig_cast_to_sbvalue = swig_cast_to_sbvalue; g_swig_get_valobj_sp_from_sbvalue = swig_get_valobj_sp_from_sbvalue; g_swig_update_provider = swig_update_provider; g_swig_mighthavechildren_provider = swig_mighthavechildren_provider; g_swig_getvalue_provider = swig_getvalue_provider; g_swig_call_command = swig_call_command; g_swig_call_command_object = swig_call_command_object; g_swig_call_module_init = swig_call_module_init; g_swig_create_os_plugin = swig_create_os_plugin; g_swig_run_script_keyword_process = swig_run_script_keyword_process; g_swig_run_script_keyword_thread = swig_run_script_keyword_thread; g_swig_run_script_keyword_target = swig_run_script_keyword_target; g_swig_run_script_keyword_frame = swig_run_script_keyword_frame; g_swig_run_script_keyword_value = swig_run_script_keyword_value; g_swig_plugin_get = swig_plugin_get; g_swig_thread_plan_script = swig_thread_plan_script; g_swig_call_thread_plan = swig_call_thread_plan; } void ScriptInterpreterPython::InitializePrivate() { if (g_initialized) return; g_initialized = true; Timer scoped_timer(LLVM_PRETTY_FUNCTION, LLVM_PRETTY_FUNCTION); // RAII-based initialization which correctly handles multiple-initialization, // version- // specific differences among Python 2 and Python 3, and saving and restoring // various // other pieces of state that can get mucked with during initialization. InitializePythonRAII initialize_guard; if (g_swig_init_callback) g_swig_init_callback(); // Update the path python uses to search for modules to include the current // directory. PyRun_SimpleString("import sys"); AddToSysPath(AddLocation::End, "."); FileSpec file_spec; // Don't denormalize paths when calling file_spec.GetPath(). On platforms // that use // a backslash as the path separator, this will result in executing python // code containing // paths with unescaped backslashes. But Python also accepts forward slashes, // so to make // life easier we just use that. if (HostInfo::GetLLDBPath(ePathTypePythonDir, file_spec)) AddToSysPath(AddLocation::Beginning, file_spec.GetPath(false)); if (HostInfo::GetLLDBPath(ePathTypeLLDBShlibDir, file_spec)) AddToSysPath(AddLocation::Beginning, file_spec.GetPath(false)); PyRun_SimpleString("sys.dont_write_bytecode = 1; import " "lldb.embedded_interpreter; from " "lldb.embedded_interpreter import run_python_interpreter; " "from lldb.embedded_interpreter import run_one_line"); } void ScriptInterpreterPython::AddToSysPath(AddLocation location, std::string path) { std::string path_copy; std::string statement; if (location == AddLocation::Beginning) { statement.assign("sys.path.insert(0,\""); statement.append(path); statement.append("\")"); } else { statement.assign("sys.path.append(\""); statement.append(path); statement.append("\")"); } PyRun_SimpleString(statement.c_str()); } // void // ScriptInterpreterPython::Terminate () //{ // // We are intentionally NOT calling Py_Finalize here (this would be the // logical place to call it). Calling // // Py_Finalize here causes test suite runs to seg fault: The test suite // runs in Python. It registers // // SBDebugger::Terminate to be called 'at_exit'. When the test suite // Python harness finishes up, it calls // // Py_Finalize, which calls all the 'at_exit' registered functions. // SBDebugger::Terminate calls Debugger::Terminate, // // which calls lldb::Terminate, which calls ScriptInterpreter::Terminate, // which calls // // ScriptInterpreterPython::Terminate. So if we call Py_Finalize here, we // end up with Py_Finalize being called from // // within Py_Finalize, which results in a seg fault. // // // // Since this function only gets called when lldb is shutting down and // going away anyway, the fact that we don't // // actually call Py_Finalize should not cause any problems (everything // should shut down/go away anyway when the // // process exits). // // //// Py_Finalize (); //} #endif // #ifdef LLDB_DISABLE_PYTHON Index: vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp (revision 311542) @@ -1,131 +1,130 @@ //===-- DWARFDebugRanges.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "DWARFDebugRanges.h" #include "SymbolFileDWARF.h" #include "lldb/Core/Stream.h" #include using namespace lldb_private; using namespace std; DWARFDebugRanges::DWARFDebugRanges() : m_range_map() {} DWARFDebugRanges::~DWARFDebugRanges() {} void DWARFDebugRanges::Extract(SymbolFileDWARF *dwarf2Data) { DWARFRangeList range_list; lldb::offset_t offset = 0; dw_offset_t debug_ranges_offset = offset; while (Extract(dwarf2Data, &offset, range_list)) { range_list.Sort(); m_range_map[debug_ranges_offset] = range_list; debug_ranges_offset = offset; } } bool DWARFDebugRanges::Extract(SymbolFileDWARF *dwarf2Data, lldb::offset_t *offset_ptr, DWARFRangeList &range_list) { range_list.Clear(); lldb::offset_t range_offset = *offset_ptr; const DWARFDataExtractor &debug_ranges_data = dwarf2Data->get_debug_ranges_data(); uint32_t addr_size = debug_ranges_data.GetAddressByteSize(); while ( debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) { dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); if (!begin && !end) { // End of range list break; } // Extend 4 byte addresses that consists of 32 bits of 1's to be 64 bits // of ones switch (addr_size) { case 2: if (begin == 0xFFFFull) begin = LLDB_INVALID_ADDRESS; break; case 4: if (begin == 0xFFFFFFFFull) begin = LLDB_INVALID_ADDRESS; break; case 8: break; default: - assert(!"DWARFRangeList::Extract() unsupported address size."); - break; + llvm_unreachable("DWARFRangeList::Extract() unsupported address size."); } // Filter out empty ranges if (begin < end) range_list.Append(DWARFRangeList::Entry(begin, end - begin)); } // Make sure we consumed at least something return range_offset != *offset_ptr; } void DWARFDebugRanges::Dump(Stream &s, const DWARFDataExtractor &debug_ranges_data, lldb::offset_t *offset_ptr, dw_addr_t cu_base_addr) { uint32_t addr_size = s.GetAddressByteSize(); bool verbose = s.GetVerbose(); dw_addr_t base_addr = cu_base_addr; while ( debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) { dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); // Extend 4 byte addresses that consists of 32 bits of 1's to be 64 bits // of ones if (begin == 0xFFFFFFFFull && addr_size == 4) begin = LLDB_INVALID_ADDRESS; s.Indent(); if (verbose) { s.AddressRange(begin, end, sizeof(dw_addr_t), " offsets = "); } if (begin == 0 && end == 0) { s.PutCString(" End"); break; } else if (begin == LLDB_INVALID_ADDRESS) { // A base address selection entry base_addr = end; s.Address(base_addr, sizeof(dw_addr_t), " Base address = "); } else { // Convert from offset to an address dw_addr_t begin_addr = begin + base_addr; dw_addr_t end_addr = end + base_addr; s.AddressRange(begin_addr, end_addr, sizeof(dw_addr_t), verbose ? " ==> addrs = " : NULL); } } } bool DWARFDebugRanges::FindRanges(dw_addr_t debug_ranges_base, dw_offset_t debug_ranges_offset, DWARFRangeList &range_list) const { dw_addr_t debug_ranges_address = debug_ranges_base + debug_ranges_offset; range_map_const_iterator pos = m_range_map.find(debug_ranges_address); if (pos != m_range_map.end()) { range_list = pos->second; return true; } return false; } Index: vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp (revision 311542) @@ -1,745 +1,744 @@ //===-- DWARFFormValue.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 "lldb/Core/Stream.h" #include "lldb/Core/dwarf.h" #include "DWARFCompileUnit.h" #include "DWARFFormValue.h" class DWARFCompileUnit; using namespace lldb_private; static uint8_t g_form_sizes_addr4[] = { 0, // 0x00 unused 4, // 0x01 DW_FORM_addr 0, // 0x02 unused 0, // 0x03 DW_FORM_block2 0, // 0x04 DW_FORM_block4 2, // 0x05 DW_FORM_data2 4, // 0x06 DW_FORM_data4 8, // 0x07 DW_FORM_data8 0, // 0x08 DW_FORM_string 0, // 0x09 DW_FORM_block 0, // 0x0a DW_FORM_block1 1, // 0x0b DW_FORM_data1 1, // 0x0c DW_FORM_flag 0, // 0x0d DW_FORM_sdata 4, // 0x0e DW_FORM_strp 0, // 0x0f DW_FORM_udata 0, // 0x10 DW_FORM_ref_addr (addr size for DWARF2 and earlier, 4 bytes for // DWARF32, 8 bytes for DWARF32 in DWARF 3 and later 1, // 0x11 DW_FORM_ref1 2, // 0x12 DW_FORM_ref2 4, // 0x13 DW_FORM_ref4 8, // 0x14 DW_FORM_ref8 0, // 0x15 DW_FORM_ref_udata 0, // 0x16 DW_FORM_indirect 4, // 0x17 DW_FORM_sec_offset 0, // 0x18 DW_FORM_exprloc 0, // 0x19 DW_FORM_flag_present 0, // 0x1a 0, // 0x1b 0, // 0x1c 0, // 0x1d 0, // 0x1e 0, // 0x1f 8, // 0x20 DW_FORM_ref_sig8 }; static uint8_t g_form_sizes_addr8[] = { 0, // 0x00 unused 8, // 0x01 DW_FORM_addr 0, // 0x02 unused 0, // 0x03 DW_FORM_block2 0, // 0x04 DW_FORM_block4 2, // 0x05 DW_FORM_data2 4, // 0x06 DW_FORM_data4 8, // 0x07 DW_FORM_data8 0, // 0x08 DW_FORM_string 0, // 0x09 DW_FORM_block 0, // 0x0a DW_FORM_block1 1, // 0x0b DW_FORM_data1 1, // 0x0c DW_FORM_flag 0, // 0x0d DW_FORM_sdata 4, // 0x0e DW_FORM_strp 0, // 0x0f DW_FORM_udata 0, // 0x10 DW_FORM_ref_addr (addr size for DWARF2 and earlier, 4 bytes for // DWARF32, 8 bytes for DWARF32 in DWARF 3 and later 1, // 0x11 DW_FORM_ref1 2, // 0x12 DW_FORM_ref2 4, // 0x13 DW_FORM_ref4 8, // 0x14 DW_FORM_ref8 0, // 0x15 DW_FORM_ref_udata 0, // 0x16 DW_FORM_indirect 4, // 0x17 DW_FORM_sec_offset 0, // 0x18 DW_FORM_exprloc 0, // 0x19 DW_FORM_flag_present 0, // 0x1a 0, // 0x1b 0, // 0x1c 0, // 0x1d 0, // 0x1e 0, // 0x1f 8, // 0x20 DW_FORM_ref_sig8 }; // Difference with g_form_sizes_addr8: // DW_FORM_strp and DW_FORM_sec_offset are 8 instead of 4 static uint8_t g_form_sizes_addr8_dwarf64[] = { 0, // 0x00 unused 8, // 0x01 DW_FORM_addr 0, // 0x02 unused 0, // 0x03 DW_FORM_block2 0, // 0x04 DW_FORM_block4 2, // 0x05 DW_FORM_data2 4, // 0x06 DW_FORM_data4 8, // 0x07 DW_FORM_data8 0, // 0x08 DW_FORM_string 0, // 0x09 DW_FORM_block 0, // 0x0a DW_FORM_block1 1, // 0x0b DW_FORM_data1 1, // 0x0c DW_FORM_flag 0, // 0x0d DW_FORM_sdata 8, // 0x0e DW_FORM_strp 0, // 0x0f DW_FORM_udata 0, // 0x10 DW_FORM_ref_addr (addr size for DWARF2 and earlier, 4 bytes for // DWARF32, 8 bytes for DWARF32 in DWARF 3 and later 1, // 0x11 DW_FORM_ref1 2, // 0x12 DW_FORM_ref2 4, // 0x13 DW_FORM_ref4 8, // 0x14 DW_FORM_ref8 0, // 0x15 DW_FORM_ref_udata 0, // 0x16 DW_FORM_indirect 8, // 0x17 DW_FORM_sec_offset 0, // 0x18 DW_FORM_exprloc 0, // 0x19 DW_FORM_flag_present 0, // 0x1a 0, // 0x1b 0, // 0x1c 0, // 0x1d 0, // 0x1e 0, // 0x1f 8, // 0x20 DW_FORM_ref_sig8 }; DWARFFormValue::FixedFormSizes DWARFFormValue::GetFixedFormSizesForAddressSize(uint8_t addr_size, bool is_dwarf64) { if (!is_dwarf64) { switch (addr_size) { case 4: return FixedFormSizes(g_form_sizes_addr4, sizeof(g_form_sizes_addr4)); case 8: return FixedFormSizes(g_form_sizes_addr8, sizeof(g_form_sizes_addr8)); } } else { if (addr_size == 8) return FixedFormSizes(g_form_sizes_addr8_dwarf64, sizeof(g_form_sizes_addr8_dwarf64)); // is_dwarf64 && addr_size == 4 : no provider does this. } return FixedFormSizes(); } DWARFFormValue::DWARFFormValue() : m_cu(NULL), m_form(0), m_value() {} DWARFFormValue::DWARFFormValue(const DWARFCompileUnit *cu, dw_form_t form) : m_cu(cu), m_form(form), m_value() {} void DWARFFormValue::Clear() { m_cu = nullptr; m_form = 0; memset(&m_value, 0, sizeof(m_value)); } bool DWARFFormValue::ExtractValue(const DWARFDataExtractor &data, lldb::offset_t *offset_ptr) { bool indirect = false; bool is_block = false; m_value.data = NULL; uint8_t ref_addr_size; // Read the value for the form into value and follow and DW_FORM_indirect // instances we run into do { indirect = false; switch (m_form) { case DW_FORM_addr: assert(m_cu); m_value.value.uval = data.GetMaxU64( offset_ptr, DWARFCompileUnit::GetAddressByteSize(m_cu)); break; case DW_FORM_block2: m_value.value.uval = data.GetU16(offset_ptr); is_block = true; break; case DW_FORM_block4: m_value.value.uval = data.GetU32(offset_ptr); is_block = true; break; case DW_FORM_data2: m_value.value.uval = data.GetU16(offset_ptr); break; case DW_FORM_data4: m_value.value.uval = data.GetU32(offset_ptr); break; case DW_FORM_data8: m_value.value.uval = data.GetU64(offset_ptr); break; case DW_FORM_string: m_value.value.cstr = data.GetCStr(offset_ptr); break; case DW_FORM_exprloc: case DW_FORM_block: m_value.value.uval = data.GetULEB128(offset_ptr); is_block = true; break; case DW_FORM_block1: m_value.value.uval = data.GetU8(offset_ptr); is_block = true; break; case DW_FORM_data1: m_value.value.uval = data.GetU8(offset_ptr); break; case DW_FORM_flag: m_value.value.uval = data.GetU8(offset_ptr); break; case DW_FORM_sdata: m_value.value.sval = data.GetSLEB128(offset_ptr); break; case DW_FORM_strp: assert(m_cu); m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFCompileUnit::IsDWARF64(m_cu) ? 8 : 4); break; // case DW_FORM_APPLE_db_str: case DW_FORM_udata: m_value.value.uval = data.GetULEB128(offset_ptr); break; case DW_FORM_ref_addr: assert(m_cu); ref_addr_size = 4; if (m_cu->GetVersion() <= 2) ref_addr_size = m_cu->GetAddressByteSize(); else ref_addr_size = m_cu->IsDWARF64() ? 8 : 4; m_value.value.uval = data.GetMaxU64(offset_ptr, ref_addr_size); break; case DW_FORM_ref1: m_value.value.uval = data.GetU8(offset_ptr); break; case DW_FORM_ref2: m_value.value.uval = data.GetU16(offset_ptr); break; case DW_FORM_ref4: m_value.value.uval = data.GetU32(offset_ptr); break; case DW_FORM_ref8: m_value.value.uval = data.GetU64(offset_ptr); break; case DW_FORM_ref_udata: m_value.value.uval = data.GetULEB128(offset_ptr); break; case DW_FORM_indirect: m_form = data.GetULEB128(offset_ptr); indirect = true; break; case DW_FORM_sec_offset: assert(m_cu); m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFCompileUnit::IsDWARF64(m_cu) ? 8 : 4); break; case DW_FORM_flag_present: m_value.value.uval = 1; break; case DW_FORM_ref_sig8: m_value.value.uval = data.GetU64(offset_ptr); break; case DW_FORM_GNU_str_index: m_value.value.uval = data.GetULEB128(offset_ptr); break; case DW_FORM_GNU_addr_index: m_value.value.uval = data.GetULEB128(offset_ptr); break; default: return false; break; } } while (indirect); if (is_block) { m_value.data = data.PeekData(*offset_ptr, m_value.value.uval); if (m_value.data != NULL) { *offset_ptr += m_value.value.uval; } } return true; } bool DWARFFormValue::SkipValue(const DWARFDataExtractor &debug_info_data, lldb::offset_t *offset_ptr) const { return DWARFFormValue::SkipValue(m_form, debug_info_data, offset_ptr, m_cu); } bool DWARFFormValue::SkipValue(dw_form_t form, const DWARFDataExtractor &debug_info_data, lldb::offset_t *offset_ptr, const DWARFCompileUnit *cu) { uint8_t ref_addr_size; switch (form) { // Blocks if inlined data that have a length field and the data bytes // inlined in the .debug_info case DW_FORM_exprloc: case DW_FORM_block: { dw_uleb128_t size = debug_info_data.GetULEB128(offset_ptr); *offset_ptr += size; } return true; case DW_FORM_block1: { dw_uleb128_t size = debug_info_data.GetU8(offset_ptr); *offset_ptr += size; } return true; case DW_FORM_block2: { dw_uleb128_t size = debug_info_data.GetU16(offset_ptr); *offset_ptr += size; } return true; case DW_FORM_block4: { dw_uleb128_t size = debug_info_data.GetU32(offset_ptr); *offset_ptr += size; } return true; // Inlined NULL terminated C-strings case DW_FORM_string: debug_info_data.GetCStr(offset_ptr); return true; // Compile unit address sized values case DW_FORM_addr: *offset_ptr += DWARFCompileUnit::GetAddressByteSize(cu); return true; case DW_FORM_ref_addr: ref_addr_size = 4; assert(cu); // CU must be valid for DW_FORM_ref_addr objects or we will get // this wrong if (cu->GetVersion() <= 2) ref_addr_size = cu->GetAddressByteSize(); else ref_addr_size = cu->IsDWARF64() ? 8 : 4; *offset_ptr += ref_addr_size; return true; // 0 bytes values (implied from DW_FORM) case DW_FORM_flag_present: return true; // 1 byte values case DW_FORM_data1: case DW_FORM_flag: case DW_FORM_ref1: *offset_ptr += 1; return true; // 2 byte values case DW_FORM_data2: case DW_FORM_ref2: *offset_ptr += 2; return true; // 32 bit for DWARF 32, 64 for DWARF 64 case DW_FORM_sec_offset: case DW_FORM_strp: assert(cu); *offset_ptr += (cu->IsDWARF64() ? 8 : 4); return true; // 4 byte values case DW_FORM_data4: case DW_FORM_ref4: *offset_ptr += 4; return true; // 8 byte values case DW_FORM_data8: case DW_FORM_ref8: case DW_FORM_ref_sig8: *offset_ptr += 8; return true; // signed or unsigned LEB 128 values case DW_FORM_sdata: case DW_FORM_udata: case DW_FORM_ref_udata: case DW_FORM_GNU_addr_index: case DW_FORM_GNU_str_index: debug_info_data.Skip_LEB128(offset_ptr); return true; case DW_FORM_indirect: { dw_form_t indirect_form = debug_info_data.GetULEB128(offset_ptr); return DWARFFormValue::SkipValue(indirect_form, debug_info_data, offset_ptr, cu); } default: break; } return false; } void DWARFFormValue::Dump(Stream &s) const { uint64_t uvalue = Unsigned(); bool cu_relative_offset = false; bool verbose = s.GetVerbose(); switch (m_form) { case DW_FORM_addr: s.Address(uvalue, sizeof(uint64_t)); break; case DW_FORM_flag: case DW_FORM_data1: s.PutHex8(uvalue); break; case DW_FORM_data2: s.PutHex16(uvalue); break; case DW_FORM_sec_offset: case DW_FORM_data4: s.PutHex32(uvalue); break; case DW_FORM_ref_sig8: case DW_FORM_data8: s.PutHex64(uvalue); break; case DW_FORM_string: s.QuotedCString(AsCString()); break; case DW_FORM_exprloc: case DW_FORM_block: case DW_FORM_block1: case DW_FORM_block2: case DW_FORM_block4: if (uvalue > 0) { switch (m_form) { case DW_FORM_exprloc: case DW_FORM_block: s.Printf("<0x%" PRIx64 "> ", uvalue); break; case DW_FORM_block1: s.Printf("<0x%2.2x> ", (uint8_t)uvalue); break; case DW_FORM_block2: s.Printf("<0x%4.4x> ", (uint16_t)uvalue); break; case DW_FORM_block4: s.Printf("<0x%8.8x> ", (uint32_t)uvalue); break; default: break; } const uint8_t *data_ptr = m_value.data; if (data_ptr) { const uint8_t *end_data_ptr = data_ptr + uvalue; // uvalue contains size of block while (data_ptr < end_data_ptr) { s.Printf("%2.2x ", *data_ptr); ++data_ptr; } } else s.PutCString("NULL"); } break; case DW_FORM_sdata: s.PutSLEB128(uvalue); break; case DW_FORM_udata: s.PutULEB128(uvalue); break; case DW_FORM_strp: { const char *dbg_str = AsCString(); if (dbg_str) { if (verbose) s.Printf(" .debug_str[0x%8.8x] = ", (uint32_t)uvalue); s.QuotedCString(dbg_str); } else { s.PutHex32(uvalue); } } break; case DW_FORM_ref_addr: { assert(m_cu); // CU must be valid for DW_FORM_ref_addr objects or we will // get this wrong if (m_cu->GetVersion() <= 2) s.Address(uvalue, sizeof(uint64_t) * 2); else s.Address(uvalue, 4 * 2); // 4 for DWARF32, 8 for DWARF64, but we don't // support DWARF64 yet break; } case DW_FORM_ref1: cu_relative_offset = true; if (verbose) s.Printf("cu + 0x%2.2x", (uint8_t)uvalue); break; case DW_FORM_ref2: cu_relative_offset = true; if (verbose) s.Printf("cu + 0x%4.4x", (uint16_t)uvalue); break; case DW_FORM_ref4: cu_relative_offset = true; if (verbose) s.Printf("cu + 0x%4.4x", (uint32_t)uvalue); break; case DW_FORM_ref8: cu_relative_offset = true; if (verbose) s.Printf("cu + 0x%8.8" PRIx64, uvalue); break; case DW_FORM_ref_udata: cu_relative_offset = true; if (verbose) s.Printf("cu + 0x%" PRIx64, uvalue); break; // All DW_FORM_indirect attributes should be resolved prior to calling this // function case DW_FORM_indirect: s.PutCString("DW_FORM_indirect"); break; case DW_FORM_flag_present: break; default: s.Printf("DW_FORM(0x%4.4x)", m_form); break; } if (cu_relative_offset) { assert(m_cu); // CU must be valid for DW_FORM_ref forms that are compile // unit relative or we will get this wrong if (verbose) s.PutCString(" => "); s.Printf("{0x%8.8" PRIx64 "}", uvalue + m_cu->GetOffset()); } } const char *DWARFFormValue::AsCString() const { SymbolFileDWARF *symbol_file = m_cu->GetSymbolFileDWARF(); if (m_form == DW_FORM_string) { return m_value.value.cstr; } else if (m_form == DW_FORM_strp) { if (!symbol_file) return nullptr; return symbol_file->get_debug_str_data().PeekCStr(m_value.value.uval); } else if (m_form == DW_FORM_GNU_str_index) { if (!symbol_file) return nullptr; uint32_t index_size = m_cu->IsDWARF64() ? 8 : 4; lldb::offset_t offset = m_value.value.uval * index_size; dw_offset_t str_offset = symbol_file->get_debug_str_offsets_data().GetMaxU64(&offset, index_size); return symbol_file->get_debug_str_data().PeekCStr(str_offset); } return nullptr; } dw_addr_t DWARFFormValue::Address() const { SymbolFileDWARF *symbol_file = m_cu->GetSymbolFileDWARF(); if (m_form == DW_FORM_addr) return Unsigned(); assert(m_cu); assert(m_form == DW_FORM_GNU_addr_index); if (!symbol_file) return 0; uint32_t index_size = m_cu->GetAddressByteSize(); dw_offset_t addr_base = m_cu->GetAddrBase(); lldb::offset_t offset = addr_base + m_value.value.uval * index_size; return symbol_file->get_debug_addr_data().GetMaxU64(&offset, index_size); } uint64_t DWARFFormValue::Reference() const { uint64_t die_offset = m_value.value.uval; switch (m_form) { case DW_FORM_ref1: case DW_FORM_ref2: case DW_FORM_ref4: case DW_FORM_ref8: case DW_FORM_ref_udata: assert(m_cu); // CU must be valid for DW_FORM_ref forms that are compile // unit relative or we will get this wrong die_offset += m_cu->GetOffset(); break; default: break; } return die_offset; } uint64_t DWARFFormValue::Reference(dw_offset_t base_offset) const { uint64_t die_offset = m_value.value.uval; switch (m_form) { case DW_FORM_ref1: case DW_FORM_ref2: case DW_FORM_ref4: case DW_FORM_ref8: case DW_FORM_ref_udata: die_offset += base_offset; break; default: break; } return die_offset; } const uint8_t *DWARFFormValue::BlockData() const { return m_value.data; } bool DWARFFormValue::IsBlockForm(const dw_form_t form) { switch (form) { case DW_FORM_exprloc: case DW_FORM_block: case DW_FORM_block1: case DW_FORM_block2: case DW_FORM_block4: return true; } return false; } bool DWARFFormValue::IsDataForm(const dw_form_t form) { switch (form) { case DW_FORM_sdata: case DW_FORM_udata: case DW_FORM_data1: case DW_FORM_data2: case DW_FORM_data4: case DW_FORM_data8: return true; } return false; } int DWARFFormValue::Compare(const DWARFFormValue &a_value, const DWARFFormValue &b_value) { dw_form_t a_form = a_value.Form(); dw_form_t b_form = b_value.Form(); if (a_form < b_form) return -1; if (a_form > b_form) return 1; switch (a_form) { case DW_FORM_addr: case DW_FORM_flag: case DW_FORM_data1: case DW_FORM_data2: case DW_FORM_data4: case DW_FORM_data8: case DW_FORM_udata: case DW_FORM_ref_addr: case DW_FORM_sec_offset: case DW_FORM_flag_present: case DW_FORM_ref_sig8: case DW_FORM_GNU_addr_index: { uint64_t a = a_value.Unsigned(); uint64_t b = b_value.Unsigned(); if (a < b) return -1; if (a > b) return 1; return 0; } case DW_FORM_sdata: { int64_t a = a_value.Signed(); int64_t b = b_value.Signed(); if (a < b) return -1; if (a > b) return 1; return 0; } case DW_FORM_string: case DW_FORM_strp: case DW_FORM_GNU_str_index: { const char *a_string = a_value.AsCString(); const char *b_string = b_value.AsCString(); if (a_string == b_string) return 0; else if (a_string && b_string) return strcmp(a_string, b_string); else if (a_string == NULL) return -1; // A string is NULL, and B is valid else return 1; // A string valid, and B is NULL } case DW_FORM_block: case DW_FORM_block1: case DW_FORM_block2: case DW_FORM_block4: case DW_FORM_exprloc: { uint64_t a_len = a_value.Unsigned(); uint64_t b_len = b_value.Unsigned(); if (a_len < b_len) return -1; if (a_len > b_len) return 1; // The block lengths are the same return memcmp(a_value.BlockData(), b_value.BlockData(), a_value.Unsigned()); } break; case DW_FORM_ref1: case DW_FORM_ref2: case DW_FORM_ref4: case DW_FORM_ref8: case DW_FORM_ref_udata: { uint64_t a = a_value.Reference(); uint64_t b = b_value.Reference(); if (a < b) return -1; if (a > b) return 1; return 0; } case DW_FORM_indirect: - assert(!"This shouldn't happen after the form has been extracted..."); - break; + llvm_unreachable( + "This shouldn't happen after the form has been extracted..."); default: - assert(!"Unhandled DW_FORM"); - break; + llvm_unreachable("Unhandled DW_FORM"); } return -1; } Index: vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp (revision 311542) @@ -1,644 +1,643 @@ //===-- HashedNameToDIE.cpp -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "HashedNameToDIE.h" #include "llvm/ADT/StringRef.h" void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array, DIEArray &die_offsets) { const size_t count = die_info_array.size(); for (size_t i = 0; i < count; ++i) die_offsets.emplace_back(die_info_array[i].cu_offset, die_info_array[i].offset); } void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array, const dw_tag_t tag, DIEArray &die_offsets) { if (tag == 0) { ExtractDIEArray(die_info_array, die_offsets); } else { const size_t count = die_info_array.size(); for (size_t i = 0; i < count; ++i) { const dw_tag_t die_tag = die_info_array[i].tag; bool tag_matches = die_tag == 0 || tag == die_tag; if (!tag_matches) { if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type) tag_matches = tag == DW_TAG_structure_type || tag == DW_TAG_class_type; } if (tag_matches) die_offsets.emplace_back(die_info_array[i].cu_offset, die_info_array[i].offset); } } } void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array, const dw_tag_t tag, const uint32_t qualified_name_hash, DIEArray &die_offsets) { if (tag == 0) { ExtractDIEArray(die_info_array, die_offsets); } else { const size_t count = die_info_array.size(); for (size_t i = 0; i < count; ++i) { if (qualified_name_hash != die_info_array[i].qualified_name_hash) continue; const dw_tag_t die_tag = die_info_array[i].tag; bool tag_matches = die_tag == 0 || tag == die_tag; if (!tag_matches) { if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type) tag_matches = tag == DW_TAG_structure_type || tag == DW_TAG_class_type; } if (tag_matches) die_offsets.emplace_back(die_info_array[i].cu_offset, die_info_array[i].offset); } } } void DWARFMappedHash::ExtractClassOrStructDIEArray( const DIEInfoArray &die_info_array, bool return_implementation_only_if_available, DIEArray &die_offsets) { const size_t count = die_info_array.size(); for (size_t i = 0; i < count; ++i) { const dw_tag_t die_tag = die_info_array[i].tag; if (die_tag == 0 || die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type) { if (die_info_array[i].type_flags & eTypeFlagClassIsImplementation) { if (return_implementation_only_if_available) { // We found the one true definition for this class, so // only return that die_offsets.clear(); die_offsets.emplace_back(die_info_array[i].cu_offset, die_info_array[i].offset); return; } else { // Put the one true definition as the first entry so it // matches first die_offsets.emplace(die_offsets.begin(), die_info_array[i].cu_offset, die_info_array[i].offset); } } else { die_offsets.emplace_back(die_info_array[i].cu_offset, die_info_array[i].offset); } } } } void DWARFMappedHash::ExtractTypesFromDIEArray( const DIEInfoArray &die_info_array, uint32_t type_flag_mask, uint32_t type_flag_value, DIEArray &die_offsets) { const size_t count = die_info_array.size(); for (size_t i = 0; i < count; ++i) { if ((die_info_array[i].type_flags & type_flag_mask) == type_flag_value) die_offsets.emplace_back(die_info_array[i].cu_offset, die_info_array[i].offset); } } const char *DWARFMappedHash::GetAtomTypeName(uint16_t atom) { switch (atom) { case eAtomTypeNULL: return "NULL"; case eAtomTypeDIEOffset: return "die-offset"; case eAtomTypeCUOffset: return "cu-offset"; case eAtomTypeTag: return "die-tag"; case eAtomTypeNameFlags: return "name-flags"; case eAtomTypeTypeFlags: return "type-flags"; case eAtomTypeQualNameHash: return "qualified-name-hash"; } return ""; } DWARFMappedHash::DIEInfo::DIEInfo() : cu_offset(DW_INVALID_OFFSET), offset(DW_INVALID_OFFSET), tag(0), type_flags(0), qualified_name_hash(0) {} DWARFMappedHash::DIEInfo::DIEInfo(dw_offset_t c, dw_offset_t o, dw_tag_t t, uint32_t f, uint32_t h) : cu_offset(c), offset(o), tag(t), type_flags(f), qualified_name_hash(h) {} DWARFMappedHash::Prologue::Prologue(dw_offset_t _die_base_offset) : die_base_offset(_die_base_offset), atoms(), atom_mask(0), min_hash_data_byte_size(0), hash_data_has_fixed_byte_size(true) { // Define an array of DIE offsets by first defining an array, // and then define the atom type for the array, in this case // we have an array of DIE offsets AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4); } void DWARFMappedHash::Prologue::ClearAtoms() { hash_data_has_fixed_byte_size = true; min_hash_data_byte_size = 0; atom_mask = 0; atoms.clear(); } bool DWARFMappedHash::Prologue::ContainsAtom(AtomType atom_type) const { return (atom_mask & (1u << atom_type)) != 0; } void DWARFMappedHash::Prologue::Clear() { die_base_offset = 0; ClearAtoms(); } void DWARFMappedHash::Prologue::AppendAtom(AtomType type, dw_form_t form) { atoms.push_back({type, form}); atom_mask |= 1u << type; switch (form) { case DW_FORM_indirect: case DW_FORM_exprloc: case DW_FORM_flag_present: case DW_FORM_ref_sig8: - assert(!"Unhandled atom form"); - break; + llvm_unreachable("Unhandled atom form"); case DW_FORM_string: case DW_FORM_block: case DW_FORM_block1: case DW_FORM_sdata: case DW_FORM_udata: case DW_FORM_ref_udata: case DW_FORM_GNU_addr_index: case DW_FORM_GNU_str_index: hash_data_has_fixed_byte_size = false; LLVM_FALLTHROUGH; case DW_FORM_flag: case DW_FORM_data1: case DW_FORM_ref1: case DW_FORM_sec_offset: min_hash_data_byte_size += 1; break; case DW_FORM_block2: hash_data_has_fixed_byte_size = false; LLVM_FALLTHROUGH; case DW_FORM_data2: case DW_FORM_ref2: min_hash_data_byte_size += 2; break; case DW_FORM_block4: hash_data_has_fixed_byte_size = false; LLVM_FALLTHROUGH; case DW_FORM_data4: case DW_FORM_ref4: case DW_FORM_addr: case DW_FORM_ref_addr: case DW_FORM_strp: min_hash_data_byte_size += 4; break; case DW_FORM_data8: case DW_FORM_ref8: min_hash_data_byte_size += 8; break; } } lldb::offset_t DWARFMappedHash::Prologue::Read(const lldb_private::DataExtractor &data, lldb::offset_t offset) { ClearAtoms(); die_base_offset = data.GetU32(&offset); const uint32_t atom_count = data.GetU32(&offset); if (atom_count == 0x00060003u) { // Old format, deal with contents of old pre-release format while (data.GetU32(&offset)) /* do nothing */; // Hardcode to the only known value for now. AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4); } else { for (uint32_t i = 0; i < atom_count; ++i) { AtomType type = (AtomType)data.GetU16(&offset); dw_form_t form = (dw_form_t)data.GetU16(&offset); AppendAtom(type, form); } } return offset; } size_t DWARFMappedHash::Prologue::GetByteSize() const { // Add an extra count to the atoms size for the zero termination Atom that // gets // written to disk return sizeof(die_base_offset) + sizeof(uint32_t) + atoms.size() * sizeof(Atom); } size_t DWARFMappedHash::Prologue::GetMinimumHashDataByteSize() const { return min_hash_data_byte_size; } bool DWARFMappedHash::Prologue::HashDataHasFixedByteSize() const { return hash_data_has_fixed_byte_size; } size_t DWARFMappedHash::Header::GetByteSize(const HeaderData &header_data) { return header_data.GetByteSize(); } lldb::offset_t DWARFMappedHash::Header::Read(lldb_private::DataExtractor &data, lldb::offset_t offset) { offset = MappedHash::Header::Read(data, offset); if (offset != UINT32_MAX) { offset = header_data.Read(data, offset); } return offset; } bool DWARFMappedHash::Header::Read(const lldb_private::DWARFDataExtractor &data, lldb::offset_t *offset_ptr, DIEInfo &hash_data) const { const size_t num_atoms = header_data.atoms.size(); if (num_atoms == 0) return false; for (size_t i = 0; i < num_atoms; ++i) { DWARFFormValue form_value(NULL, header_data.atoms[i].form); if (!form_value.ExtractValue(data, offset_ptr)) return false; switch (header_data.atoms[i].type) { case eAtomTypeDIEOffset: // DIE offset, check form for encoding hash_data.offset = (dw_offset_t)form_value.Reference(header_data.die_base_offset); break; case eAtomTypeTag: // DW_TAG value for the DIE hash_data.tag = (dw_tag_t)form_value.Unsigned(); break; case eAtomTypeTypeFlags: // Flags from enum TypeFlags hash_data.type_flags = (uint32_t)form_value.Unsigned(); break; case eAtomTypeQualNameHash: // Flags from enum TypeFlags hash_data.qualified_name_hash = form_value.Unsigned(); break; default: // We can always skip atoms we don't know about break; } } return true; } void DWARFMappedHash::Header::Dump(lldb_private::Stream &strm, const DIEInfo &hash_data) const { const size_t num_atoms = header_data.atoms.size(); for (size_t i = 0; i < num_atoms; ++i) { if (i > 0) strm.PutCString(", "); DWARFFormValue form_value(NULL, header_data.atoms[i].form); switch (header_data.atoms[i].type) { case eAtomTypeDIEOffset: // DIE offset, check form for encoding strm.Printf("{0x%8.8x}", hash_data.offset); break; case eAtomTypeTag: // DW_TAG value for the DIE { const char *tag_cstr = lldb_private::DW_TAG_value_to_name(hash_data.tag); if (tag_cstr) strm.PutCString(tag_cstr); else strm.Printf("DW_TAG_(0x%4.4x)", hash_data.tag); } break; case eAtomTypeTypeFlags: // Flags from enum TypeFlags strm.Printf("0x%2.2x", hash_data.type_flags); if (hash_data.type_flags) { strm.PutCString(" ("); if (hash_data.type_flags & eTypeFlagClassIsImplementation) strm.PutCString(" implementation"); strm.PutCString(" )"); } break; case eAtomTypeQualNameHash: // Flags from enum TypeFlags strm.Printf("0x%8.8x", hash_data.qualified_name_hash); break; default: strm.Printf("AtomType(0x%x)", header_data.atoms[i].type); break; } } } DWARFMappedHash::MemoryTable::MemoryTable( lldb_private::DWARFDataExtractor &table_data, const lldb_private::DWARFDataExtractor &string_table, const char *name) : MappedHash::MemoryTable(table_data), m_data(table_data), m_string_table(string_table), m_name(name) {} const char * DWARFMappedHash::MemoryTable::GetStringForKeyType(KeyType key) const { // The key in the DWARF table is the .debug_str offset for the string return m_string_table.PeekCStr(key); } bool DWARFMappedHash::MemoryTable::ReadHashData(uint32_t hash_data_offset, HashData &hash_data) const { lldb::offset_t offset = hash_data_offset; offset += 4; // Skip string table offset that contains offset of hash name in // .debug_str const uint32_t count = m_data.GetU32(&offset); if (count > 0) { hash_data.resize(count); for (uint32_t i = 0; i < count; ++i) { if (!m_header.Read(m_data, &offset, hash_data[i])) return false; } } else hash_data.clear(); return true; } DWARFMappedHash::MemoryTable::Result DWARFMappedHash::MemoryTable::GetHashDataForName( const char *name, lldb::offset_t *hash_data_offset_ptr, Pair &pair) const { pair.key = m_data.GetU32(hash_data_offset_ptr); pair.value.clear(); // If the key is zero, this terminates our chain of HashData objects // for this hash value. if (pair.key == 0) return eResultEndOfHashData; // There definitely should be a string for this string offset, if // there isn't, there is something wrong, return and error const char *strp_cstr = m_string_table.PeekCStr(pair.key); if (strp_cstr == NULL) { *hash_data_offset_ptr = UINT32_MAX; return eResultError; } const uint32_t count = m_data.GetU32(hash_data_offset_ptr); const size_t min_total_hash_data_size = count * m_header.header_data.GetMinimumHashDataByteSize(); if (count > 0 && m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr, min_total_hash_data_size)) { // We have at least one HashData entry, and we have enough // data to parse at least "count" HashData entries. // First make sure the entire C string matches... const bool match = strcmp(name, strp_cstr) == 0; if (!match && m_header.header_data.HashDataHasFixedByteSize()) { // If the string doesn't match and we have fixed size data, // we can just add the total byte size of all HashData objects // to the hash data offset and be done... *hash_data_offset_ptr += min_total_hash_data_size; } else { // If the string does match, or we don't have fixed size data // then we need to read the hash data as a stream. If the // string matches we also append all HashData objects to the // value array. for (uint32_t i = 0; i < count; ++i) { DIEInfo die_info; if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) { // Only happened if the HashData of the string matched... if (match) pair.value.push_back(die_info); } else { // Something went wrong while reading the data *hash_data_offset_ptr = UINT32_MAX; return eResultError; } } } // Return the correct response depending on if the string matched // or not... if (match) return eResultKeyMatch; // The key (cstring) matches and we have lookup // results! else return eResultKeyMismatch; // The key doesn't match, this function will // get called // again for the next key/value or the key terminator // which in our case is a zero .debug_str offset. } else { *hash_data_offset_ptr = UINT32_MAX; return eResultError; } } DWARFMappedHash::MemoryTable::Result DWARFMappedHash::MemoryTable::AppendHashDataForRegularExpression( const lldb_private::RegularExpression ®ex, lldb::offset_t *hash_data_offset_ptr, Pair &pair) const { pair.key = m_data.GetU32(hash_data_offset_ptr); // If the key is zero, this terminates our chain of HashData objects // for this hash value. if (pair.key == 0) return eResultEndOfHashData; // There definitely should be a string for this string offset, if // there isn't, there is something wrong, return and error const char *strp_cstr = m_string_table.PeekCStr(pair.key); if (strp_cstr == NULL) return eResultError; const uint32_t count = m_data.GetU32(hash_data_offset_ptr); const size_t min_total_hash_data_size = count * m_header.header_data.GetMinimumHashDataByteSize(); if (count > 0 && m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr, min_total_hash_data_size)) { const bool match = regex.Execute(llvm::StringRef(strp_cstr)); if (!match && m_header.header_data.HashDataHasFixedByteSize()) { // If the regex doesn't match and we have fixed size data, // we can just add the total byte size of all HashData objects // to the hash data offset and be done... *hash_data_offset_ptr += min_total_hash_data_size; } else { // If the string does match, or we don't have fixed size data // then we need to read the hash data as a stream. If the // string matches we also append all HashData objects to the // value array. for (uint32_t i = 0; i < count; ++i) { DIEInfo die_info; if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) { // Only happened if the HashData of the string matched... if (match) pair.value.push_back(die_info); } else { // Something went wrong while reading the data *hash_data_offset_ptr = UINT32_MAX; return eResultError; } } } // Return the correct response depending on if the string matched // or not... if (match) return eResultKeyMatch; // The key (cstring) matches and we have lookup // results! else return eResultKeyMismatch; // The key doesn't match, this function will // get called // again for the next key/value or the key terminator // which in our case is a zero .debug_str offset. } else { *hash_data_offset_ptr = UINT32_MAX; return eResultError; } } size_t DWARFMappedHash::MemoryTable::AppendAllDIEsThatMatchingRegex( const lldb_private::RegularExpression ®ex, DIEInfoArray &die_info_array) const { const uint32_t hash_count = m_header.hashes_count; Pair pair; for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) { lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx); while (hash_data_offset != UINT32_MAX) { const lldb::offset_t prev_hash_data_offset = hash_data_offset; Result hash_result = AppendHashDataForRegularExpression(regex, &hash_data_offset, pair); if (prev_hash_data_offset == hash_data_offset) break; // Check the result of getting our hash data switch (hash_result) { case eResultKeyMatch: case eResultKeyMismatch: // Whether we matches or not, it doesn't matter, we // keep looking. break; case eResultEndOfHashData: case eResultError: hash_data_offset = UINT32_MAX; break; } } } die_info_array.swap(pair.value); return die_info_array.size(); } size_t DWARFMappedHash::MemoryTable::AppendAllDIEsInRange( const uint32_t die_offset_start, const uint32_t die_offset_end, DIEInfoArray &die_info_array) const { const uint32_t hash_count = m_header.hashes_count; for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) { bool done = false; lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx); while (!done && hash_data_offset != UINT32_MAX) { KeyType key = m_data.GetU32(&hash_data_offset); // If the key is zero, this terminates our chain of HashData objects // for this hash value. if (key == 0) break; const uint32_t count = m_data.GetU32(&hash_data_offset); for (uint32_t i = 0; i < count; ++i) { DIEInfo die_info; if (m_header.Read(m_data, &hash_data_offset, die_info)) { if (die_info.offset == 0) done = true; if (die_offset_start <= die_info.offset && die_info.offset < die_offset_end) die_info_array.push_back(die_info); } } } } return die_info_array.size(); } size_t DWARFMappedHash::MemoryTable::FindByName(const char *name, DIEArray &die_offsets) { if (!name || !name[0]) return 0; DIEInfoArray die_info_array; if (FindByName(name, die_info_array)) DWARFMappedHash::ExtractDIEArray(die_info_array, die_offsets); return die_info_array.size(); } size_t DWARFMappedHash::MemoryTable::FindByNameAndTag(const char *name, const dw_tag_t tag, DIEArray &die_offsets) { DIEInfoArray die_info_array; if (FindByName(name, die_info_array)) DWARFMappedHash::ExtractDIEArray(die_info_array, tag, die_offsets); return die_info_array.size(); } size_t DWARFMappedHash::MemoryTable::FindByNameAndTagAndQualifiedNameHash( const char *name, const dw_tag_t tag, const uint32_t qualified_name_hash, DIEArray &die_offsets) { DIEInfoArray die_info_array; if (FindByName(name, die_info_array)) DWARFMappedHash::ExtractDIEArray(die_info_array, tag, qualified_name_hash, die_offsets); return die_info_array.size(); } size_t DWARFMappedHash::MemoryTable::FindCompleteObjCClassByName( const char *name, DIEArray &die_offsets, bool must_be_implementation) { DIEInfoArray die_info_array; if (FindByName(name, die_info_array)) { if (must_be_implementation && GetHeader().header_data.ContainsAtom(eAtomTypeTypeFlags)) { // If we have two atoms, then we have the DIE offset and // the type flags so we can find the objective C class // efficiently. DWARFMappedHash::ExtractTypesFromDIEArray(die_info_array, UINT32_MAX, eTypeFlagClassIsImplementation, die_offsets); } else { // We don't only want the one true definition, so try and see // what we can find, and only return class or struct DIEs. // If we do have the full implementation, then return it alone, // else return all possible matches. const bool return_implementation_only_if_available = true; DWARFMappedHash::ExtractClassOrStructDIEArray( die_info_array, return_implementation_only_if_available, die_offsets); } } return die_offsets.size(); } size_t DWARFMappedHash::MemoryTable::FindByName(const char *name, DIEInfoArray &die_info_array) { if (!name || !name[0]) return 0; Pair kv_pair; size_t old_size = die_info_array.size(); if (Find(name, kv_pair)) { die_info_array.swap(kv_pair.value); return die_info_array.size() - old_size; } return 0; } Index: vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp (revision 311542) @@ -1,1441 +1,1440 @@ //===-- SymbolFileDWARFDebugMap.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 "SymbolFileDWARFDebugMap.h" #include "DWARFDebugAranges.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RangeMap.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/Section.h" #include "lldb/Host/FileSystem.h" //#define DEBUG_OSO_DMAP // DO NOT CHECKIN WITH THIS NOT COMMENTED OUT #if defined(DEBUG_OSO_DMAP) #include "lldb/Core/StreamFile.h" #endif #include "lldb/Core/Timer.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/TypeMap.h" #include "lldb/Symbol/VariableList.h" #include "llvm/Support/ScopedPrinter.h" #include "LogChannelDWARF.h" #include "SymbolFileDWARF.h" using namespace lldb; using namespace lldb_private; // Subclass lldb_private::Module so we can intercept the // "Module::GetObjectFile()" // (so we can fixup the object file sections) and also for // "Module::GetSymbolVendor()" // (so we can fixup the symbol file id. const SymbolFileDWARFDebugMap::FileRangeMap & SymbolFileDWARFDebugMap::CompileUnitInfo::GetFileRangeMap( SymbolFileDWARFDebugMap *exe_symfile) { if (file_range_map_valid) return file_range_map; file_range_map_valid = true; Module *oso_module = exe_symfile->GetModuleByCompUnitInfo(this); if (!oso_module) return file_range_map; ObjectFile *oso_objfile = oso_module->GetObjectFile(); if (!oso_objfile) return file_range_map; Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP)); if (log) { ConstString object_name(oso_module->GetObjectName()); log->Printf( "%p: SymbolFileDWARFDebugMap::CompileUnitInfo::GetFileRangeMap ('%s')", static_cast(this), oso_module->GetSpecificationDescription().c_str()); } std::vector cu_infos; if (exe_symfile->GetCompUnitInfosForModule(oso_module, cu_infos)) { for (auto comp_unit_info : cu_infos) { Symtab *exe_symtab = exe_symfile->GetObjectFile()->GetSymtab(); ModuleSP oso_module_sp(oso_objfile->GetModule()); Symtab *oso_symtab = oso_objfile->GetSymtab(); /// const uint32_t fun_resolve_flags = SymbolContext::Module | /// eSymbolContextCompUnit | eSymbolContextFunction; // SectionList *oso_sections = oso_objfile->Sections(); // Now we need to make sections that map from zero based object // file addresses to where things ended up in the main executable. assert(comp_unit_info->first_symbol_index != UINT32_MAX); // End index is one past the last valid symbol index const uint32_t oso_end_idx = comp_unit_info->last_symbol_index + 1; for (uint32_t idx = comp_unit_info->first_symbol_index + 2; // Skip the N_SO and N_OSO idx < oso_end_idx; ++idx) { Symbol *exe_symbol = exe_symtab->SymbolAtIndex(idx); if (exe_symbol) { if (exe_symbol->IsDebug() == false) continue; switch (exe_symbol->GetType()) { default: break; case eSymbolTypeCode: { // For each N_FUN, or function that we run into in the debug map // we make a new section that we add to the sections found in the // .o file. This new section has the file address set to what the // addresses are in the .o file, and the load address is adjusted // to match where it ended up in the final executable! We do this // before we parse any dwarf info so that when it goes get parsed // all section/offset addresses that get registered will resolve // correctly to the new addresses in the main executable. // First we find the original symbol in the .o file's symbol table Symbol *oso_fun_symbol = oso_symtab->FindFirstSymbolWithNameAndType( exe_symbol->GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled), eSymbolTypeCode, Symtab::eDebugNo, Symtab::eVisibilityAny); if (oso_fun_symbol) { // Add the inverse OSO file address to debug map entry mapping exe_symfile->AddOSOFileRange( this, exe_symbol->GetAddressRef().GetFileAddress(), exe_symbol->GetByteSize(), oso_fun_symbol->GetAddressRef().GetFileAddress(), oso_fun_symbol->GetByteSize()); } } break; case eSymbolTypeData: { // For each N_GSYM we remap the address for the global by making // a new section that we add to the sections found in the .o file. // This new section has the file address set to what the // addresses are in the .o file, and the load address is adjusted // to match where it ended up in the final executable! We do this // before we parse any dwarf info so that when it goes get parsed // all section/offset addresses that get registered will resolve // correctly to the new addresses in the main executable. We // initially set the section size to be 1 byte, but will need to // fix up these addresses further after all globals have been // parsed to span the gaps, or we can find the global variable // sizes from the DWARF info as we are parsing. // Next we find the non-stab entry that corresponds to the N_GSYM in // the .o file Symbol *oso_gsym_symbol = oso_symtab->FindFirstSymbolWithNameAndType( exe_symbol->GetMangled().GetName(lldb::eLanguageTypeUnknown, Mangled::ePreferMangled), eSymbolTypeData, Symtab::eDebugNo, Symtab::eVisibilityAny); if (exe_symbol && oso_gsym_symbol && exe_symbol->ValueIsAddress() && oso_gsym_symbol->ValueIsAddress()) { // Add the inverse OSO file address to debug map entry mapping exe_symfile->AddOSOFileRange( this, exe_symbol->GetAddressRef().GetFileAddress(), exe_symbol->GetByteSize(), oso_gsym_symbol->GetAddressRef().GetFileAddress(), oso_gsym_symbol->GetByteSize()); } } break; } } } exe_symfile->FinalizeOSOFileRanges(this); // We don't need the symbols anymore for the .o files oso_objfile->ClearSymtab(); } } return file_range_map; } class DebugMapModule : public Module { public: DebugMapModule(const ModuleSP &exe_module_sp, uint32_t cu_idx, const FileSpec &file_spec, const ArchSpec &arch, const ConstString *object_name, off_t object_offset, const llvm::sys::TimePoint<> object_mod_time) : Module(file_spec, arch, object_name, object_offset, object_mod_time), m_exe_module_wp(exe_module_sp), m_cu_idx(cu_idx) {} ~DebugMapModule() override = default; SymbolVendor * GetSymbolVendor(bool can_create = true, lldb_private::Stream *feedback_strm = NULL) override { // Scope for locker if (m_symfile_ap.get() || can_create == false) return m_symfile_ap.get(); ModuleSP exe_module_sp(m_exe_module_wp.lock()); if (exe_module_sp) { // Now get the object file outside of a locking scope ObjectFile *oso_objfile = GetObjectFile(); if (oso_objfile) { std::lock_guard guard(m_mutex); SymbolVendor *symbol_vendor = Module::GetSymbolVendor(can_create, feedback_strm); if (symbol_vendor) { // Set a pointer to this class to set our OSO DWARF file know // that the DWARF is being used along with a debug map and that // it will have the remapped sections that we do below. SymbolFileDWARF *oso_symfile = SymbolFileDWARFDebugMap::GetSymbolFileAsSymbolFileDWARF( symbol_vendor->GetSymbolFile()); if (!oso_symfile) return NULL; ObjectFile *exe_objfile = exe_module_sp->GetObjectFile(); SymbolVendor *exe_sym_vendor = exe_module_sp->GetSymbolVendor(); if (exe_objfile && exe_sym_vendor) { oso_symfile->SetDebugMapModule(exe_module_sp); // Set the ID of the symbol file DWARF to the index of the OSO // shifted left by 32 bits to provide a unique prefix for any // UserID's that get created in the symbol file. oso_symfile->SetID(((uint64_t)m_cu_idx + 1ull) << 32ull); } return symbol_vendor; } } } return NULL; } protected: ModuleWP m_exe_module_wp; const uint32_t m_cu_idx; }; void SymbolFileDWARFDebugMap::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); } void SymbolFileDWARFDebugMap::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } lldb_private::ConstString SymbolFileDWARFDebugMap::GetPluginNameStatic() { static ConstString g_name("dwarf-debugmap"); return g_name; } const char *SymbolFileDWARFDebugMap::GetPluginDescriptionStatic() { return "DWARF and DWARF3 debug symbol file reader (debug map)."; } SymbolFile *SymbolFileDWARFDebugMap::CreateInstance(ObjectFile *obj_file) { return new SymbolFileDWARFDebugMap(obj_file); } SymbolFileDWARFDebugMap::SymbolFileDWARFDebugMap(ObjectFile *ofile) : SymbolFile(ofile), m_flags(), m_compile_unit_infos(), m_func_indexes(), m_glob_indexes(), m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate) {} SymbolFileDWARFDebugMap::~SymbolFileDWARFDebugMap() {} void SymbolFileDWARFDebugMap::InitializeObject() {} void SymbolFileDWARFDebugMap::InitOSO() { if (m_flags.test(kHaveInitializedOSOs)) return; m_flags.set(kHaveInitializedOSOs); // If the object file has been stripped, there is no sense in looking further // as all of the debug symbols for the debug map will not be available if (m_obj_file->IsStripped()) return; // Also make sure the file type is some sort of executable. Core files, debug // info files (dSYM), object files (.o files), and stub libraries all can switch (m_obj_file->GetType()) { case ObjectFile::eTypeInvalid: case ObjectFile::eTypeCoreFile: case ObjectFile::eTypeDebugInfo: case ObjectFile::eTypeObjectFile: case ObjectFile::eTypeStubLibrary: case ObjectFile::eTypeUnknown: case ObjectFile::eTypeJIT: return; case ObjectFile::eTypeExecutable: case ObjectFile::eTypeDynamicLinker: case ObjectFile::eTypeSharedLibrary: break; } // In order to get the abilities of this plug-in, we look at the list of // N_OSO entries (object files) from the symbol table and make sure that // these files exist and also contain valid DWARF. If we get any of that // then we return the abilities of the first N_OSO's DWARF. Symtab *symtab = m_obj_file->GetSymtab(); if (symtab) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP)); std::vector oso_indexes; // When a mach-o symbol is encoded, the n_type field is encoded in bits // 23:16, and the n_desc field is encoded in bits 15:0. // // To find all N_OSO entries that are part of the DWARF + debug map // we find only object file symbols with the flags value as follows: // bits 23:16 == 0x66 (N_OSO) // bits 15: 0 == 0x0001 (specifies this is a debug map object file) const uint32_t k_oso_symbol_flags_value = 0x660001u; const uint32_t oso_index_count = symtab->AppendSymbolIndexesWithTypeAndFlagsValue( eSymbolTypeObjectFile, k_oso_symbol_flags_value, oso_indexes); if (oso_index_count > 0) { symtab->AppendSymbolIndexesWithType(eSymbolTypeCode, Symtab::eDebugYes, Symtab::eVisibilityAny, m_func_indexes); symtab->AppendSymbolIndexesWithType(eSymbolTypeData, Symtab::eDebugYes, Symtab::eVisibilityAny, m_glob_indexes); symtab->SortSymbolIndexesByValue(m_func_indexes, true); symtab->SortSymbolIndexesByValue(m_glob_indexes, true); for (uint32_t sym_idx : m_func_indexes) { const Symbol *symbol = symtab->SymbolAtIndex(sym_idx); lldb::addr_t file_addr = symbol->GetAddressRef().GetFileAddress(); lldb::addr_t byte_size = symbol->GetByteSize(); DebugMap::Entry debug_map_entry( file_addr, byte_size, OSOEntry(sym_idx, LLDB_INVALID_ADDRESS)); m_debug_map.Append(debug_map_entry); } for (uint32_t sym_idx : m_glob_indexes) { const Symbol *symbol = symtab->SymbolAtIndex(sym_idx); lldb::addr_t file_addr = symbol->GetAddressRef().GetFileAddress(); lldb::addr_t byte_size = symbol->GetByteSize(); DebugMap::Entry debug_map_entry( file_addr, byte_size, OSOEntry(sym_idx, LLDB_INVALID_ADDRESS)); m_debug_map.Append(debug_map_entry); } m_debug_map.Sort(); m_compile_unit_infos.resize(oso_index_count); for (uint32_t i = 0; i < oso_index_count; ++i) { const uint32_t so_idx = oso_indexes[i] - 1; const uint32_t oso_idx = oso_indexes[i]; const Symbol *so_symbol = symtab->SymbolAtIndex(so_idx); const Symbol *oso_symbol = symtab->SymbolAtIndex(oso_idx); if (so_symbol && oso_symbol && so_symbol->GetType() == eSymbolTypeSourceFile && oso_symbol->GetType() == eSymbolTypeObjectFile) { m_compile_unit_infos[i].so_file.SetFile( so_symbol->GetName().AsCString(), false); m_compile_unit_infos[i].oso_path = oso_symbol->GetName(); m_compile_unit_infos[i].oso_mod_time = llvm::sys::toTimePoint(oso_symbol->GetIntegerValue(0)); uint32_t sibling_idx = so_symbol->GetSiblingIndex(); // The sibling index can't be less that or equal to the current index // "i" if (sibling_idx == UINT32_MAX) { m_obj_file->GetModule()->ReportError( "N_SO in symbol with UID %u has invalid sibling in debug map, " "please file a bug and attach the binary listed in this error", so_symbol->GetID()); } else { const Symbol *last_symbol = symtab->SymbolAtIndex(sibling_idx - 1); m_compile_unit_infos[i].first_symbol_index = so_idx; m_compile_unit_infos[i].last_symbol_index = sibling_idx - 1; m_compile_unit_infos[i].first_symbol_id = so_symbol->GetID(); m_compile_unit_infos[i].last_symbol_id = last_symbol->GetID(); if (log) log->Printf("Initialized OSO 0x%8.8x: file=%s", i, oso_symbol->GetName().GetCString()); } } else { if (oso_symbol == NULL) m_obj_file->GetModule()->ReportError( "N_OSO symbol[%u] can't be found, please file a bug and attach " "the binary listed in this error", oso_idx); else if (so_symbol == NULL) m_obj_file->GetModule()->ReportError( "N_SO not found for N_OSO symbol[%u], please file a bug and " "attach the binary listed in this error", oso_idx); else if (so_symbol->GetType() != eSymbolTypeSourceFile) m_obj_file->GetModule()->ReportError( "N_SO has incorrect symbol type (%u) for N_OSO symbol[%u], " "please file a bug and attach the binary listed in this error", so_symbol->GetType(), oso_idx); else if (oso_symbol->GetType() != eSymbolTypeSourceFile) m_obj_file->GetModule()->ReportError( "N_OSO has incorrect symbol type (%u) for N_OSO symbol[%u], " "please file a bug and attach the binary listed in this error", oso_symbol->GetType(), oso_idx); } } } } } Module *SymbolFileDWARFDebugMap::GetModuleByOSOIndex(uint32_t oso_idx) { const uint32_t cu_count = GetNumCompileUnits(); if (oso_idx < cu_count) return GetModuleByCompUnitInfo(&m_compile_unit_infos[oso_idx]); return NULL; } Module *SymbolFileDWARFDebugMap::GetModuleByCompUnitInfo( CompileUnitInfo *comp_unit_info) { if (!comp_unit_info->oso_sp) { auto pos = m_oso_map.find(comp_unit_info->oso_path); if (pos != m_oso_map.end()) { comp_unit_info->oso_sp = pos->second; } else { ObjectFile *obj_file = GetObjectFile(); comp_unit_info->oso_sp.reset(new OSOInfo()); m_oso_map[comp_unit_info->oso_path] = comp_unit_info->oso_sp; const char *oso_path = comp_unit_info->oso_path.GetCString(); FileSpec oso_file(oso_path, false); ConstString oso_object; if (oso_file.Exists()) { auto oso_mod_time = FileSystem::GetModificationTime(oso_file); if (oso_mod_time != comp_unit_info->oso_mod_time) { obj_file->GetModule()->ReportError( "debug map object file '%s' has changed (actual time is " "%s, debug map time is %s" ") since this executable was linked, file will be ignored", oso_file.GetPath().c_str(), llvm::to_string(oso_mod_time).c_str(), llvm::to_string(comp_unit_info->oso_mod_time).c_str()); return NULL; } } else { const bool must_exist = true; if (!ObjectFile::SplitArchivePathWithObject(oso_path, oso_file, oso_object, must_exist)) { return NULL; } } // Always create a new module for .o files. Why? Because we // use the debug map, to add new sections to each .o file and // even though a .o file might not have changed, the sections // that get added to the .o file can change. ArchSpec oso_arch; // Only adopt the architecture from the module (not the vendor or OS) // since .o files for "i386-apple-ios" will historically show up as // "i386-apple-macosx" // due to the lack of a LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS // load command... oso_arch.SetTriple(m_obj_file->GetModule() ->GetArchitecture() .GetTriple() .getArchName() .str() .c_str()); comp_unit_info->oso_sp->module_sp.reset(new DebugMapModule( obj_file->GetModule(), GetCompUnitInfoIndex(comp_unit_info), oso_file, oso_arch, oso_object ? &oso_object : NULL, 0, oso_object ? comp_unit_info->oso_mod_time : llvm::sys::TimePoint<>())); } } if (comp_unit_info->oso_sp) return comp_unit_info->oso_sp->module_sp.get(); return NULL; } bool SymbolFileDWARFDebugMap::GetFileSpecForSO(uint32_t oso_idx, FileSpec &file_spec) { if (oso_idx < m_compile_unit_infos.size()) { if (m_compile_unit_infos[oso_idx].so_file) { file_spec = m_compile_unit_infos[oso_idx].so_file; return true; } } return false; } ObjectFile *SymbolFileDWARFDebugMap::GetObjectFileByOSOIndex(uint32_t oso_idx) { Module *oso_module = GetModuleByOSOIndex(oso_idx); if (oso_module) return oso_module->GetObjectFile(); return NULL; } SymbolFileDWARF * SymbolFileDWARFDebugMap::GetSymbolFile(const SymbolContext &sc) { CompileUnitInfo *comp_unit_info = GetCompUnitInfo(sc); if (comp_unit_info) return GetSymbolFileByCompUnitInfo(comp_unit_info); return NULL; } ObjectFile *SymbolFileDWARFDebugMap::GetObjectFileByCompUnitInfo( CompileUnitInfo *comp_unit_info) { Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info); if (oso_module) return oso_module->GetObjectFile(); return NULL; } uint32_t SymbolFileDWARFDebugMap::GetCompUnitInfoIndex( const CompileUnitInfo *comp_unit_info) { if (!m_compile_unit_infos.empty()) { const CompileUnitInfo *first_comp_unit_info = &m_compile_unit_infos.front(); const CompileUnitInfo *last_comp_unit_info = &m_compile_unit_infos.back(); if (first_comp_unit_info <= comp_unit_info && comp_unit_info <= last_comp_unit_info) return comp_unit_info - first_comp_unit_info; } return UINT32_MAX; } SymbolFileDWARF * SymbolFileDWARFDebugMap::GetSymbolFileByOSOIndex(uint32_t oso_idx) { if (oso_idx < m_compile_unit_infos.size()) return GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[oso_idx]); return NULL; } SymbolFileDWARF * SymbolFileDWARFDebugMap::GetSymbolFileAsSymbolFileDWARF(SymbolFile *sym_file) { if (sym_file && sym_file->GetPluginName() == SymbolFileDWARF::GetPluginNameStatic()) return (SymbolFileDWARF *)sym_file; return NULL; } SymbolFileDWARF *SymbolFileDWARFDebugMap::GetSymbolFileByCompUnitInfo( CompileUnitInfo *comp_unit_info) { Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info); if (oso_module) { SymbolVendor *sym_vendor = oso_module->GetSymbolVendor(); if (sym_vendor) return GetSymbolFileAsSymbolFileDWARF(sym_vendor->GetSymbolFile()); } return NULL; } uint32_t SymbolFileDWARFDebugMap::CalculateAbilities() { // In order to get the abilities of this plug-in, we look at the list of // N_OSO entries (object files) from the symbol table and make sure that // these files exist and also contain valid DWARF. If we get any of that // then we return the abilities of the first N_OSO's DWARF. const uint32_t oso_index_count = GetNumCompileUnits(); if (oso_index_count > 0) { InitOSO(); if (!m_compile_unit_infos.empty()) { return SymbolFile::CompileUnits | SymbolFile::Functions | SymbolFile::Blocks | SymbolFile::GlobalVariables | SymbolFile::LocalVariables | SymbolFile::VariableTypes | SymbolFile::LineTables; } } return 0; } uint32_t SymbolFileDWARFDebugMap::GetNumCompileUnits() { InitOSO(); return m_compile_unit_infos.size(); } CompUnitSP SymbolFileDWARFDebugMap::ParseCompileUnitAtIndex(uint32_t cu_idx) { CompUnitSP comp_unit_sp; const uint32_t cu_count = GetNumCompileUnits(); if (cu_idx < cu_count) { Module *oso_module = GetModuleByCompUnitInfo(&m_compile_unit_infos[cu_idx]); if (oso_module) { FileSpec so_file_spec; if (GetFileSpecForSO(cu_idx, so_file_spec)) { // User zero as the ID to match the compile unit at offset // zero in each .o file since each .o file can only have // one compile unit for now. lldb::user_id_t cu_id = 0; m_compile_unit_infos[cu_idx].compile_unit_sp.reset( new CompileUnit(m_obj_file->GetModule(), NULL, so_file_spec, cu_id, eLanguageTypeUnknown, eLazyBoolCalculate)); if (m_compile_unit_infos[cu_idx].compile_unit_sp) { // Let our symbol vendor know about this compile unit m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex( cu_idx, m_compile_unit_infos[cu_idx].compile_unit_sp); } } } comp_unit_sp = m_compile_unit_infos[cu_idx].compile_unit_sp; } return comp_unit_sp; } SymbolFileDWARFDebugMap::CompileUnitInfo * SymbolFileDWARFDebugMap::GetCompUnitInfo(const SymbolContext &sc) { const uint32_t cu_count = GetNumCompileUnits(); for (uint32_t i = 0; i < cu_count; ++i) { if (sc.comp_unit == m_compile_unit_infos[i].compile_unit_sp.get()) return &m_compile_unit_infos[i]; } return NULL; } size_t SymbolFileDWARFDebugMap::GetCompUnitInfosForModule( const lldb_private::Module *module, std::vector &cu_infos) { const uint32_t cu_count = GetNumCompileUnits(); for (uint32_t i = 0; i < cu_count; ++i) { if (module == GetModuleByCompUnitInfo(&m_compile_unit_infos[i])) cu_infos.push_back(&m_compile_unit_infos[i]); } return cu_infos.size(); } lldb::LanguageType SymbolFileDWARFDebugMap::ParseCompileUnitLanguage(const SymbolContext &sc) { SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); if (oso_dwarf) return oso_dwarf->ParseCompileUnitLanguage(sc); return eLanguageTypeUnknown; } size_t SymbolFileDWARFDebugMap::ParseCompileUnitFunctions(const SymbolContext &sc) { SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); if (oso_dwarf) return oso_dwarf->ParseCompileUnitFunctions(sc); return 0; } bool SymbolFileDWARFDebugMap::ParseCompileUnitLineTable( const SymbolContext &sc) { SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); if (oso_dwarf) return oso_dwarf->ParseCompileUnitLineTable(sc); return false; } bool SymbolFileDWARFDebugMap::ParseCompileUnitDebugMacros( const SymbolContext &sc) { SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); if (oso_dwarf) return oso_dwarf->ParseCompileUnitDebugMacros(sc); return false; } bool SymbolFileDWARFDebugMap::ParseCompileUnitSupportFiles( const SymbolContext &sc, FileSpecList &support_files) { SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); if (oso_dwarf) return oso_dwarf->ParseCompileUnitSupportFiles(sc, support_files); return false; } bool SymbolFileDWARFDebugMap::ParseCompileUnitIsOptimized( const lldb_private::SymbolContext &sc) { SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); if (oso_dwarf) return oso_dwarf->ParseCompileUnitIsOptimized(sc); return false; } bool SymbolFileDWARFDebugMap::ParseImportedModules( const SymbolContext &sc, std::vector &imported_modules) { SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); if (oso_dwarf) return oso_dwarf->ParseImportedModules(sc, imported_modules); return false; } size_t SymbolFileDWARFDebugMap::ParseFunctionBlocks(const SymbolContext &sc) { SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); if (oso_dwarf) return oso_dwarf->ParseFunctionBlocks(sc); return 0; } size_t SymbolFileDWARFDebugMap::ParseTypes(const SymbolContext &sc) { SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); if (oso_dwarf) return oso_dwarf->ParseTypes(sc); return 0; } size_t SymbolFileDWARFDebugMap::ParseVariablesForContext(const SymbolContext &sc) { SymbolFileDWARF *oso_dwarf = GetSymbolFile(sc); if (oso_dwarf) return oso_dwarf->ParseVariablesForContext(sc); return 0; } Type *SymbolFileDWARFDebugMap::ResolveTypeUID(lldb::user_id_t type_uid) { const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid); SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx); if (oso_dwarf) return oso_dwarf->ResolveTypeUID(type_uid); return NULL; } bool SymbolFileDWARFDebugMap::CompleteType(CompilerType &compiler_type) { bool success = false; if (compiler_type) { ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { if (oso_dwarf->HasForwardDeclForClangType(compiler_type)) { oso_dwarf->CompleteType(compiler_type); success = true; return true; } return false; }); } return success; } uint32_t SymbolFileDWARFDebugMap::ResolveSymbolContext( const Address &exe_so_addr, uint32_t resolve_scope, SymbolContext &sc) { uint32_t resolved_flags = 0; Symtab *symtab = m_obj_file->GetSymtab(); if (symtab) { const addr_t exe_file_addr = exe_so_addr.GetFileAddress(); const DebugMap::Entry *debug_map_entry = m_debug_map.FindEntryThatContains(exe_file_addr); if (debug_map_entry) { sc.symbol = symtab->SymbolAtIndex(debug_map_entry->data.GetExeSymbolIndex()); if (sc.symbol != NULL) { resolved_flags |= eSymbolContextSymbol; uint32_t oso_idx = 0; CompileUnitInfo *comp_unit_info = GetCompileUnitInfoForSymbolWithID(sc.symbol->GetID(), &oso_idx); if (comp_unit_info) { comp_unit_info->GetFileRangeMap(this); Module *oso_module = GetModuleByCompUnitInfo(comp_unit_info); if (oso_module) { lldb::addr_t oso_file_addr = exe_file_addr - debug_map_entry->GetRangeBase() + debug_map_entry->data.GetOSOFileAddress(); Address oso_so_addr; if (oso_module->ResolveFileAddress(oso_file_addr, oso_so_addr)) { resolved_flags |= oso_module->GetSymbolVendor()->ResolveSymbolContext( oso_so_addr, resolve_scope, sc); } } } } } } return resolved_flags; } uint32_t SymbolFileDWARFDebugMap::ResolveSymbolContext( const FileSpec &file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList &sc_list) { const uint32_t initial = sc_list.GetSize(); const uint32_t cu_count = GetNumCompileUnits(); for (uint32_t i = 0; i < cu_count; ++i) { // If we are checking for inlines, then we need to look through all // compile units no matter if "file_spec" matches. bool resolve = check_inlines; if (!resolve) { FileSpec so_file_spec; if (GetFileSpecForSO(i, so_file_spec)) { // Match the full path if the incoming file_spec has a directory (not // just a basename) const bool full_match = (bool)file_spec.GetDirectory(); resolve = FileSpec::Equal(file_spec, so_file_spec, full_match); } } if (resolve) { SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(i); if (oso_dwarf) oso_dwarf->ResolveSymbolContext(file_spec, line, check_inlines, resolve_scope, sc_list); } } return sc_list.GetSize() - initial; } uint32_t SymbolFileDWARFDebugMap::PrivateFindGlobalVariables( const ConstString &name, const CompilerDeclContext *parent_decl_ctx, const std::vector &indexes, // Indexes into the symbol table that match "name" uint32_t max_matches, VariableList &variables) { const uint32_t original_size = variables.GetSize(); const size_t match_count = indexes.size(); for (size_t i = 0; i < match_count; ++i) { uint32_t oso_idx; CompileUnitInfo *comp_unit_info = GetCompileUnitInfoForSymbolWithIndex(indexes[i], &oso_idx); if (comp_unit_info) { SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx); if (oso_dwarf) { if (oso_dwarf->FindGlobalVariables(name, parent_decl_ctx, true, max_matches, variables)) if (variables.GetSize() > max_matches) break; } } } return variables.GetSize() - original_size; } uint32_t SymbolFileDWARFDebugMap::FindGlobalVariables( const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, uint32_t max_matches, VariableList &variables) { // If we aren't appending the results to this list, then clear the list if (!append) variables.Clear(); // Remember how many variables are in the list before we search in case // we are appending the results to a variable list. const uint32_t original_size = variables.GetSize(); uint32_t total_matches = 0; ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { const uint32_t oso_matches = oso_dwarf->FindGlobalVariables( name, parent_decl_ctx, true, max_matches, variables); if (oso_matches > 0) { total_matches += oso_matches; // Are we getting all matches? if (max_matches == UINT32_MAX) return false; // Yep, continue getting everything // If we have found enough matches, lets get out if (max_matches >= total_matches) return true; // Update the max matches for any subsequent calls to find globals // in any other object files with DWARF max_matches -= oso_matches; } return false; }); // Return the number of variable that were appended to the list return variables.GetSize() - original_size; } uint32_t SymbolFileDWARFDebugMap::FindGlobalVariables(const RegularExpression ®ex, bool append, uint32_t max_matches, VariableList &variables) { // If we aren't appending the results to this list, then clear the list if (!append) variables.Clear(); // Remember how many variables are in the list before we search in case // we are appending the results to a variable list. const uint32_t original_size = variables.GetSize(); uint32_t total_matches = 0; ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { const uint32_t oso_matches = oso_dwarf->FindGlobalVariables(regex, true, max_matches, variables); if (oso_matches > 0) { total_matches += oso_matches; // Are we getting all matches? if (max_matches == UINT32_MAX) return false; // Yep, continue getting everything // If we have found enough matches, lets get out if (max_matches >= total_matches) return true; // Update the max matches for any subsequent calls to find globals // in any other object files with DWARF max_matches -= oso_matches; } return false; }); // Return the number of variable that were appended to the list return variables.GetSize() - original_size; } int SymbolFileDWARFDebugMap::SymbolContainsSymbolWithIndex( uint32_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info) { const uint32_t symbol_idx = *symbol_idx_ptr; if (symbol_idx < comp_unit_info->first_symbol_index) return -1; if (symbol_idx <= comp_unit_info->last_symbol_index) return 0; return 1; } int SymbolFileDWARFDebugMap::SymbolContainsSymbolWithID( user_id_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info) { const user_id_t symbol_id = *symbol_idx_ptr; if (symbol_id < comp_unit_info->first_symbol_id) return -1; if (symbol_id <= comp_unit_info->last_symbol_id) return 0; return 1; } SymbolFileDWARFDebugMap::CompileUnitInfo * SymbolFileDWARFDebugMap::GetCompileUnitInfoForSymbolWithIndex( uint32_t symbol_idx, uint32_t *oso_idx_ptr) { const uint32_t oso_index_count = m_compile_unit_infos.size(); CompileUnitInfo *comp_unit_info = NULL; if (oso_index_count) { comp_unit_info = (CompileUnitInfo *)bsearch( &symbol_idx, &m_compile_unit_infos[0], m_compile_unit_infos.size(), sizeof(CompileUnitInfo), (ComparisonFunction)SymbolContainsSymbolWithIndex); } if (oso_idx_ptr) { if (comp_unit_info != NULL) *oso_idx_ptr = comp_unit_info - &m_compile_unit_infos[0]; else *oso_idx_ptr = UINT32_MAX; } return comp_unit_info; } SymbolFileDWARFDebugMap::CompileUnitInfo * SymbolFileDWARFDebugMap::GetCompileUnitInfoForSymbolWithID( user_id_t symbol_id, uint32_t *oso_idx_ptr) { const uint32_t oso_index_count = m_compile_unit_infos.size(); CompileUnitInfo *comp_unit_info = NULL; if (oso_index_count) { comp_unit_info = (CompileUnitInfo *)::bsearch( &symbol_id, &m_compile_unit_infos[0], m_compile_unit_infos.size(), sizeof(CompileUnitInfo), (ComparisonFunction)SymbolContainsSymbolWithID); } if (oso_idx_ptr) { if (comp_unit_info != NULL) *oso_idx_ptr = comp_unit_info - &m_compile_unit_infos[0]; else *oso_idx_ptr = UINT32_MAX; } return comp_unit_info; } static void RemoveFunctionsWithModuleNotEqualTo(const ModuleSP &module_sp, SymbolContextList &sc_list, uint32_t start_idx) { // We found functions in .o files. Not all functions in the .o files // will have made it into the final output file. The ones that did // make it into the final output file will have a section whose module // matches the module from the ObjectFile for this SymbolFile. When // the modules don't match, then we have something that was in a // .o file, but doesn't map to anything in the final executable. uint32_t i = start_idx; while (i < sc_list.GetSize()) { SymbolContext sc; sc_list.GetContextAtIndex(i, sc); if (sc.function) { const SectionSP section_sp( sc.function->GetAddressRange().GetBaseAddress().GetSection()); if (section_sp->GetModule() != module_sp) { sc_list.RemoveContextAtIndex(i); continue; } } ++i; } } uint32_t SymbolFileDWARFDebugMap::FindFunctions( const ConstString &name, const CompilerDeclContext *parent_decl_ctx, uint32_t name_type_mask, bool include_inlines, bool append, SymbolContextList &sc_list) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "SymbolFileDWARFDebugMap::FindFunctions (name = %s)", name.GetCString()); uint32_t initial_size = 0; if (append) initial_size = sc_list.GetSize(); else sc_list.Clear(); ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { uint32_t sc_idx = sc_list.GetSize(); if (oso_dwarf->FindFunctions(name, parent_decl_ctx, name_type_mask, include_inlines, true, sc_list)) { RemoveFunctionsWithModuleNotEqualTo(m_obj_file->GetModule(), sc_list, sc_idx); } return false; }); return sc_list.GetSize() - initial_size; } uint32_t SymbolFileDWARFDebugMap::FindFunctions(const RegularExpression ®ex, bool include_inlines, bool append, SymbolContextList &sc_list) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "SymbolFileDWARFDebugMap::FindFunctions (regex = '%s')", regex.GetText().str().c_str()); uint32_t initial_size = 0; if (append) initial_size = sc_list.GetSize(); else sc_list.Clear(); ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { uint32_t sc_idx = sc_list.GetSize(); if (oso_dwarf->FindFunctions(regex, include_inlines, true, sc_list)) { RemoveFunctionsWithModuleNotEqualTo(m_obj_file->GetModule(), sc_list, sc_idx); } return false; }); return sc_list.GetSize() - initial_size; } size_t SymbolFileDWARFDebugMap::GetTypes(SymbolContextScope *sc_scope, uint32_t type_mask, TypeList &type_list) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "SymbolFileDWARFDebugMap::GetTypes (type_mask = 0x%8.8x)", type_mask); uint32_t initial_size = type_list.GetSize(); SymbolFileDWARF *oso_dwarf = NULL; if (sc_scope) { SymbolContext sc; sc_scope->CalculateSymbolContext(&sc); CompileUnitInfo *cu_info = GetCompUnitInfo(sc); if (cu_info) { oso_dwarf = GetSymbolFileByCompUnitInfo(cu_info); if (oso_dwarf) oso_dwarf->GetTypes(sc_scope, type_mask, type_list); } } else { ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { oso_dwarf->GetTypes(sc_scope, type_mask, type_list); return false; }); } return type_list.GetSize() - initial_size; } TypeSP SymbolFileDWARFDebugMap::FindDefinitionTypeForDWARFDeclContext( const DWARFDeclContext &die_decl_ctx) { TypeSP type_sp; ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { type_sp = oso_dwarf->FindDefinitionTypeForDWARFDeclContext(die_decl_ctx); return ((bool)type_sp); }); return type_sp; } bool SymbolFileDWARFDebugMap::Supports_DW_AT_APPLE_objc_complete_type( SymbolFileDWARF *skip_dwarf_oso) { if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolCalculate) { m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolNo; ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { if (skip_dwarf_oso != oso_dwarf && oso_dwarf->Supports_DW_AT_APPLE_objc_complete_type(NULL)) { m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes; return true; } return false; }); } return m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolYes; } TypeSP SymbolFileDWARFDebugMap::FindCompleteObjCDefinitionTypeForDIE( const DWARFDIE &die, const ConstString &type_name, bool must_be_implementation) { // If we have a debug map, we will have an Objective C symbol whose name is // the type name and whose type is eSymbolTypeObjCClass. If we can find that // symbol and find its containing parent, we can locate the .o file that will // contain the implementation definition since it will be scoped inside the // N_SO // and we can then locate the SymbolFileDWARF that corresponds to that N_SO. SymbolFileDWARF *oso_dwarf = NULL; TypeSP type_sp; ObjectFile *module_objfile = m_obj_file->GetModule()->GetObjectFile(); if (module_objfile) { Symtab *symtab = module_objfile->GetSymtab(); if (symtab) { Symbol *objc_class_symbol = symtab->FindFirstSymbolWithNameAndType( type_name, eSymbolTypeObjCClass, Symtab::eDebugAny, Symtab::eVisibilityAny); if (objc_class_symbol) { // Get the N_SO symbol that contains the objective C class symbol as // this // should be the .o file that contains the real definition... const Symbol *source_file_symbol = symtab->GetParent(objc_class_symbol); if (source_file_symbol && source_file_symbol->GetType() == eSymbolTypeSourceFile) { const uint32_t source_file_symbol_idx = symtab->GetIndexForSymbol(source_file_symbol); if (source_file_symbol_idx != UINT32_MAX) { CompileUnitInfo *compile_unit_info = GetCompileUnitInfoForSymbolWithIndex(source_file_symbol_idx, NULL); if (compile_unit_info) { oso_dwarf = GetSymbolFileByCompUnitInfo(compile_unit_info); if (oso_dwarf) { TypeSP type_sp(oso_dwarf->FindCompleteObjCDefinitionTypeForDIE( die, type_name, must_be_implementation)); if (type_sp) { return type_sp; } } } } } } } } // Only search all .o files for the definition if we don't need the // implementation // because otherwise, with a valid debug map we should have the ObjC class // symbol and // the code above should have found it. if (must_be_implementation == false) { TypeSP type_sp; ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { type_sp = oso_dwarf->FindCompleteObjCDefinitionTypeForDIE( die, type_name, must_be_implementation); return (bool)type_sp; }); return type_sp; } return TypeSP(); } uint32_t SymbolFileDWARFDebugMap::FindTypes( const SymbolContext &sc, const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, uint32_t max_matches, llvm::DenseSet &searched_symbol_files, TypeMap &types) { if (!append) types.Clear(); const uint32_t initial_types_size = types.GetSize(); SymbolFileDWARF *oso_dwarf; if (sc.comp_unit) { oso_dwarf = GetSymbolFile(sc); if (oso_dwarf) return oso_dwarf->FindTypes(sc, name, parent_decl_ctx, append, max_matches, searched_symbol_files, types); } else { ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { oso_dwarf->FindTypes(sc, name, parent_decl_ctx, append, max_matches, searched_symbol_files, types); if (types.GetSize() >= max_matches) return true; else return false; }); } return types.GetSize() - initial_types_size; } // // uint32_t // SymbolFileDWARFDebugMap::FindTypes (const SymbolContext& sc, const // RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding // encoding, lldb::user_id_t udt_uid, TypeList& types) //{ // SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); // if (oso_dwarf) // return oso_dwarf->FindTypes (sc, regex, append, max_matches, encoding, // udt_uid, types); // return 0; //} CompilerDeclContext SymbolFileDWARFDebugMap::FindNamespace( const lldb_private::SymbolContext &sc, const lldb_private::ConstString &name, const CompilerDeclContext *parent_decl_ctx) { CompilerDeclContext matching_namespace; SymbolFileDWARF *oso_dwarf; if (sc.comp_unit) { oso_dwarf = GetSymbolFile(sc); if (oso_dwarf) matching_namespace = oso_dwarf->FindNamespace(sc, name, parent_decl_ctx); } else { ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { matching_namespace = oso_dwarf->FindNamespace(sc, name, parent_decl_ctx); return (bool)matching_namespace; }); } return matching_namespace; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ lldb_private::ConstString SymbolFileDWARFDebugMap::GetPluginName() { return GetPluginNameStatic(); } uint32_t SymbolFileDWARFDebugMap::GetPluginVersion() { return 1; } lldb::CompUnitSP SymbolFileDWARFDebugMap::GetCompileUnit(SymbolFileDWARF *oso_dwarf) { if (oso_dwarf) { const uint32_t cu_count = GetNumCompileUnits(); for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) { SymbolFileDWARF *oso_symfile = GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[cu_idx]); if (oso_symfile == oso_dwarf) { if (!m_compile_unit_infos[cu_idx].compile_unit_sp) m_compile_unit_infos[cu_idx].compile_unit_sp = ParseCompileUnitAtIndex(cu_idx); return m_compile_unit_infos[cu_idx].compile_unit_sp; } } } - assert(!"this shouldn't happen"); - return lldb::CompUnitSP(); + llvm_unreachable("this shouldn't happen"); } SymbolFileDWARFDebugMap::CompileUnitInfo * SymbolFileDWARFDebugMap::GetCompileUnitInfo(SymbolFileDWARF *oso_dwarf) { if (oso_dwarf) { const uint32_t cu_count = GetNumCompileUnits(); for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) { SymbolFileDWARF *oso_symfile = GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[cu_idx]); if (oso_symfile == oso_dwarf) { return &m_compile_unit_infos[cu_idx]; } } } return NULL; } void SymbolFileDWARFDebugMap::SetCompileUnit(SymbolFileDWARF *oso_dwarf, const CompUnitSP &cu_sp) { if (oso_dwarf) { const uint32_t cu_count = GetNumCompileUnits(); for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) { SymbolFileDWARF *oso_symfile = GetSymbolFileByCompUnitInfo(&m_compile_unit_infos[cu_idx]); if (oso_symfile == oso_dwarf) { if (m_compile_unit_infos[cu_idx].compile_unit_sp) { assert(m_compile_unit_infos[cu_idx].compile_unit_sp.get() == cu_sp.get()); } else { m_compile_unit_infos[cu_idx].compile_unit_sp = cu_sp; m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex( cu_idx, cu_sp); } } } } } CompilerDeclContext SymbolFileDWARFDebugMap::GetDeclContextForUID(lldb::user_id_t type_uid) { const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid); SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx); if (oso_dwarf) return oso_dwarf->GetDeclContextForUID(type_uid); return CompilerDeclContext(); } CompilerDeclContext SymbolFileDWARFDebugMap::GetDeclContextContainingUID(lldb::user_id_t type_uid) { const uint64_t oso_idx = GetOSOIndexFromUserID(type_uid); SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx); if (oso_dwarf) return oso_dwarf->GetDeclContextContainingUID(type_uid); return CompilerDeclContext(); } void SymbolFileDWARFDebugMap::ParseDeclsForContext( lldb_private::CompilerDeclContext decl_ctx) { ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) -> bool { oso_dwarf->ParseDeclsForContext(decl_ctx); return true; // Keep iterating }); } bool SymbolFileDWARFDebugMap::AddOSOFileRange(CompileUnitInfo *cu_info, lldb::addr_t exe_file_addr, lldb::addr_t exe_byte_size, lldb::addr_t oso_file_addr, lldb::addr_t oso_byte_size) { const uint32_t debug_map_idx = m_debug_map.FindEntryIndexThatContains(exe_file_addr); if (debug_map_idx != UINT32_MAX) { DebugMap::Entry *debug_map_entry = m_debug_map.FindEntryThatContains(exe_file_addr); debug_map_entry->data.SetOSOFileAddress(oso_file_addr); addr_t range_size = std::min(exe_byte_size, oso_byte_size); if (range_size == 0) { range_size = std::max(exe_byte_size, oso_byte_size); if (range_size == 0) range_size = 1; } cu_info->file_range_map.Append( FileRangeMap::Entry(oso_file_addr, range_size, exe_file_addr)); return true; } return false; } void SymbolFileDWARFDebugMap::FinalizeOSOFileRanges(CompileUnitInfo *cu_info) { cu_info->file_range_map.Sort(); #if defined(DEBUG_OSO_DMAP) const FileRangeMap &oso_file_range_map = cu_info->GetFileRangeMap(this); const size_t n = oso_file_range_map.GetSize(); printf("SymbolFileDWARFDebugMap::FinalizeOSOFileRanges (cu_info = %p) %s\n", cu_info, cu_info->oso_sp->module_sp->GetFileSpec().GetPath().c_str()); for (size_t i = 0; i < n; ++i) { const FileRangeMap::Entry &entry = oso_file_range_map.GetEntryRef(i); printf("oso [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ") ==> exe [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ")\n", entry.GetRangeBase(), entry.GetRangeEnd(), entry.data, entry.data + entry.GetByteSize()); } #endif } lldb::addr_t SymbolFileDWARFDebugMap::LinkOSOFileAddress(SymbolFileDWARF *oso_symfile, lldb::addr_t oso_file_addr) { CompileUnitInfo *cu_info = GetCompileUnitInfo(oso_symfile); if (cu_info) { const FileRangeMap::Entry *oso_range_entry = cu_info->GetFileRangeMap(this).FindEntryThatContains(oso_file_addr); if (oso_range_entry) { const DebugMap::Entry *debug_map_entry = m_debug_map.FindEntryThatContains(oso_range_entry->data); if (debug_map_entry) { const lldb::addr_t offset = oso_file_addr - oso_range_entry->GetRangeBase(); const lldb::addr_t exe_file_addr = debug_map_entry->GetRangeBase() + offset; return exe_file_addr; } } } return LLDB_INVALID_ADDRESS; } bool SymbolFileDWARFDebugMap::LinkOSOAddress(Address &addr) { // Make sure this address hasn't been fixed already Module *exe_module = GetObjectFile()->GetModule().get(); Module *addr_module = addr.GetModule().get(); if (addr_module == exe_module) return true; // Address is already in terms of the main executable module CompileUnitInfo *cu_info = GetCompileUnitInfo(GetSymbolFileAsSymbolFileDWARF( addr_module->GetSymbolVendor()->GetSymbolFile())); if (cu_info) { const lldb::addr_t oso_file_addr = addr.GetFileAddress(); const FileRangeMap::Entry *oso_range_entry = cu_info->GetFileRangeMap(this).FindEntryThatContains(oso_file_addr); if (oso_range_entry) { const DebugMap::Entry *debug_map_entry = m_debug_map.FindEntryThatContains(oso_range_entry->data); if (debug_map_entry) { const lldb::addr_t offset = oso_file_addr - oso_range_entry->GetRangeBase(); const lldb::addr_t exe_file_addr = debug_map_entry->GetRangeBase() + offset; return exe_module->ResolveFileAddress(exe_file_addr, addr); } } } return true; } LineTable *SymbolFileDWARFDebugMap::LinkOSOLineTable(SymbolFileDWARF *oso_dwarf, LineTable *line_table) { CompileUnitInfo *cu_info = GetCompileUnitInfo(oso_dwarf); if (cu_info) return line_table->LinkLineTable(cu_info->GetFileRangeMap(this)); return NULL; } size_t SymbolFileDWARFDebugMap::AddOSOARanges(SymbolFileDWARF *dwarf2Data, DWARFDebugAranges *debug_aranges) { size_t num_line_entries_added = 0; if (debug_aranges && dwarf2Data) { CompileUnitInfo *compile_unit_info = GetCompileUnitInfo(dwarf2Data); if (compile_unit_info) { const FileRangeMap &file_range_map = compile_unit_info->GetFileRangeMap(this); for (size_t idx = 0; idx < file_range_map.GetSize(); idx++) { const FileRangeMap::Entry *entry = file_range_map.GetEntryAtIndex(idx); if (entry) { debug_aranges->AppendRange(dwarf2Data->GetID(), entry->GetRangeBase(), entry->GetRangeEnd()); num_line_entries_added++; } } } } return num_line_entries_added; } Index: vendor/lldb/dist/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp (revision 311541) +++ vendor/lldb/dist/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp (revision 311542) @@ -1,665 +1,664 @@ //===-- UnwindAssemblyInstEmulation.cpp --------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "UnwindAssemblyInstEmulation.h" #include "lldb/Core/Address.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/Error.h" #include "lldb/Core/FormatEntity.h" #include "lldb/Core/Log.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/StreamString.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" using namespace lldb; using namespace lldb_private; //----------------------------------------------------------------------------------------------- // UnwindAssemblyInstEmulation method definitions //----------------------------------------------------------------------------------------------- bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( AddressRange &range, Thread &thread, UnwindPlan &unwind_plan) { std::vector function_text(range.GetByteSize()); ProcessSP process_sp(thread.GetProcess()); if (process_sp) { Error error; const bool prefer_file_cache = true; if (process_sp->GetTarget().ReadMemory( range.GetBaseAddress(), prefer_file_cache, function_text.data(), range.GetByteSize(), error) != range.GetByteSize()) { return false; } } return GetNonCallSiteUnwindPlanFromAssembly( range, function_text.data(), function_text.size(), unwind_plan); } bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( AddressRange &range, uint8_t *opcode_data, size_t opcode_size, UnwindPlan &unwind_plan) { if (opcode_data == nullptr || opcode_size == 0) return false; if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid() && m_inst_emulator_ap.get()) { // The instruction emulation subclass setup the unwind plan for the // first instruction. m_inst_emulator_ap->CreateFunctionEntryUnwind(unwind_plan); // CreateFunctionEntryUnwind should have created the first row. If it // doesn't, then we are done. if (unwind_plan.GetRowCount() == 0) return false; const bool prefer_file_cache = true; DisassemblerSP disasm_sp(Disassembler::DisassembleBytes( m_arch, NULL, NULL, range.GetBaseAddress(), opcode_data, opcode_size, 99999, prefer_file_cache)); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (disasm_sp) { m_range_ptr = ⦥ m_unwind_plan_ptr = &unwind_plan; const uint32_t addr_byte_size = m_arch.GetAddressByteSize(); const bool show_address = true; const bool show_bytes = true; m_inst_emulator_ap->GetRegisterInfo(unwind_plan.GetRegisterKind(), unwind_plan.GetInitialCFARegister(), m_cfa_reg_info); m_fp_is_cfa = false; m_register_values.clear(); m_pushed_regs.clear(); // Initialize the CFA with a known value. In the 32 bit case // it will be 0x80000000, and in the 64 bit case 0x8000000000000000. // We use the address byte size to be safe for any future address sizes m_initial_sp = (1ull << ((addr_byte_size * 8) - 1)); RegisterValue cfa_reg_value; cfa_reg_value.SetUInt(m_initial_sp, m_cfa_reg_info.byte_size); SetRegisterValue(m_cfa_reg_info, cfa_reg_value); const InstructionList &inst_list = disasm_sp->GetInstructionList(); const size_t num_instructions = inst_list.GetSize(); if (num_instructions > 0) { Instruction *inst = inst_list.GetInstructionAtIndex(0).get(); const lldb::addr_t base_addr = inst->GetAddress().GetFileAddress(); // Map for storing the unwind plan row and the value of the registers at // a given offset. // When we see a forward branch we add a new entry to this map with the // actual unwind plan // row and register context for the target address of the branch as the // current data have // to be valid for the target address of the branch too if we are in the // same function. std::map> saved_unwind_states; // Make a copy of the current instruction Row and save it in m_curr_row // so we can add updates as we process the instructions. UnwindPlan::RowSP last_row = unwind_plan.GetLastRow(); UnwindPlan::Row *newrow = new UnwindPlan::Row; if (last_row.get()) *newrow = *last_row.get(); m_curr_row.reset(newrow); // Add the initial state to the save list with offset 0. saved_unwind_states.insert({0, {last_row, m_register_values}}); // cache the pc register number (in whatever register numbering this // UnwindPlan uses) for // quick reference during instruction parsing. RegisterInfo pc_reg_info; m_inst_emulator_ap->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc_reg_info); // cache the return address register number (in whatever register // numbering this UnwindPlan uses) for // quick reference during instruction parsing. RegisterInfo ra_reg_info; m_inst_emulator_ap->GetRegisterInfo( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, ra_reg_info); // The architecture dependent condition code of the last processed // instruction. EmulateInstruction::InstructionCondition last_condition = EmulateInstruction::UnconditionalCondition; lldb::addr_t condition_block_start_offset = 0; for (size_t idx = 0; idx < num_instructions; ++idx) { m_curr_row_modified = false; m_forward_branch_offset = 0; inst = inst_list.GetInstructionAtIndex(idx).get(); if (inst) { lldb::addr_t current_offset = inst->GetAddress().GetFileAddress() - base_addr; auto it = saved_unwind_states.upper_bound(current_offset); assert(it != saved_unwind_states.begin() && "Unwind row for the function entry missing"); --it; // Move it to the row corresponding to the current offset // If the offset of m_curr_row don't match with the offset we see in // saved_unwind_states // then we have to update m_curr_row and m_register_values based on // the saved values. It // is happenning after we processed an epilogue and a return to // caller instruction. if (it->second.first->GetOffset() != m_curr_row->GetOffset()) { UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *it->second.first; m_curr_row.reset(newrow); m_register_values = it->second.second; } m_inst_emulator_ap->SetInstruction(inst->GetOpcode(), inst->GetAddress(), nullptr); if (last_condition != m_inst_emulator_ap->GetInstructionCondition()) { if (m_inst_emulator_ap->GetInstructionCondition() != EmulateInstruction::UnconditionalCondition && saved_unwind_states.count(current_offset) == 0) { // If we don't have a saved row for the current offset then save // our // current state because we will have to restore it after the // conditional block. auto new_row = std::make_shared(*m_curr_row.get()); saved_unwind_states.insert( {current_offset, {new_row, m_register_values}}); } // If the last instruction was conditional with a different // condition // then the then current condition then restore the condition. if (last_condition != EmulateInstruction::UnconditionalCondition) { const auto &saved_state = saved_unwind_states.at(condition_block_start_offset); m_curr_row = std::make_shared(*saved_state.first); m_curr_row->SetOffset(current_offset); m_register_values = saved_state.second; bool replace_existing = true; // The last instruction might already // created a row for this offset and // we want to overwrite it. unwind_plan.InsertRow( std::make_shared(*m_curr_row), replace_existing); } // We are starting a new conditional block at the catual offset condition_block_start_offset = current_offset; } if (log && log->GetVerbose()) { StreamString strm; lldb_private::FormatEntity::Entry format; FormatEntity::Parse("${frame.pc}: ", format); inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize(), show_address, show_bytes, NULL, NULL, NULL, &format, 0); log->PutString(strm.GetString()); } last_condition = m_inst_emulator_ap->GetInstructionCondition(); m_inst_emulator_ap->EvaluateInstruction( eEmulateInstructionOptionIgnoreConditions); // If the current instruction is a branch forward then save the // current CFI information // for the offset where we are branching. if (m_forward_branch_offset != 0 && range.ContainsFileAddress(inst->GetAddress().GetFileAddress() + m_forward_branch_offset)) { auto newrow = std::make_shared(*m_curr_row.get()); newrow->SetOffset(current_offset + m_forward_branch_offset); saved_unwind_states.insert( {current_offset + m_forward_branch_offset, {newrow, m_register_values}}); unwind_plan.InsertRow(newrow); } // Were there any changes to the CFI while evaluating this // instruction? if (m_curr_row_modified) { // Save the modified row if we don't already have a CFI row in the // currennt address if (saved_unwind_states.count( current_offset + inst->GetOpcode().GetByteSize()) == 0) { m_curr_row->SetOffset(current_offset + inst->GetOpcode().GetByteSize()); unwind_plan.InsertRow(m_curr_row); saved_unwind_states.insert( {current_offset + inst->GetOpcode().GetByteSize(), {m_curr_row, m_register_values}}); // Allocate a new Row for m_curr_row, copy the current state // into it UnwindPlan::Row *newrow = new UnwindPlan::Row; *newrow = *m_curr_row.get(); m_curr_row.reset(newrow); } } } } } } if (log && log->GetVerbose()) { StreamString strm; lldb::addr_t base_addr = range.GetBaseAddress().GetFileAddress(); strm.Printf("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):", base_addr, base_addr + range.GetByteSize()); unwind_plan.Dump(strm, nullptr, base_addr); log->PutString(strm.GetString()); } return unwind_plan.GetRowCount() > 0; } return false; } bool UnwindAssemblyInstEmulation::AugmentUnwindPlanFromCallSite( AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) { return false; } bool UnwindAssemblyInstEmulation::GetFastUnwindPlan(AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) { return false; } bool UnwindAssemblyInstEmulation::FirstNonPrologueInsn( AddressRange &func, const ExecutionContext &exe_ctx, Address &first_non_prologue_insn) { return false; } UnwindAssembly * UnwindAssemblyInstEmulation::CreateInstance(const ArchSpec &arch) { std::unique_ptr inst_emulator_ap( EmulateInstruction::FindPlugin(arch, eInstructionTypePrologueEpilogue, NULL)); // Make sure that all prologue instructions are handled if (inst_emulator_ap.get()) return new UnwindAssemblyInstEmulation(arch, inst_emulator_ap.release()); return NULL; } //------------------------------------------------------------------ // PluginInterface protocol in UnwindAssemblyParser_x86 //------------------------------------------------------------------ ConstString UnwindAssemblyInstEmulation::GetPluginName() { return GetPluginNameStatic(); } uint32_t UnwindAssemblyInstEmulation::GetPluginVersion() { return 1; } void UnwindAssemblyInstEmulation::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); } void UnwindAssemblyInstEmulation::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } ConstString UnwindAssemblyInstEmulation::GetPluginNameStatic() { static ConstString g_name("inst-emulation"); return g_name; } const char *UnwindAssemblyInstEmulation::GetPluginDescriptionStatic() { return "Instruction emulation based unwind information."; } uint64_t UnwindAssemblyInstEmulation::MakeRegisterKindValuePair( const RegisterInfo ®_info) { lldb::RegisterKind reg_kind; uint32_t reg_num; if (EmulateInstruction::GetBestRegisterKindAndNumber(®_info, reg_kind, reg_num)) return (uint64_t)reg_kind << 24 | reg_num; return 0ull; } void UnwindAssemblyInstEmulation::SetRegisterValue( const RegisterInfo ®_info, const RegisterValue ®_value) { m_register_values[MakeRegisterKindValuePair(reg_info)] = reg_value; } bool UnwindAssemblyInstEmulation::GetRegisterValue(const RegisterInfo ®_info, RegisterValue ®_value) { const uint64_t reg_id = MakeRegisterKindValuePair(reg_info); RegisterValueMap::const_iterator pos = m_register_values.find(reg_id); if (pos != m_register_values.end()) { reg_value = pos->second; return true; // We had a real value that comes from an opcode that wrote // to it... } // We are making up a value that is recognizable... reg_value.SetUInt(reg_id, reg_info.byte_size); return false; } size_t UnwindAssemblyInstEmulation::ReadMemory( EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst, size_t dst_len) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (log && log->GetVerbose()) { StreamString strm; strm.Printf( "UnwindAssemblyInstEmulation::ReadMemory (addr = 0x%16.16" PRIx64 ", dst = %p, dst_len = %" PRIu64 ", context = ", addr, dst, (uint64_t)dst_len); context.Dump(strm, instruction); log->PutString(strm.GetString()); } memset(dst, 0, dst_len); return dst_len; } size_t UnwindAssemblyInstEmulation::WriteMemory( EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, lldb::addr_t addr, const void *dst, size_t dst_len) { if (baton && dst && dst_len) return ((UnwindAssemblyInstEmulation *)baton) ->WriteMemory(instruction, context, addr, dst, dst_len); return 0; } size_t UnwindAssemblyInstEmulation::WriteMemory( EmulateInstruction *instruction, const EmulateInstruction::Context &context, lldb::addr_t addr, const void *dst, size_t dst_len) { DataExtractor data(dst, dst_len, instruction->GetArchitecture().GetByteOrder(), instruction->GetArchitecture().GetAddressByteSize()); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (log && log->GetVerbose()) { StreamString strm; strm.PutCString("UnwindAssemblyInstEmulation::WriteMemory ("); data.Dump(&strm, 0, eFormatBytes, 1, dst_len, UINT32_MAX, addr, 0, 0); strm.PutCString(", context = "); context.Dump(strm, instruction); log->PutString(strm.GetString()); } const bool cant_replace = false; switch (context.type) { default: case EmulateInstruction::eContextInvalid: case EmulateInstruction::eContextReadOpcode: case EmulateInstruction::eContextImmediate: case EmulateInstruction::eContextAdjustBaseRegister: case EmulateInstruction::eContextRegisterPlusOffset: case EmulateInstruction::eContextAdjustPC: case EmulateInstruction::eContextRegisterStore: case EmulateInstruction::eContextRegisterLoad: case EmulateInstruction::eContextRelativeBranchImmediate: case EmulateInstruction::eContextAbsoluteBranchRegister: case EmulateInstruction::eContextSupervisorCall: case EmulateInstruction::eContextTableBranchReadMemory: case EmulateInstruction::eContextWriteRegisterRandomBits: case EmulateInstruction::eContextWriteMemoryRandomBits: case EmulateInstruction::eContextArithmetic: case EmulateInstruction::eContextAdvancePC: case EmulateInstruction::eContextReturnFromException: case EmulateInstruction::eContextPopRegisterOffStack: case EmulateInstruction::eContextAdjustStackPointer: break; case EmulateInstruction::eContextPushRegisterOnStack: { uint32_t reg_num = LLDB_INVALID_REGNUM; uint32_t generic_regnum = LLDB_INVALID_REGNUM; - if (context.info_type == - EmulateInstruction::eInfoTypeRegisterToRegisterPlusOffset) { - const uint32_t unwind_reg_kind = m_unwind_plan_ptr->GetRegisterKind(); - reg_num = context.info.RegisterToRegisterPlusOffset.data_reg - .kinds[unwind_reg_kind]; - generic_regnum = context.info.RegisterToRegisterPlusOffset.data_reg - .kinds[eRegisterKindGeneric]; - } else - assert(!"unhandled case, add code to handle this!"); + assert(context.info_type == + EmulateInstruction::eInfoTypeRegisterToRegisterPlusOffset && + "unhandled case, add code to handle this!"); + const uint32_t unwind_reg_kind = m_unwind_plan_ptr->GetRegisterKind(); + reg_num = context.info.RegisterToRegisterPlusOffset.data_reg + .kinds[unwind_reg_kind]; + generic_regnum = context.info.RegisterToRegisterPlusOffset.data_reg + .kinds[eRegisterKindGeneric]; if (reg_num != LLDB_INVALID_REGNUM && generic_regnum != LLDB_REGNUM_GENERIC_SP) { if (m_pushed_regs.find(reg_num) == m_pushed_regs.end()) { m_pushed_regs[reg_num] = addr; const int32_t offset = addr - m_initial_sp; m_curr_row->SetRegisterLocationToAtCFAPlusOffset(reg_num, offset, cant_replace); m_curr_row_modified = true; } } } break; } return dst_len; } bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction, void *baton, const RegisterInfo *reg_info, RegisterValue ®_value) { if (baton && reg_info) return ((UnwindAssemblyInstEmulation *)baton) ->ReadRegister(instruction, reg_info, reg_value); return false; } bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction, const RegisterInfo *reg_info, RegisterValue ®_value) { bool synthetic = GetRegisterValue(*reg_info, reg_value); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (log && log->GetVerbose()) { StreamString strm; strm.Printf("UnwindAssemblyInstEmulation::ReadRegister (name = \"%s\") => " "synthetic_value = %i, value = ", reg_info->name, synthetic); reg_value.Dump(&strm, reg_info, false, false, eFormatDefault); log->PutString(strm.GetString()); } return true; } bool UnwindAssemblyInstEmulation::WriteRegister( EmulateInstruction *instruction, void *baton, const EmulateInstruction::Context &context, const RegisterInfo *reg_info, const RegisterValue ®_value) { if (baton && reg_info) return ((UnwindAssemblyInstEmulation *)baton) ->WriteRegister(instruction, context, reg_info, reg_value); return false; } bool UnwindAssemblyInstEmulation::WriteRegister( EmulateInstruction *instruction, const EmulateInstruction::Context &context, const RegisterInfo *reg_info, const RegisterValue ®_value) { Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND)); if (log && log->GetVerbose()) { StreamString strm; strm.Printf( "UnwindAssemblyInstEmulation::WriteRegister (name = \"%s\", value = ", reg_info->name); reg_value.Dump(&strm, reg_info, false, false, eFormatDefault); strm.PutCString(", context = "); context.Dump(strm, instruction); log->PutString(strm.GetString()); } SetRegisterValue(*reg_info, reg_value); switch (context.type) { case EmulateInstruction::eContextInvalid: case EmulateInstruction::eContextReadOpcode: case EmulateInstruction::eContextImmediate: case EmulateInstruction::eContextAdjustBaseRegister: case EmulateInstruction::eContextRegisterPlusOffset: case EmulateInstruction::eContextAdjustPC: case EmulateInstruction::eContextRegisterStore: case EmulateInstruction::eContextSupervisorCall: case EmulateInstruction::eContextTableBranchReadMemory: case EmulateInstruction::eContextWriteRegisterRandomBits: case EmulateInstruction::eContextWriteMemoryRandomBits: case EmulateInstruction::eContextAdvancePC: case EmulateInstruction::eContextReturnFromException: case EmulateInstruction::eContextPushRegisterOnStack: case EmulateInstruction::eContextRegisterLoad: // { // const uint32_t reg_num = // reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; // if (reg_num != LLDB_INVALID_REGNUM) // { // const bool can_replace_only_if_unspecified = true; // // m_curr_row.SetRegisterLocationToUndefined (reg_num, // can_replace_only_if_unspecified, // can_replace_only_if_unspecified); // m_curr_row_modified = true; // } // } break; case EmulateInstruction::eContextArithmetic: { // If we adjusted the current frame pointer by a constant then adjust the // CFA offset // with the same amount. lldb::RegisterKind kind = m_unwind_plan_ptr->GetRegisterKind(); if (m_fp_is_cfa && reg_info->kinds[kind] == m_cfa_reg_info.kinds[kind] && context.info_type == EmulateInstruction::eInfoTypeRegisterPlusOffset && context.info.RegisterPlusOffset.reg.kinds[kind] == m_cfa_reg_info.kinds[kind]) { const int64_t offset = context.info.RegisterPlusOffset.signed_offset; m_curr_row->GetCFAValue().IncOffset(-1 * offset); m_curr_row_modified = true; } } break; case EmulateInstruction::eContextAbsoluteBranchRegister: case EmulateInstruction::eContextRelativeBranchImmediate: { if (context.info_type == EmulateInstruction::eInfoTypeISAAndImmediate && context.info.ISAAndImmediate.unsigned_data32 > 0) { m_forward_branch_offset = context.info.ISAAndImmediateSigned.signed_data32; } else if (context.info_type == EmulateInstruction::eInfoTypeISAAndImmediateSigned && context.info.ISAAndImmediateSigned.signed_data32 > 0) { m_forward_branch_offset = context.info.ISAAndImmediate.unsigned_data32; } else if (context.info_type == EmulateInstruction::eInfoTypeImmediate && context.info.unsigned_immediate > 0) { m_forward_branch_offset = context.info.unsigned_immediate; } else if (context.info_type == EmulateInstruction::eInfoTypeImmediateSigned && context.info.signed_immediate > 0) { m_forward_branch_offset = context.info.signed_immediate; } } break; case EmulateInstruction::eContextPopRegisterOffStack: { const uint32_t reg_num = reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; const uint32_t generic_regnum = reg_info->kinds[eRegisterKindGeneric]; if (reg_num != LLDB_INVALID_REGNUM && generic_regnum != LLDB_REGNUM_GENERIC_SP) { switch (context.info_type) { case EmulateInstruction::eInfoTypeAddress: if (m_pushed_regs.find(reg_num) != m_pushed_regs.end() && context.info.address == m_pushed_regs[reg_num]) { m_curr_row->SetRegisterLocationToSame(reg_num, false /*must_replace*/); m_curr_row_modified = true; } break; case EmulateInstruction::eInfoTypeISA: assert( (generic_regnum == LLDB_REGNUM_GENERIC_PC || generic_regnum == LLDB_REGNUM_GENERIC_FLAGS) && "eInfoTypeISA used for poping a register other the the PC/FLAGS"); if (generic_regnum != LLDB_REGNUM_GENERIC_FLAGS) { m_curr_row->SetRegisterLocationToSame(reg_num, false /*must_replace*/); m_curr_row_modified = true; } break; default: assert(false && "unhandled case, add code to handle this!"); break; } } } break; case EmulateInstruction::eContextSetFramePointer: if (!m_fp_is_cfa) { m_fp_is_cfa = true; m_cfa_reg_info = *reg_info; const uint32_t cfa_reg_num = reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; assert(cfa_reg_num != LLDB_INVALID_REGNUM); m_curr_row->GetCFAValue().SetIsRegisterPlusOffset( cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64()); m_curr_row_modified = true; } break; case EmulateInstruction::eContextRestoreStackPointer: if (m_fp_is_cfa) { m_fp_is_cfa = false; m_cfa_reg_info = *reg_info; const uint32_t cfa_reg_num = reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; assert(cfa_reg_num != LLDB_INVALID_REGNUM); m_curr_row->GetCFAValue().SetIsRegisterPlusOffset( cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64()); m_curr_row_modified = true; } break; case EmulateInstruction::eContextAdjustStackPointer: // If we have created a frame using the frame pointer, don't follow // subsequent adjustments to the stack pointer. if (!m_fp_is_cfa) { m_curr_row->GetCFAValue().SetIsRegisterPlusOffset( m_curr_row->GetCFAValue().GetRegisterNumber(), m_initial_sp - reg_value.GetAsUInt64()); m_curr_row_modified = true; } break; } return true; } Index: vendor/lldb/dist/source/Symbol/ClangASTContext.cpp =================================================================== --- vendor/lldb/dist/source/Symbol/ClangASTContext.cpp (revision 311541) +++ vendor/lldb/dist/source/Symbol/ClangASTContext.cpp (revision 311542) @@ -1,10094 +1,10093 @@ //===-- ClangASTContext.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/ClangASTContext.h" #include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatVariadic.h" // C Includes // C++ Includes #include // std::once #include #include // Other libraries and framework includes // Clang headers like to use NDEBUG inside of them to enable/disable debug // related features using "#ifndef NDEBUG" preprocessor blocks to do one thing // or another. This is bad because it means that if clang was built in release // mode, it assumes that you are building in release mode which is not always // the case. You can end up with functions that are defined as empty in header // files when NDEBUG is not defined, and this can cause link errors with the // clang .a files that you have since you might be missing functions in the .a // file. So we have to define NDEBUG when including clang headers to avoid any // mismatches. This is covered by rdar://problem/8691220 #if !defined(NDEBUG) && !defined(LLVM_NDEBUG_OFF) #define LLDB_DEFINED_NDEBUG_FOR_CLANG #define NDEBUG // Need to include assert.h so it is as clang would expect it to be (disabled) #include #endif #include "clang/AST/ASTContext.h" #include "clang/AST/ASTImporter.h" #include "clang/AST/Attr.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Mangle.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/Type.h" #include "clang/AST/VTableBuilder.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" #include "clang/Frontend/FrontendOptions.h" #include "clang/Frontend/LangStandard.h" #ifdef LLDB_DEFINED_NDEBUG_FOR_CLANG #undef NDEBUG #undef LLDB_DEFINED_NDEBUG_FOR_CLANG // Need to re-include assert.h so it is as _we_ would expect it to be (enabled) #include #endif #include "llvm/Support/Signals.h" #include "Plugins/ExpressionParser/Clang/ClangFunctionCaller.h" #include "Plugins/ExpressionParser/Clang/ClangUserExpression.h" #include "Plugins/ExpressionParser/Clang/ClangUtilityFunction.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Flags.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/ThreadSafeDenseMap.h" #include "lldb/Core/UniqueCStringMap.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangASTImporter.h" #include "lldb/Symbol/ClangExternalASTSourceCallbacks.h" #include "lldb/Symbol/ClangExternalASTSourceCommon.h" #include "lldb/Symbol/ClangUtil.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/VerifyDecl.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Language.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/LLDBAssert.h" #include "Plugins/SymbolFile/DWARF/DWARFASTParserClang.h" #include "Plugins/SymbolFile/PDB/PDBASTParser.h" #include #include using namespace lldb; using namespace lldb_private; using namespace llvm; using namespace clang; namespace { static inline bool ClangASTContextSupportsLanguage(lldb::LanguageType language) { return language == eLanguageTypeUnknown || // Clang is the default type system Language::LanguageIsC(language) || Language::LanguageIsCPlusPlus(language) || Language::LanguageIsObjC(language) || Language::LanguageIsPascal(language) || // Use Clang for Rust until there is a proper language plugin for it language == eLanguageTypeRust || language == eLanguageTypeExtRenderScript || // Use Clang for D until there is a proper language plugin for it language == eLanguageTypeD; } } typedef lldb_private::ThreadSafeDenseMap ClangASTMap; static ClangASTMap &GetASTMap() { static ClangASTMap *g_map_ptr = nullptr; static std::once_flag g_once_flag; std::call_once(g_once_flag, []() { g_map_ptr = new ClangASTMap(); // leaked on purpose to avoid spins }); return *g_map_ptr; } static bool IsOperator(const char *name, clang::OverloadedOperatorKind &op_kind) { if (name == nullptr || name[0] == '\0') return false; #define OPERATOR_PREFIX "operator" #define OPERATOR_PREFIX_LENGTH (sizeof(OPERATOR_PREFIX) - 1) const char *post_op_name = nullptr; bool no_space = true; if (::strncmp(name, OPERATOR_PREFIX, OPERATOR_PREFIX_LENGTH)) return false; post_op_name = name + OPERATOR_PREFIX_LENGTH; if (post_op_name[0] == ' ') { post_op_name++; no_space = false; } #undef OPERATOR_PREFIX #undef OPERATOR_PREFIX_LENGTH // This is an operator, set the overloaded operator kind to invalid // in case this is a conversion operator... op_kind = clang::NUM_OVERLOADED_OPERATORS; switch (post_op_name[0]) { default: if (no_space) return false; break; case 'n': if (no_space) return false; if (strcmp(post_op_name, "new") == 0) op_kind = clang::OO_New; else if (strcmp(post_op_name, "new[]") == 0) op_kind = clang::OO_Array_New; break; case 'd': if (no_space) return false; if (strcmp(post_op_name, "delete") == 0) op_kind = clang::OO_Delete; else if (strcmp(post_op_name, "delete[]") == 0) op_kind = clang::OO_Array_Delete; break; case '+': if (post_op_name[1] == '\0') op_kind = clang::OO_Plus; else if (post_op_name[2] == '\0') { if (post_op_name[1] == '=') op_kind = clang::OO_PlusEqual; else if (post_op_name[1] == '+') op_kind = clang::OO_PlusPlus; } break; case '-': if (post_op_name[1] == '\0') op_kind = clang::OO_Minus; else if (post_op_name[2] == '\0') { switch (post_op_name[1]) { case '=': op_kind = clang::OO_MinusEqual; break; case '-': op_kind = clang::OO_MinusMinus; break; case '>': op_kind = clang::OO_Arrow; break; } } else if (post_op_name[3] == '\0') { if (post_op_name[2] == '*') op_kind = clang::OO_ArrowStar; break; } break; case '*': if (post_op_name[1] == '\0') op_kind = clang::OO_Star; else if (post_op_name[1] == '=' && post_op_name[2] == '\0') op_kind = clang::OO_StarEqual; break; case '/': if (post_op_name[1] == '\0') op_kind = clang::OO_Slash; else if (post_op_name[1] == '=' && post_op_name[2] == '\0') op_kind = clang::OO_SlashEqual; break; case '%': if (post_op_name[1] == '\0') op_kind = clang::OO_Percent; else if (post_op_name[1] == '=' && post_op_name[2] == '\0') op_kind = clang::OO_PercentEqual; break; case '^': if (post_op_name[1] == '\0') op_kind = clang::OO_Caret; else if (post_op_name[1] == '=' && post_op_name[2] == '\0') op_kind = clang::OO_CaretEqual; break; case '&': if (post_op_name[1] == '\0') op_kind = clang::OO_Amp; else if (post_op_name[2] == '\0') { switch (post_op_name[1]) { case '=': op_kind = clang::OO_AmpEqual; break; case '&': op_kind = clang::OO_AmpAmp; break; } } break; case '|': if (post_op_name[1] == '\0') op_kind = clang::OO_Pipe; else if (post_op_name[2] == '\0') { switch (post_op_name[1]) { case '=': op_kind = clang::OO_PipeEqual; break; case '|': op_kind = clang::OO_PipePipe; break; } } break; case '~': if (post_op_name[1] == '\0') op_kind = clang::OO_Tilde; break; case '!': if (post_op_name[1] == '\0') op_kind = clang::OO_Exclaim; else if (post_op_name[1] == '=' && post_op_name[2] == '\0') op_kind = clang::OO_ExclaimEqual; break; case '=': if (post_op_name[1] == '\0') op_kind = clang::OO_Equal; else if (post_op_name[1] == '=' && post_op_name[2] == '\0') op_kind = clang::OO_EqualEqual; break; case '<': if (post_op_name[1] == '\0') op_kind = clang::OO_Less; else if (post_op_name[2] == '\0') { switch (post_op_name[1]) { case '<': op_kind = clang::OO_LessLess; break; case '=': op_kind = clang::OO_LessEqual; break; } } else if (post_op_name[3] == '\0') { if (post_op_name[2] == '=') op_kind = clang::OO_LessLessEqual; } break; case '>': if (post_op_name[1] == '\0') op_kind = clang::OO_Greater; else if (post_op_name[2] == '\0') { switch (post_op_name[1]) { case '>': op_kind = clang::OO_GreaterGreater; break; case '=': op_kind = clang::OO_GreaterEqual; break; } } else if (post_op_name[1] == '>' && post_op_name[2] == '=' && post_op_name[3] == '\0') { op_kind = clang::OO_GreaterGreaterEqual; } break; case ',': if (post_op_name[1] == '\0') op_kind = clang::OO_Comma; break; case '(': if (post_op_name[1] == ')' && post_op_name[2] == '\0') op_kind = clang::OO_Call; break; case '[': if (post_op_name[1] == ']' && post_op_name[2] == '\0') op_kind = clang::OO_Subscript; break; } return true; } clang::AccessSpecifier ClangASTContext::ConvertAccessTypeToAccessSpecifier(AccessType access) { switch (access) { default: break; case eAccessNone: return AS_none; case eAccessPublic: return AS_public; case eAccessPrivate: return AS_private; case eAccessProtected: return AS_protected; } return AS_none; } static void ParseLangArgs(LangOptions &Opts, InputKind IK, const char *triple) { // FIXME: Cleanup per-file based stuff. // Set some properties which depend solely on the input kind; it would be nice // to move these to the language standard, and have the driver resolve the // input kind + language standard. if (IK == IK_Asm) { Opts.AsmPreprocessor = 1; } else if (IK == IK_ObjC || IK == IK_ObjCXX || IK == IK_PreprocessedObjC || IK == IK_PreprocessedObjCXX) { Opts.ObjC1 = Opts.ObjC2 = 1; } LangStandard::Kind LangStd = LangStandard::lang_unspecified; if (LangStd == LangStandard::lang_unspecified) { // Based on the base language, pick one. switch (IK) { case IK_None: case IK_AST: case IK_LLVM_IR: case IK_RenderScript: - assert(!"Invalid input kind!"); + llvm_unreachable("Invalid input kind!"); case IK_OpenCL: LangStd = LangStandard::lang_opencl; break; case IK_CUDA: case IK_PreprocessedCuda: LangStd = LangStandard::lang_cuda; break; case IK_Asm: case IK_C: case IK_PreprocessedC: case IK_ObjC: case IK_PreprocessedObjC: LangStd = LangStandard::lang_gnu99; break; case IK_CXX: case IK_PreprocessedCXX: case IK_ObjCXX: case IK_PreprocessedObjCXX: LangStd = LangStandard::lang_gnucxx98; break; } } const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); Opts.LineComment = Std.hasLineComments(); Opts.C99 = Std.isC99(); Opts.CPlusPlus = Std.isCPlusPlus(); Opts.CPlusPlus11 = Std.isCPlusPlus11(); Opts.Digraphs = Std.hasDigraphs(); Opts.GNUMode = Std.isGNUMode(); Opts.GNUInline = !Std.isC99(); Opts.HexFloats = Std.hasHexFloats(); Opts.ImplicitInt = Std.hasImplicitInt(); Opts.WChar = true; // OpenCL has some additional defaults. if (LangStd == LangStandard::lang_opencl) { Opts.OpenCL = 1; Opts.AltiVec = 1; Opts.CXXOperatorNames = 1; Opts.LaxVectorConversions = 1; } // OpenCL and C++ both have bool, true, false keywords. Opts.Bool = Opts.OpenCL || Opts.CPlusPlus; // if (Opts.CPlusPlus) // Opts.CXXOperatorNames = !Args.hasArg(OPT_fno_operator_names); // // if (Args.hasArg(OPT_fobjc_gc_only)) // Opts.setGCMode(LangOptions::GCOnly); // else if (Args.hasArg(OPT_fobjc_gc)) // Opts.setGCMode(LangOptions::HybridGC); // // if (Args.hasArg(OPT_print_ivar_layout)) // Opts.ObjCGCBitmapPrint = 1; // // if (Args.hasArg(OPT_faltivec)) // Opts.AltiVec = 1; // // if (Args.hasArg(OPT_pthread)) // Opts.POSIXThreads = 1; // // llvm::StringRef Vis = getLastArgValue(Args, OPT_fvisibility, // "default"); // if (Vis == "default") Opts.setValueVisibilityMode(DefaultVisibility); // else if (Vis == "hidden") // Opts.setVisibilityMode(LangOptions::Hidden); // else if (Vis == "protected") // Opts.setVisibilityMode(LangOptions::Protected); // else // Diags.Report(diag::err_drv_invalid_value) // << Args.getLastArg(OPT_fvisibility)->getAsString(Args) << Vis; // Opts.OverflowChecking = Args.hasArg(OPT_ftrapv); // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs // is specified, or -std is set to a conforming mode. Opts.Trigraphs = !Opts.GNUMode; // if (Args.hasArg(OPT_trigraphs)) // Opts.Trigraphs = 1; // // Opts.DollarIdents = Args.hasFlag(OPT_fdollars_in_identifiers, // OPT_fno_dollars_in_identifiers, // !Opts.AsmPreprocessor); // Opts.PascalStrings = Args.hasArg(OPT_fpascal_strings); // Opts.Microsoft = Args.hasArg(OPT_fms_extensions); // Opts.WritableStrings = Args.hasArg(OPT_fwritable_strings); // if (Args.hasArg(OPT_fno_lax_vector_conversions)) // Opts.LaxVectorConversions = 0; // Opts.Exceptions = Args.hasArg(OPT_fexceptions); // Opts.RTTI = !Args.hasArg(OPT_fno_rtti); // Opts.Blocks = Args.hasArg(OPT_fblocks); Opts.CharIsSigned = ArchSpec(triple).CharIsSignedByDefault(); // Opts.ShortWChar = Args.hasArg(OPT_fshort_wchar); // Opts.Freestanding = Args.hasArg(OPT_ffreestanding); // Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding; // Opts.AssumeSaneOperatorNew = // !Args.hasArg(OPT_fno_assume_sane_operator_new); // Opts.HeinousExtensions = Args.hasArg(OPT_fheinous_gnu_extensions); // Opts.AccessControl = Args.hasArg(OPT_faccess_control); // Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors); // Opts.MathErrno = !Args.hasArg(OPT_fno_math_errno); // Opts.InstantiationDepth = getLastArgIntValue(Args, OPT_ftemplate_depth, // 99, // Diags); // Opts.NeXTRuntime = !Args.hasArg(OPT_fgnu_runtime); // Opts.ObjCConstantStringClass = getLastArgValue(Args, // OPT_fconstant_string_class); // Opts.ObjCNonFragileABI = Args.hasArg(OPT_fobjc_nonfragile_abi); // Opts.CatchUndefined = Args.hasArg(OPT_fcatch_undefined_behavior); // Opts.EmitAllDecls = Args.hasArg(OPT_femit_all_decls); // Opts.PICLevel = getLastArgIntValue(Args, OPT_pic_level, 0, Diags); // Opts.Static = Args.hasArg(OPT_static_define); Opts.OptimizeSize = 0; // FIXME: Eliminate this dependency. // unsigned Opt = // Args.hasArg(OPT_Os) ? 2 : getLastArgIntValue(Args, OPT_O, 0, Diags); // Opts.Optimize = Opt != 0; unsigned Opt = 0; // This is the __NO_INLINE__ define, which just depends on things like the // optimization level and -fno-inline, not actually whether the backend has // inlining enabled. // // FIXME: This is affected by other options (-fno-inline). Opts.NoInlineDefine = !Opt; // unsigned SSP = getLastArgIntValue(Args, OPT_stack_protector, 0, Diags); // switch (SSP) { // default: // Diags.Report(diag::err_drv_invalid_value) // << Args.getLastArg(OPT_stack_protector)->getAsString(Args) << // SSP; // break; // case 0: Opts.setStackProtectorMode(LangOptions::SSPOff); break; // case 1: Opts.setStackProtectorMode(LangOptions::SSPOn); break; // case 2: Opts.setStackProtectorMode(LangOptions::SSPReq); break; // } } ClangASTContext::ClangASTContext(const char *target_triple) : TypeSystem(TypeSystem::eKindClang), m_target_triple(), m_ast_ap(), m_language_options_ap(), m_source_manager_ap(), m_diagnostics_engine_ap(), m_target_options_rp(), m_target_info_ap(), m_identifier_table_ap(), m_selector_table_ap(), m_builtins_ap(), m_callback_tag_decl(nullptr), m_callback_objc_decl(nullptr), m_callback_baton(nullptr), m_pointer_byte_size(0), m_ast_owned(false) { if (target_triple && target_triple[0]) SetTargetTriple(target_triple); } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- ClangASTContext::~ClangASTContext() { Finalize(); } ConstString ClangASTContext::GetPluginNameStatic() { return ConstString("clang"); } ConstString ClangASTContext::GetPluginName() { return ClangASTContext::GetPluginNameStatic(); } uint32_t ClangASTContext::GetPluginVersion() { return 1; } lldb::TypeSystemSP ClangASTContext::CreateInstance(lldb::LanguageType language, lldb_private::Module *module, Target *target) { if (ClangASTContextSupportsLanguage(language)) { ArchSpec arch; if (module) arch = module->GetArchitecture(); else if (target) arch = target->GetArchitecture(); if (arch.IsValid()) { ArchSpec fixed_arch = arch; // LLVM wants this to be set to iOS or MacOSX; if we're working on // a bare-boards type image, change the triple for llvm's benefit. if (fixed_arch.GetTriple().getVendor() == llvm::Triple::Apple && fixed_arch.GetTriple().getOS() == llvm::Triple::UnknownOS) { if (fixed_arch.GetTriple().getArch() == llvm::Triple::arm || fixed_arch.GetTriple().getArch() == llvm::Triple::aarch64 || fixed_arch.GetTriple().getArch() == llvm::Triple::thumb) { fixed_arch.GetTriple().setOS(llvm::Triple::IOS); } else { fixed_arch.GetTriple().setOS(llvm::Triple::MacOSX); } } if (module) { std::shared_ptr ast_sp(new ClangASTContext); if (ast_sp) { ast_sp->SetArchitecture(fixed_arch); } return ast_sp; } else if (target && target->IsValid()) { std::shared_ptr ast_sp( new ClangASTContextForExpressions(*target)); if (ast_sp) { ast_sp->SetArchitecture(fixed_arch); ast_sp->m_scratch_ast_source_ap.reset( new ClangASTSource(target->shared_from_this())); ast_sp->m_scratch_ast_source_ap->InstallASTContext( ast_sp->getASTContext()); llvm::IntrusiveRefCntPtr proxy_ast_source( ast_sp->m_scratch_ast_source_ap->CreateProxy()); ast_sp->SetExternalSource(proxy_ast_source); return ast_sp; } } } } return lldb::TypeSystemSP(); } void ClangASTContext::EnumerateSupportedLanguages( std::set &languages_for_types, std::set &languages_for_expressions) { static std::vector s_supported_languages_for_types( {lldb::eLanguageTypeC89, lldb::eLanguageTypeC, lldb::eLanguageTypeC11, lldb::eLanguageTypeC_plus_plus, lldb::eLanguageTypeC99, lldb::eLanguageTypeObjC, lldb::eLanguageTypeObjC_plus_plus, lldb::eLanguageTypeC_plus_plus_03, lldb::eLanguageTypeC_plus_plus_11, lldb::eLanguageTypeC11, lldb::eLanguageTypeC_plus_plus_14}); static std::vector s_supported_languages_for_expressions( {lldb::eLanguageTypeC_plus_plus, lldb::eLanguageTypeObjC_plus_plus, lldb::eLanguageTypeC_plus_plus_03, lldb::eLanguageTypeC_plus_plus_11, lldb::eLanguageTypeC_plus_plus_14}); languages_for_types.insert(s_supported_languages_for_types.begin(), s_supported_languages_for_types.end()); languages_for_expressions.insert( s_supported_languages_for_expressions.begin(), s_supported_languages_for_expressions.end()); } void ClangASTContext::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), "clang base AST context plug-in", CreateInstance, EnumerateSupportedLanguages); } void ClangASTContext::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } void ClangASTContext::Finalize() { if (m_ast_ap.get()) { GetASTMap().Erase(m_ast_ap.get()); if (!m_ast_owned) m_ast_ap.release(); } m_builtins_ap.reset(); m_selector_table_ap.reset(); m_identifier_table_ap.reset(); m_target_info_ap.reset(); m_target_options_rp.reset(); m_diagnostics_engine_ap.reset(); m_source_manager_ap.reset(); m_language_options_ap.reset(); m_ast_ap.reset(); m_scratch_ast_source_ap.reset(); } void ClangASTContext::Clear() { m_ast_ap.reset(); m_language_options_ap.reset(); m_source_manager_ap.reset(); m_diagnostics_engine_ap.reset(); m_target_options_rp.reset(); m_target_info_ap.reset(); m_identifier_table_ap.reset(); m_selector_table_ap.reset(); m_builtins_ap.reset(); m_pointer_byte_size = 0; } const char *ClangASTContext::GetTargetTriple() { return m_target_triple.c_str(); } void ClangASTContext::SetTargetTriple(const char *target_triple) { Clear(); m_target_triple.assign(target_triple); } void ClangASTContext::SetArchitecture(const ArchSpec &arch) { SetTargetTriple(arch.GetTriple().str().c_str()); } bool ClangASTContext::HasExternalSource() { ASTContext *ast = getASTContext(); if (ast) return ast->getExternalSource() != nullptr; return false; } void ClangASTContext::SetExternalSource( llvm::IntrusiveRefCntPtr &ast_source_ap) { ASTContext *ast = getASTContext(); if (ast) { ast->setExternalSource(ast_source_ap); ast->getTranslationUnitDecl()->setHasExternalLexicalStorage(true); // ast->getTranslationUnitDecl()->setHasExternalVisibleStorage(true); } } void ClangASTContext::RemoveExternalSource() { ASTContext *ast = getASTContext(); if (ast) { llvm::IntrusiveRefCntPtr empty_ast_source_ap; ast->setExternalSource(empty_ast_source_ap); ast->getTranslationUnitDecl()->setHasExternalLexicalStorage(false); // ast->getTranslationUnitDecl()->setHasExternalVisibleStorage(false); } } void ClangASTContext::setASTContext(clang::ASTContext *ast_ctx) { if (!m_ast_owned) { m_ast_ap.release(); } m_ast_owned = false; m_ast_ap.reset(ast_ctx); GetASTMap().Insert(ast_ctx, this); } ASTContext *ClangASTContext::getASTContext() { if (m_ast_ap.get() == nullptr) { m_ast_owned = true; m_ast_ap.reset(new ASTContext(*getLanguageOptions(), *getSourceManager(), *getIdentifierTable(), *getSelectorTable(), *getBuiltinContext())); m_ast_ap->getDiagnostics().setClient(getDiagnosticConsumer(), false); // This can be NULL if we don't know anything about the architecture or if // the // target for an architecture isn't enabled in the llvm/clang that we built TargetInfo *target_info = getTargetInfo(); if (target_info) m_ast_ap->InitBuiltinTypes(*target_info); if ((m_callback_tag_decl || m_callback_objc_decl) && m_callback_baton) { m_ast_ap->getTranslationUnitDecl()->setHasExternalLexicalStorage(); // m_ast_ap->getTranslationUnitDecl()->setHasExternalVisibleStorage(); } GetASTMap().Insert(m_ast_ap.get(), this); llvm::IntrusiveRefCntPtr ast_source_ap( new ClangExternalASTSourceCallbacks( ClangASTContext::CompleteTagDecl, ClangASTContext::CompleteObjCInterfaceDecl, nullptr, ClangASTContext::LayoutRecordType, this)); SetExternalSource(ast_source_ap); } return m_ast_ap.get(); } ClangASTContext *ClangASTContext::GetASTContext(clang::ASTContext *ast) { ClangASTContext *clang_ast = GetASTMap().Lookup(ast); return clang_ast; } Builtin::Context *ClangASTContext::getBuiltinContext() { if (m_builtins_ap.get() == nullptr) m_builtins_ap.reset(new Builtin::Context()); return m_builtins_ap.get(); } IdentifierTable *ClangASTContext::getIdentifierTable() { if (m_identifier_table_ap.get() == nullptr) m_identifier_table_ap.reset( new IdentifierTable(*ClangASTContext::getLanguageOptions(), nullptr)); return m_identifier_table_ap.get(); } LangOptions *ClangASTContext::getLanguageOptions() { if (m_language_options_ap.get() == nullptr) { m_language_options_ap.reset(new LangOptions()); ParseLangArgs(*m_language_options_ap, IK_ObjCXX, GetTargetTriple()); // InitializeLangOptions(*m_language_options_ap, IK_ObjCXX); } return m_language_options_ap.get(); } SelectorTable *ClangASTContext::getSelectorTable() { if (m_selector_table_ap.get() == nullptr) m_selector_table_ap.reset(new SelectorTable()); return m_selector_table_ap.get(); } clang::FileManager *ClangASTContext::getFileManager() { if (m_file_manager_ap.get() == nullptr) { clang::FileSystemOptions file_system_options; m_file_manager_ap.reset(new clang::FileManager(file_system_options)); } return m_file_manager_ap.get(); } clang::SourceManager *ClangASTContext::getSourceManager() { if (m_source_manager_ap.get() == nullptr) m_source_manager_ap.reset( new clang::SourceManager(*getDiagnosticsEngine(), *getFileManager())); return m_source_manager_ap.get(); } clang::DiagnosticsEngine *ClangASTContext::getDiagnosticsEngine() { if (m_diagnostics_engine_ap.get() == nullptr) { llvm::IntrusiveRefCntPtr diag_id_sp(new DiagnosticIDs()); m_diagnostics_engine_ap.reset( new DiagnosticsEngine(diag_id_sp, new DiagnosticOptions())); } return m_diagnostics_engine_ap.get(); } clang::MangleContext *ClangASTContext::getMangleContext() { if (m_mangle_ctx_ap.get() == nullptr) m_mangle_ctx_ap.reset(getASTContext()->createMangleContext()); return m_mangle_ctx_ap.get(); } class NullDiagnosticConsumer : public DiagnosticConsumer { public: NullDiagnosticConsumer() { m_log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS); } void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &info) { if (m_log) { llvm::SmallVector diag_str(10); info.FormatDiagnostic(diag_str); diag_str.push_back('\0'); m_log->Printf("Compiler diagnostic: %s\n", diag_str.data()); } } DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const { return new NullDiagnosticConsumer(); } private: Log *m_log; }; DiagnosticConsumer *ClangASTContext::getDiagnosticConsumer() { if (m_diagnostic_consumer_ap.get() == nullptr) m_diagnostic_consumer_ap.reset(new NullDiagnosticConsumer); return m_diagnostic_consumer_ap.get(); } std::shared_ptr &ClangASTContext::getTargetOptions() { if (m_target_options_rp.get() == nullptr && !m_target_triple.empty()) { m_target_options_rp = std::make_shared(); if (m_target_options_rp.get() != nullptr) m_target_options_rp->Triple = m_target_triple; } return m_target_options_rp; } TargetInfo *ClangASTContext::getTargetInfo() { // target_triple should be something like "x86_64-apple-macosx" if (m_target_info_ap.get() == nullptr && !m_target_triple.empty()) m_target_info_ap.reset(TargetInfo::CreateTargetInfo(*getDiagnosticsEngine(), getTargetOptions())); return m_target_info_ap.get(); } #pragma mark Basic Types static inline bool QualTypeMatchesBitSize(const uint64_t bit_size, ASTContext *ast, QualType qual_type) { uint64_t qual_type_bit_size = ast->getTypeSize(qual_type); if (qual_type_bit_size == bit_size) return true; return false; } CompilerType ClangASTContext::GetBuiltinTypeForEncodingAndBitSize(Encoding encoding, size_t bit_size) { return ClangASTContext::GetBuiltinTypeForEncodingAndBitSize( getASTContext(), encoding, bit_size); } CompilerType ClangASTContext::GetBuiltinTypeForEncodingAndBitSize( ASTContext *ast, Encoding encoding, uint32_t bit_size) { if (!ast) return CompilerType(); switch (encoding) { case eEncodingInvalid: if (QualTypeMatchesBitSize(bit_size, ast, ast->VoidPtrTy)) return CompilerType(ast, ast->VoidPtrTy); break; case eEncodingUint: if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy)) return CompilerType(ast, ast->UnsignedCharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy)) return CompilerType(ast, ast->UnsignedShortTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedIntTy)) return CompilerType(ast, ast->UnsignedIntTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongTy)) return CompilerType(ast, ast->UnsignedLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongLongTy)) return CompilerType(ast, ast->UnsignedLongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedInt128Ty)) return CompilerType(ast, ast->UnsignedInt128Ty); break; case eEncodingSint: if (QualTypeMatchesBitSize(bit_size, ast, ast->SignedCharTy)) return CompilerType(ast, ast->SignedCharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->ShortTy)) return CompilerType(ast, ast->ShortTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->IntTy)) return CompilerType(ast, ast->IntTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->LongTy)) return CompilerType(ast, ast->LongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->LongLongTy)) return CompilerType(ast, ast->LongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->Int128Ty)) return CompilerType(ast, ast->Int128Ty); break; case eEncodingIEEE754: if (QualTypeMatchesBitSize(bit_size, ast, ast->FloatTy)) return CompilerType(ast, ast->FloatTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->DoubleTy)) return CompilerType(ast, ast->DoubleTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->LongDoubleTy)) return CompilerType(ast, ast->LongDoubleTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->HalfTy)) return CompilerType(ast, ast->HalfTy); break; case eEncodingVector: // Sanity check that bit_size is a multiple of 8's. if (bit_size && !(bit_size & 0x7u)) return CompilerType( ast, ast->getExtVectorType(ast->UnsignedCharTy, bit_size / 8)); break; } return CompilerType(); } lldb::BasicType ClangASTContext::GetBasicTypeEnumeration(const ConstString &name) { if (name) { typedef UniqueCStringMap TypeNameToBasicTypeMap; static TypeNameToBasicTypeMap g_type_map; static std::once_flag g_once_flag; std::call_once(g_once_flag, []() { // "void" g_type_map.Append(ConstString("void").GetStringRef(), eBasicTypeVoid); // "char" g_type_map.Append(ConstString("char").GetStringRef(), eBasicTypeChar); g_type_map.Append(ConstString("signed char").GetStringRef(), eBasicTypeSignedChar); g_type_map.Append(ConstString("unsigned char").GetStringRef(), eBasicTypeUnsignedChar); g_type_map.Append(ConstString("wchar_t").GetStringRef(), eBasicTypeWChar); g_type_map.Append(ConstString("signed wchar_t").GetStringRef(), eBasicTypeSignedWChar); g_type_map.Append(ConstString("unsigned wchar_t").GetStringRef(), eBasicTypeUnsignedWChar); // "short" g_type_map.Append(ConstString("short").GetStringRef(), eBasicTypeShort); g_type_map.Append(ConstString("short int").GetStringRef(), eBasicTypeShort); g_type_map.Append(ConstString("unsigned short").GetStringRef(), eBasicTypeUnsignedShort); g_type_map.Append(ConstString("unsigned short int").GetStringRef(), eBasicTypeUnsignedShort); // "int" g_type_map.Append(ConstString("int").GetStringRef(), eBasicTypeInt); g_type_map.Append(ConstString("signed int").GetStringRef(), eBasicTypeInt); g_type_map.Append(ConstString("unsigned int").GetStringRef(), eBasicTypeUnsignedInt); g_type_map.Append(ConstString("unsigned").GetStringRef(), eBasicTypeUnsignedInt); // "long" g_type_map.Append(ConstString("long").GetStringRef(), eBasicTypeLong); g_type_map.Append(ConstString("long int").GetStringRef(), eBasicTypeLong); g_type_map.Append(ConstString("unsigned long").GetStringRef(), eBasicTypeUnsignedLong); g_type_map.Append(ConstString("unsigned long int").GetStringRef(), eBasicTypeUnsignedLong); // "long long" g_type_map.Append(ConstString("long long").GetStringRef(), eBasicTypeLongLong); g_type_map.Append(ConstString("long long int").GetStringRef(), eBasicTypeLongLong); g_type_map.Append(ConstString("unsigned long long").GetStringRef(), eBasicTypeUnsignedLongLong); g_type_map.Append(ConstString("unsigned long long int").GetStringRef(), eBasicTypeUnsignedLongLong); // "int128" g_type_map.Append(ConstString("__int128_t").GetStringRef(), eBasicTypeInt128); g_type_map.Append(ConstString("__uint128_t").GetStringRef(), eBasicTypeUnsignedInt128); // Miscellaneous g_type_map.Append(ConstString("bool").GetStringRef(), eBasicTypeBool); g_type_map.Append(ConstString("float").GetStringRef(), eBasicTypeFloat); g_type_map.Append(ConstString("double").GetStringRef(), eBasicTypeDouble); g_type_map.Append(ConstString("long double").GetStringRef(), eBasicTypeLongDouble); g_type_map.Append(ConstString("id").GetStringRef(), eBasicTypeObjCID); g_type_map.Append(ConstString("SEL").GetStringRef(), eBasicTypeObjCSel); g_type_map.Append(ConstString("nullptr").GetStringRef(), eBasicTypeNullPtr); g_type_map.Sort(); }); return g_type_map.Find(name.GetStringRef(), eBasicTypeInvalid); } return eBasicTypeInvalid; } CompilerType ClangASTContext::GetBasicType(ASTContext *ast, const ConstString &name) { if (ast) { lldb::BasicType basic_type = ClangASTContext::GetBasicTypeEnumeration(name); return ClangASTContext::GetBasicType(ast, basic_type); } return CompilerType(); } uint32_t ClangASTContext::GetPointerByteSize() { if (m_pointer_byte_size == 0) m_pointer_byte_size = GetBasicType(lldb::eBasicTypeVoid) .GetPointerType() .GetByteSize(nullptr); return m_pointer_byte_size; } CompilerType ClangASTContext::GetBasicType(lldb::BasicType basic_type) { return GetBasicType(getASTContext(), basic_type); } CompilerType ClangASTContext::GetBasicType(ASTContext *ast, lldb::BasicType basic_type) { if (!ast) return CompilerType(); lldb::opaque_compiler_type_t clang_type = GetOpaqueCompilerType(ast, basic_type); if (clang_type) return CompilerType(GetASTContext(ast), clang_type); return CompilerType(); } CompilerType ClangASTContext::GetBuiltinTypeForDWARFEncodingAndBitSize( const char *type_name, uint32_t dw_ate, uint32_t bit_size) { ASTContext *ast = getASTContext(); #define streq(a, b) strcmp(a, b) == 0 assert(ast != nullptr); if (ast) { switch (dw_ate) { default: break; case DW_ATE_address: if (QualTypeMatchesBitSize(bit_size, ast, ast->VoidPtrTy)) return CompilerType(ast, ast->VoidPtrTy); break; case DW_ATE_boolean: if (QualTypeMatchesBitSize(bit_size, ast, ast->BoolTy)) return CompilerType(ast, ast->BoolTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy)) return CompilerType(ast, ast->UnsignedCharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy)) return CompilerType(ast, ast->UnsignedShortTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedIntTy)) return CompilerType(ast, ast->UnsignedIntTy); break; case DW_ATE_lo_user: // This has been seen to mean DW_AT_complex_integer if (type_name) { if (::strstr(type_name, "complex")) { CompilerType complex_int_clang_type = GetBuiltinTypeForDWARFEncodingAndBitSize("int", DW_ATE_signed, bit_size / 2); return CompilerType(ast, ast->getComplexType(ClangUtil::GetQualType( complex_int_clang_type))); } } break; case DW_ATE_complex_float: if (QualTypeMatchesBitSize(bit_size, ast, ast->FloatComplexTy)) return CompilerType(ast, ast->FloatComplexTy); else if (QualTypeMatchesBitSize(bit_size, ast, ast->DoubleComplexTy)) return CompilerType(ast, ast->DoubleComplexTy); else if (QualTypeMatchesBitSize(bit_size, ast, ast->LongDoubleComplexTy)) return CompilerType(ast, ast->LongDoubleComplexTy); else { CompilerType complex_float_clang_type = GetBuiltinTypeForDWARFEncodingAndBitSize("float", DW_ATE_float, bit_size / 2); return CompilerType(ast, ast->getComplexType(ClangUtil::GetQualType( complex_float_clang_type))); } break; case DW_ATE_float: if (streq(type_name, "float") && QualTypeMatchesBitSize(bit_size, ast, ast->FloatTy)) return CompilerType(ast, ast->FloatTy); if (streq(type_name, "double") && QualTypeMatchesBitSize(bit_size, ast, ast->DoubleTy)) return CompilerType(ast, ast->DoubleTy); if (streq(type_name, "long double") && QualTypeMatchesBitSize(bit_size, ast, ast->LongDoubleTy)) return CompilerType(ast, ast->LongDoubleTy); // Fall back to not requiring a name match if (QualTypeMatchesBitSize(bit_size, ast, ast->FloatTy)) return CompilerType(ast, ast->FloatTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->DoubleTy)) return CompilerType(ast, ast->DoubleTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->LongDoubleTy)) return CompilerType(ast, ast->LongDoubleTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->HalfTy)) return CompilerType(ast, ast->HalfTy); break; case DW_ATE_signed: if (type_name) { if (streq(type_name, "wchar_t") && QualTypeMatchesBitSize(bit_size, ast, ast->WCharTy) && (getTargetInfo() && TargetInfo::isTypeSigned(getTargetInfo()->getWCharType()))) return CompilerType(ast, ast->WCharTy); if (streq(type_name, "void") && QualTypeMatchesBitSize(bit_size, ast, ast->VoidTy)) return CompilerType(ast, ast->VoidTy); if (strstr(type_name, "long long") && QualTypeMatchesBitSize(bit_size, ast, ast->LongLongTy)) return CompilerType(ast, ast->LongLongTy); if (strstr(type_name, "long") && QualTypeMatchesBitSize(bit_size, ast, ast->LongTy)) return CompilerType(ast, ast->LongTy); if (strstr(type_name, "short") && QualTypeMatchesBitSize(bit_size, ast, ast->ShortTy)) return CompilerType(ast, ast->ShortTy); if (strstr(type_name, "char")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->CharTy)) return CompilerType(ast, ast->CharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->SignedCharTy)) return CompilerType(ast, ast->SignedCharTy); } if (strstr(type_name, "int")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->IntTy)) return CompilerType(ast, ast->IntTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->Int128Ty)) return CompilerType(ast, ast->Int128Ty); } } // We weren't able to match up a type name, just search by size if (QualTypeMatchesBitSize(bit_size, ast, ast->CharTy)) return CompilerType(ast, ast->CharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->ShortTy)) return CompilerType(ast, ast->ShortTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->IntTy)) return CompilerType(ast, ast->IntTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->LongTy)) return CompilerType(ast, ast->LongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->LongLongTy)) return CompilerType(ast, ast->LongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->Int128Ty)) return CompilerType(ast, ast->Int128Ty); break; case DW_ATE_signed_char: if (ast->getLangOpts().CharIsSigned && type_name && streq(type_name, "char")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->CharTy)) return CompilerType(ast, ast->CharTy); } if (QualTypeMatchesBitSize(bit_size, ast, ast->SignedCharTy)) return CompilerType(ast, ast->SignedCharTy); break; case DW_ATE_unsigned: if (type_name) { if (streq(type_name, "wchar_t")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->WCharTy)) { if (!(getTargetInfo() && TargetInfo::isTypeSigned(getTargetInfo()->getWCharType()))) return CompilerType(ast, ast->WCharTy); } } if (strstr(type_name, "long long")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongLongTy)) return CompilerType(ast, ast->UnsignedLongLongTy); } else if (strstr(type_name, "long")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongTy)) return CompilerType(ast, ast->UnsignedLongTy); } else if (strstr(type_name, "short")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy)) return CompilerType(ast, ast->UnsignedShortTy); } else if (strstr(type_name, "char")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy)) return CompilerType(ast, ast->UnsignedCharTy); } else if (strstr(type_name, "int")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedIntTy)) return CompilerType(ast, ast->UnsignedIntTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedInt128Ty)) return CompilerType(ast, ast->UnsignedInt128Ty); } } // We weren't able to match up a type name, just search by size if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy)) return CompilerType(ast, ast->UnsignedCharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy)) return CompilerType(ast, ast->UnsignedShortTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedIntTy)) return CompilerType(ast, ast->UnsignedIntTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongTy)) return CompilerType(ast, ast->UnsignedLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongLongTy)) return CompilerType(ast, ast->UnsignedLongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedInt128Ty)) return CompilerType(ast, ast->UnsignedInt128Ty); break; case DW_ATE_unsigned_char: if (!ast->getLangOpts().CharIsSigned && type_name && streq(type_name, "char")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->CharTy)) return CompilerType(ast, ast->CharTy); } if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy)) return CompilerType(ast, ast->UnsignedCharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy)) return CompilerType(ast, ast->UnsignedShortTy); break; case DW_ATE_imaginary_float: break; case DW_ATE_UTF: if (type_name) { if (streq(type_name, "char16_t")) { return CompilerType(ast, ast->Char16Ty); } else if (streq(type_name, "char32_t")) { return CompilerType(ast, ast->Char32Ty); } } break; } } // This assert should fire for anything that we don't catch above so we know // to fix any issues we run into. if (type_name) { Host::SystemLog(Host::eSystemLogError, "error: need to add support for " "DW_TAG_base_type '%s' encoded with " "DW_ATE = 0x%x, bit_size = %u\n", type_name, dw_ate, bit_size); } else { Host::SystemLog(Host::eSystemLogError, "error: need to add support for " "DW_TAG_base_type encoded with " "DW_ATE = 0x%x, bit_size = %u\n", dw_ate, bit_size); } return CompilerType(); } CompilerType ClangASTContext::GetUnknownAnyType(clang::ASTContext *ast) { if (ast) return CompilerType(ast, ast->UnknownAnyTy); return CompilerType(); } CompilerType ClangASTContext::GetCStringType(bool is_const) { ASTContext *ast = getASTContext(); QualType char_type(ast->CharTy); if (is_const) char_type.addConst(); return CompilerType(ast, ast->getPointerType(char_type)); } clang::DeclContext * ClangASTContext::GetTranslationUnitDecl(clang::ASTContext *ast) { return ast->getTranslationUnitDecl(); } clang::Decl *ClangASTContext::CopyDecl(ASTContext *dst_ast, ASTContext *src_ast, clang::Decl *source_decl) { FileSystemOptions file_system_options; FileManager file_manager(file_system_options); ASTImporter importer(*dst_ast, file_manager, *src_ast, file_manager, false); return importer.Import(source_decl); } bool ClangASTContext::AreTypesSame(CompilerType type1, CompilerType type2, bool ignore_qualifiers) { ClangASTContext *ast = llvm::dyn_cast_or_null(type1.GetTypeSystem()); if (!ast || ast != type2.GetTypeSystem()) return false; if (type1.GetOpaqueQualType() == type2.GetOpaqueQualType()) return true; QualType type1_qual = ClangUtil::GetQualType(type1); QualType type2_qual = ClangUtil::GetQualType(type2); if (ignore_qualifiers) { type1_qual = type1_qual.getUnqualifiedType(); type2_qual = type2_qual.getUnqualifiedType(); } return ast->getASTContext()->hasSameType(type1_qual, type2_qual); } CompilerType ClangASTContext::GetTypeForDecl(clang::NamedDecl *decl) { if (clang::ObjCInterfaceDecl *interface_decl = llvm::dyn_cast(decl)) return GetTypeForDecl(interface_decl); if (clang::TagDecl *tag_decl = llvm::dyn_cast(decl)) return GetTypeForDecl(tag_decl); return CompilerType(); } CompilerType ClangASTContext::GetTypeForDecl(TagDecl *decl) { // No need to call the getASTContext() accessor (which can create the AST // if it isn't created yet, because we can't have created a decl in this // AST if our AST didn't already exist... ASTContext *ast = &decl->getASTContext(); if (ast) return CompilerType(ast, ast->getTagDeclType(decl)); return CompilerType(); } CompilerType ClangASTContext::GetTypeForDecl(ObjCInterfaceDecl *decl) { // No need to call the getASTContext() accessor (which can create the AST // if it isn't created yet, because we can't have created a decl in this // AST if our AST didn't already exist... ASTContext *ast = &decl->getASTContext(); if (ast) return CompilerType(ast, ast->getObjCInterfaceType(decl)); return CompilerType(); } #pragma mark Structure, Unions, Classes CompilerType ClangASTContext::CreateRecordType(DeclContext *decl_ctx, AccessType access_type, const char *name, int kind, LanguageType language, ClangASTMetadata *metadata) { ASTContext *ast = getASTContext(); assert(ast != nullptr); if (decl_ctx == nullptr) decl_ctx = ast->getTranslationUnitDecl(); if (language == eLanguageTypeObjC || language == eLanguageTypeObjC_plus_plus) { bool isForwardDecl = true; bool isInternal = false; return CreateObjCClass(name, decl_ctx, isForwardDecl, isInternal, metadata); } // NOTE: Eventually CXXRecordDecl will be merged back into RecordDecl and // we will need to update this code. I was told to currently always use // the CXXRecordDecl class since we often don't know from debug information // if something is struct or a class, so we default to always use the more // complete definition just in case. bool is_anonymous = (!name) || (!name[0]); CXXRecordDecl *decl = CXXRecordDecl::Create( *ast, (TagDecl::TagKind)kind, decl_ctx, SourceLocation(), SourceLocation(), is_anonymous ? nullptr : &ast->Idents.get(name)); if (is_anonymous) decl->setAnonymousStructOrUnion(true); if (decl) { if (metadata) SetMetadata(ast, decl, *metadata); if (access_type != eAccessNone) decl->setAccess(ConvertAccessTypeToAccessSpecifier(access_type)); if (decl_ctx) decl_ctx->addDecl(decl); return CompilerType(ast, ast->getTagDeclType(decl)); } return CompilerType(); } static TemplateParameterList *CreateTemplateParameterList( ASTContext *ast, const ClangASTContext::TemplateParameterInfos &template_param_infos, llvm::SmallVector &template_param_decls) { const bool parameter_pack = false; const bool is_typename = false; const unsigned depth = 0; const size_t num_template_params = template_param_infos.GetSize(); for (size_t i = 0; i < num_template_params; ++i) { const char *name = template_param_infos.names[i]; IdentifierInfo *identifier_info = nullptr; if (name && name[0]) identifier_info = &ast->Idents.get(name); if (template_param_infos.args[i].getKind() == TemplateArgument::Integral) { template_param_decls.push_back(NonTypeTemplateParmDecl::Create( *ast, ast->getTranslationUnitDecl(), // Is this the right decl context?, // SourceLocation StartLoc, SourceLocation(), SourceLocation(), depth, i, identifier_info, template_param_infos.args[i].getIntegralType(), parameter_pack, nullptr)); } else { template_param_decls.push_back(TemplateTypeParmDecl::Create( *ast, ast->getTranslationUnitDecl(), // Is this the right decl context? SourceLocation(), SourceLocation(), depth, i, identifier_info, is_typename, parameter_pack)); } } clang::Expr *const requires_clause = nullptr; // TODO: Concepts TemplateParameterList *template_param_list = TemplateParameterList::Create( *ast, SourceLocation(), SourceLocation(), template_param_decls, SourceLocation(), requires_clause); return template_param_list; } clang::FunctionTemplateDecl *ClangASTContext::CreateFunctionTemplateDecl( clang::DeclContext *decl_ctx, clang::FunctionDecl *func_decl, const char *name, const TemplateParameterInfos &template_param_infos) { // /// \brief Create a function template node. ASTContext *ast = getASTContext(); llvm::SmallVector template_param_decls; TemplateParameterList *template_param_list = CreateTemplateParameterList( ast, template_param_infos, template_param_decls); FunctionTemplateDecl *func_tmpl_decl = FunctionTemplateDecl::Create( *ast, decl_ctx, func_decl->getLocation(), func_decl->getDeclName(), template_param_list, func_decl); for (size_t i = 0, template_param_decl_count = template_param_decls.size(); i < template_param_decl_count; ++i) { // TODO: verify which decl context we should put template_param_decls into.. template_param_decls[i]->setDeclContext(func_decl); } return func_tmpl_decl; } void ClangASTContext::CreateFunctionTemplateSpecializationInfo( FunctionDecl *func_decl, clang::FunctionTemplateDecl *func_tmpl_decl, const TemplateParameterInfos &infos) { TemplateArgumentList template_args(TemplateArgumentList::OnStack, infos.args); func_decl->setFunctionTemplateSpecialization(func_tmpl_decl, &template_args, nullptr); } ClassTemplateDecl *ClangASTContext::CreateClassTemplateDecl( DeclContext *decl_ctx, lldb::AccessType access_type, const char *class_name, int kind, const TemplateParameterInfos &template_param_infos) { ASTContext *ast = getASTContext(); ClassTemplateDecl *class_template_decl = nullptr; if (decl_ctx == nullptr) decl_ctx = ast->getTranslationUnitDecl(); IdentifierInfo &identifier_info = ast->Idents.get(class_name); DeclarationName decl_name(&identifier_info); clang::DeclContext::lookup_result result = decl_ctx->lookup(decl_name); for (NamedDecl *decl : result) { class_template_decl = dyn_cast(decl); if (class_template_decl) return class_template_decl; } llvm::SmallVector template_param_decls; TemplateParameterList *template_param_list = CreateTemplateParameterList( ast, template_param_infos, template_param_decls); CXXRecordDecl *template_cxx_decl = CXXRecordDecl::Create( *ast, (TagDecl::TagKind)kind, decl_ctx, // What decl context do we use here? TU? The actual decl // context? SourceLocation(), SourceLocation(), &identifier_info); for (size_t i = 0, template_param_decl_count = template_param_decls.size(); i < template_param_decl_count; ++i) { template_param_decls[i]->setDeclContext(template_cxx_decl); } // With templated classes, we say that a class is templated with // specializations, but that the bare class has no functions. // template_cxx_decl->startDefinition(); // template_cxx_decl->completeDefinition(); class_template_decl = ClassTemplateDecl::Create( *ast, decl_ctx, // What decl context do we use here? TU? The actual decl // context? SourceLocation(), decl_name, template_param_list, template_cxx_decl, nullptr); if (class_template_decl) { if (access_type != eAccessNone) class_template_decl->setAccess( ConvertAccessTypeToAccessSpecifier(access_type)); // if (TagDecl *ctx_tag_decl = dyn_cast(decl_ctx)) // CompleteTagDeclarationDefinition(GetTypeForDecl(ctx_tag_decl)); decl_ctx->addDecl(class_template_decl); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(class_template_decl); #endif } return class_template_decl; } ClassTemplateSpecializationDecl * ClangASTContext::CreateClassTemplateSpecializationDecl( DeclContext *decl_ctx, ClassTemplateDecl *class_template_decl, int kind, const TemplateParameterInfos &template_param_infos) { ASTContext *ast = getASTContext(); ClassTemplateSpecializationDecl *class_template_specialization_decl = ClassTemplateSpecializationDecl::Create( *ast, (TagDecl::TagKind)kind, decl_ctx, SourceLocation(), SourceLocation(), class_template_decl, template_param_infos.args, nullptr); class_template_specialization_decl->setSpecializationKind( TSK_ExplicitSpecialization); return class_template_specialization_decl; } CompilerType ClangASTContext::CreateClassTemplateSpecializationType( ClassTemplateSpecializationDecl *class_template_specialization_decl) { if (class_template_specialization_decl) { ASTContext *ast = getASTContext(); if (ast) return CompilerType( ast, ast->getTagDeclType(class_template_specialization_decl)); } return CompilerType(); } static inline bool check_op_param(bool is_method, clang::OverloadedOperatorKind op_kind, bool unary, bool binary, uint32_t num_params) { // Special-case call since it can take any number of operands if (op_kind == OO_Call) return true; // The parameter count doesn't include "this" if (is_method) ++num_params; if (num_params == 1) return unary; if (num_params == 2) return binary; else return false; } bool ClangASTContext::CheckOverloadedOperatorKindParameterCount( bool is_method, clang::OverloadedOperatorKind op_kind, uint32_t num_params) { switch (op_kind) { default: break; // C++ standard allows any number of arguments to new/delete case OO_New: case OO_Array_New: case OO_Delete: case OO_Array_Delete: return true; } #define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \ case OO_##Name: \ return check_op_param(is_method, op_kind, Unary, Binary, num_params); switch (op_kind) { #include "clang/Basic/OperatorKinds.def" default: break; } return false; } clang::AccessSpecifier ClangASTContext::UnifyAccessSpecifiers(clang::AccessSpecifier lhs, clang::AccessSpecifier rhs) { // Make the access equal to the stricter of the field and the nested field's // access if (lhs == AS_none || rhs == AS_none) return AS_none; if (lhs == AS_private || rhs == AS_private) return AS_private; if (lhs == AS_protected || rhs == AS_protected) return AS_protected; return AS_public; } bool ClangASTContext::FieldIsBitfield(FieldDecl *field, uint32_t &bitfield_bit_size) { return FieldIsBitfield(getASTContext(), field, bitfield_bit_size); } bool ClangASTContext::FieldIsBitfield(ASTContext *ast, FieldDecl *field, uint32_t &bitfield_bit_size) { if (ast == nullptr || field == nullptr) return false; if (field->isBitField()) { Expr *bit_width_expr = field->getBitWidth(); if (bit_width_expr) { llvm::APSInt bit_width_apsint; if (bit_width_expr->isIntegerConstantExpr(bit_width_apsint, *ast)) { bitfield_bit_size = bit_width_apsint.getLimitedValue(UINT32_MAX); return true; } } } return false; } bool ClangASTContext::RecordHasFields(const RecordDecl *record_decl) { if (record_decl == nullptr) return false; if (!record_decl->field_empty()) return true; // No fields, lets check this is a CXX record and check the base classes const CXXRecordDecl *cxx_record_decl = dyn_cast(record_decl); if (cxx_record_decl) { CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { const CXXRecordDecl *base_class_decl = cast( base_class->getType()->getAs()->getDecl()); if (RecordHasFields(base_class_decl)) return true; } } return false; } #pragma mark Objective C Classes CompilerType ClangASTContext::CreateObjCClass(const char *name, DeclContext *decl_ctx, bool isForwardDecl, bool isInternal, ClangASTMetadata *metadata) { ASTContext *ast = getASTContext(); assert(ast != nullptr); assert(name && name[0]); if (decl_ctx == nullptr) decl_ctx = ast->getTranslationUnitDecl(); ObjCInterfaceDecl *decl = ObjCInterfaceDecl::Create( *ast, decl_ctx, SourceLocation(), &ast->Idents.get(name), nullptr, nullptr, SourceLocation(), /*isForwardDecl,*/ isInternal); if (decl && metadata) SetMetadata(ast, decl, *metadata); return CompilerType(ast, ast->getObjCInterfaceType(decl)); } static inline bool BaseSpecifierIsEmpty(const CXXBaseSpecifier *b) { return ClangASTContext::RecordHasFields(b->getType()->getAsCXXRecordDecl()) == false; } uint32_t ClangASTContext::GetNumBaseClasses(const CXXRecordDecl *cxx_record_decl, bool omit_empty_base_classes) { uint32_t num_bases = 0; if (cxx_record_decl) { if (omit_empty_base_classes) { CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { // Skip empty base classes if (omit_empty_base_classes) { if (BaseSpecifierIsEmpty(base_class)) continue; } ++num_bases; } } else num_bases = cxx_record_decl->getNumBases(); } return num_bases; } #pragma mark Namespace Declarations NamespaceDecl * ClangASTContext::GetUniqueNamespaceDeclaration(const char *name, DeclContext *decl_ctx) { NamespaceDecl *namespace_decl = nullptr; ASTContext *ast = getASTContext(); TranslationUnitDecl *translation_unit_decl = ast->getTranslationUnitDecl(); if (decl_ctx == nullptr) decl_ctx = translation_unit_decl; if (name) { IdentifierInfo &identifier_info = ast->Idents.get(name); DeclarationName decl_name(&identifier_info); clang::DeclContext::lookup_result result = decl_ctx->lookup(decl_name); for (NamedDecl *decl : result) { namespace_decl = dyn_cast(decl); if (namespace_decl) return namespace_decl; } namespace_decl = NamespaceDecl::Create(*ast, decl_ctx, false, SourceLocation(), SourceLocation(), &identifier_info, nullptr); decl_ctx->addDecl(namespace_decl); } else { if (decl_ctx == translation_unit_decl) { namespace_decl = translation_unit_decl->getAnonymousNamespace(); if (namespace_decl) return namespace_decl; namespace_decl = NamespaceDecl::Create(*ast, decl_ctx, false, SourceLocation(), SourceLocation(), nullptr, nullptr); translation_unit_decl->setAnonymousNamespace(namespace_decl); translation_unit_decl->addDecl(namespace_decl); assert(namespace_decl == translation_unit_decl->getAnonymousNamespace()); } else { NamespaceDecl *parent_namespace_decl = cast(decl_ctx); if (parent_namespace_decl) { namespace_decl = parent_namespace_decl->getAnonymousNamespace(); if (namespace_decl) return namespace_decl; namespace_decl = NamespaceDecl::Create(*ast, decl_ctx, false, SourceLocation(), SourceLocation(), nullptr, nullptr); parent_namespace_decl->setAnonymousNamespace(namespace_decl); parent_namespace_decl->addDecl(namespace_decl); assert(namespace_decl == parent_namespace_decl->getAnonymousNamespace()); } else { // BAD!!! } } } #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(namespace_decl); #endif return namespace_decl; } NamespaceDecl *ClangASTContext::GetUniqueNamespaceDeclaration( clang::ASTContext *ast, const char *name, clang::DeclContext *decl_ctx) { ClangASTContext *ast_ctx = ClangASTContext::GetASTContext(ast); if (ast_ctx == nullptr) return nullptr; return ast_ctx->GetUniqueNamespaceDeclaration(name, decl_ctx); } clang::BlockDecl * ClangASTContext::CreateBlockDeclaration(clang::DeclContext *ctx) { if (ctx != nullptr) { clang::BlockDecl *decl = clang::BlockDecl::Create(*getASTContext(), ctx, clang::SourceLocation()); ctx->addDecl(decl); return decl; } return nullptr; } clang::DeclContext *FindLCABetweenDecls(clang::DeclContext *left, clang::DeclContext *right, clang::DeclContext *root) { if (root == nullptr) return nullptr; std::set path_left; for (clang::DeclContext *d = left; d != nullptr; d = d->getParent()) path_left.insert(d); for (clang::DeclContext *d = right; d != nullptr; d = d->getParent()) if (path_left.find(d) != path_left.end()) return d; return nullptr; } clang::UsingDirectiveDecl *ClangASTContext::CreateUsingDirectiveDeclaration( clang::DeclContext *decl_ctx, clang::NamespaceDecl *ns_decl) { if (decl_ctx != nullptr && ns_decl != nullptr) { clang::TranslationUnitDecl *translation_unit = (clang::TranslationUnitDecl *)GetTranslationUnitDecl(getASTContext()); clang::UsingDirectiveDecl *using_decl = clang::UsingDirectiveDecl::Create( *getASTContext(), decl_ctx, clang::SourceLocation(), clang::SourceLocation(), clang::NestedNameSpecifierLoc(), clang::SourceLocation(), ns_decl, FindLCABetweenDecls(decl_ctx, ns_decl, translation_unit)); decl_ctx->addDecl(using_decl); return using_decl; } return nullptr; } clang::UsingDecl * ClangASTContext::CreateUsingDeclaration(clang::DeclContext *current_decl_ctx, clang::NamedDecl *target) { if (current_decl_ctx != nullptr && target != nullptr) { clang::UsingDecl *using_decl = clang::UsingDecl::Create( *getASTContext(), current_decl_ctx, clang::SourceLocation(), clang::NestedNameSpecifierLoc(), clang::DeclarationNameInfo(), false); clang::UsingShadowDecl *shadow_decl = clang::UsingShadowDecl::Create( *getASTContext(), current_decl_ctx, clang::SourceLocation(), using_decl, target); using_decl->addShadowDecl(shadow_decl); current_decl_ctx->addDecl(using_decl); return using_decl; } return nullptr; } clang::VarDecl *ClangASTContext::CreateVariableDeclaration( clang::DeclContext *decl_context, const char *name, clang::QualType type) { if (decl_context != nullptr) { clang::VarDecl *var_decl = clang::VarDecl::Create( *getASTContext(), decl_context, clang::SourceLocation(), clang::SourceLocation(), name && name[0] ? &getASTContext()->Idents.getOwn(name) : nullptr, type, nullptr, clang::SC_None); var_decl->setAccess(clang::AS_public); decl_context->addDecl(var_decl); return var_decl; } return nullptr; } lldb::opaque_compiler_type_t ClangASTContext::GetOpaqueCompilerType(clang::ASTContext *ast, lldb::BasicType basic_type) { switch (basic_type) { case eBasicTypeVoid: return ast->VoidTy.getAsOpaquePtr(); case eBasicTypeChar: return ast->CharTy.getAsOpaquePtr(); case eBasicTypeSignedChar: return ast->SignedCharTy.getAsOpaquePtr(); case eBasicTypeUnsignedChar: return ast->UnsignedCharTy.getAsOpaquePtr(); case eBasicTypeWChar: return ast->getWCharType().getAsOpaquePtr(); case eBasicTypeSignedWChar: return ast->getSignedWCharType().getAsOpaquePtr(); case eBasicTypeUnsignedWChar: return ast->getUnsignedWCharType().getAsOpaquePtr(); case eBasicTypeChar16: return ast->Char16Ty.getAsOpaquePtr(); case eBasicTypeChar32: return ast->Char32Ty.getAsOpaquePtr(); case eBasicTypeShort: return ast->ShortTy.getAsOpaquePtr(); case eBasicTypeUnsignedShort: return ast->UnsignedShortTy.getAsOpaquePtr(); case eBasicTypeInt: return ast->IntTy.getAsOpaquePtr(); case eBasicTypeUnsignedInt: return ast->UnsignedIntTy.getAsOpaquePtr(); case eBasicTypeLong: return ast->LongTy.getAsOpaquePtr(); case eBasicTypeUnsignedLong: return ast->UnsignedLongTy.getAsOpaquePtr(); case eBasicTypeLongLong: return ast->LongLongTy.getAsOpaquePtr(); case eBasicTypeUnsignedLongLong: return ast->UnsignedLongLongTy.getAsOpaquePtr(); case eBasicTypeInt128: return ast->Int128Ty.getAsOpaquePtr(); case eBasicTypeUnsignedInt128: return ast->UnsignedInt128Ty.getAsOpaquePtr(); case eBasicTypeBool: return ast->BoolTy.getAsOpaquePtr(); case eBasicTypeHalf: return ast->HalfTy.getAsOpaquePtr(); case eBasicTypeFloat: return ast->FloatTy.getAsOpaquePtr(); case eBasicTypeDouble: return ast->DoubleTy.getAsOpaquePtr(); case eBasicTypeLongDouble: return ast->LongDoubleTy.getAsOpaquePtr(); case eBasicTypeFloatComplex: return ast->FloatComplexTy.getAsOpaquePtr(); case eBasicTypeDoubleComplex: return ast->DoubleComplexTy.getAsOpaquePtr(); case eBasicTypeLongDoubleComplex: return ast->LongDoubleComplexTy.getAsOpaquePtr(); case eBasicTypeObjCID: return ast->getObjCIdType().getAsOpaquePtr(); case eBasicTypeObjCClass: return ast->getObjCClassType().getAsOpaquePtr(); case eBasicTypeObjCSel: return ast->getObjCSelType().getAsOpaquePtr(); case eBasicTypeNullPtr: return ast->NullPtrTy.getAsOpaquePtr(); default: return nullptr; } } #pragma mark Function Types clang::DeclarationName ClangASTContext::GetDeclarationName(const char *name, const CompilerType &function_clang_type) { if (!name || !name[0]) return clang::DeclarationName(); clang::OverloadedOperatorKind op_kind = clang::NUM_OVERLOADED_OPERATORS; if (!IsOperator(name, op_kind) || op_kind == clang::NUM_OVERLOADED_OPERATORS) return DeclarationName(&getASTContext()->Idents.get( name)); // Not operator, but a regular function. // Check the number of operator parameters. Sometimes we have // seen bad DWARF that doesn't correctly describe operators and // if we try to create a method and add it to the class, clang // will assert and crash, so we need to make sure things are // acceptable. clang::QualType method_qual_type(ClangUtil::GetQualType(function_clang_type)); const clang::FunctionProtoType *function_type = llvm::dyn_cast(method_qual_type.getTypePtr()); if (function_type == nullptr) return clang::DeclarationName(); const bool is_method = false; const unsigned int num_params = function_type->getNumParams(); if (!ClangASTContext::CheckOverloadedOperatorKindParameterCount( is_method, op_kind, num_params)) return clang::DeclarationName(); return getASTContext()->DeclarationNames.getCXXOperatorName(op_kind); } FunctionDecl *ClangASTContext::CreateFunctionDeclaration( DeclContext *decl_ctx, const char *name, const CompilerType &function_clang_type, int storage, bool is_inline) { FunctionDecl *func_decl = nullptr; ASTContext *ast = getASTContext(); if (decl_ctx == nullptr) decl_ctx = ast->getTranslationUnitDecl(); const bool hasWrittenPrototype = true; const bool isConstexprSpecified = false; clang::DeclarationName declarationName = GetDeclarationName(name, function_clang_type); func_decl = FunctionDecl::Create( *ast, decl_ctx, SourceLocation(), SourceLocation(), declarationName, ClangUtil::GetQualType(function_clang_type), nullptr, (clang::StorageClass)storage, is_inline, hasWrittenPrototype, isConstexprSpecified); if (func_decl) decl_ctx->addDecl(func_decl); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(func_decl); #endif return func_decl; } CompilerType ClangASTContext::CreateFunctionType( ASTContext *ast, const CompilerType &result_type, const CompilerType *args, unsigned num_args, bool is_variadic, unsigned type_quals) { if (ast == nullptr) return CompilerType(); // invalid AST if (!result_type || !ClangUtil::IsClangType(result_type)) return CompilerType(); // invalid return type std::vector qual_type_args; if (num_args > 0 && args == nullptr) return CompilerType(); // invalid argument array passed in // Verify that all arguments are valid and the right type for (unsigned i = 0; i < num_args; ++i) { if (args[i]) { // Make sure we have a clang type in args[i] and not a type from another // language whose name might match const bool is_clang_type = ClangUtil::IsClangType(args[i]); lldbassert(is_clang_type); if (is_clang_type) qual_type_args.push_back(ClangUtil::GetQualType(args[i])); else return CompilerType(); // invalid argument type (must be a clang type) } else return CompilerType(); // invalid argument type (empty) } // TODO: Detect calling convention in DWARF? FunctionProtoType::ExtProtoInfo proto_info; proto_info.Variadic = is_variadic; proto_info.ExceptionSpec = EST_None; proto_info.TypeQuals = type_quals; proto_info.RefQualifier = RQ_None; return CompilerType(ast, ast->getFunctionType(ClangUtil::GetQualType(result_type), qual_type_args, proto_info)); } ParmVarDecl *ClangASTContext::CreateParameterDeclaration( const char *name, const CompilerType ¶m_type, int storage) { ASTContext *ast = getASTContext(); assert(ast != nullptr); return ParmVarDecl::Create(*ast, ast->getTranslationUnitDecl(), SourceLocation(), SourceLocation(), name && name[0] ? &ast->Idents.get(name) : nullptr, ClangUtil::GetQualType(param_type), nullptr, (clang::StorageClass)storage, nullptr); } void ClangASTContext::SetFunctionParameters(FunctionDecl *function_decl, ParmVarDecl **params, unsigned num_params) { if (function_decl) function_decl->setParams(ArrayRef(params, num_params)); } CompilerType ClangASTContext::CreateBlockPointerType(const CompilerType &function_type) { QualType block_type = m_ast_ap->getBlockPointerType( clang::QualType::getFromOpaquePtr(function_type.GetOpaqueQualType())); return CompilerType(this, block_type.getAsOpaquePtr()); } #pragma mark Array Types CompilerType ClangASTContext::CreateArrayType(const CompilerType &element_type, size_t element_count, bool is_vector) { if (element_type.IsValid()) { ASTContext *ast = getASTContext(); assert(ast != nullptr); if (is_vector) { return CompilerType( ast, ast->getExtVectorType(ClangUtil::GetQualType(element_type), element_count)); } else { llvm::APInt ap_element_count(64, element_count); if (element_count == 0) { return CompilerType(ast, ast->getIncompleteArrayType( ClangUtil::GetQualType(element_type), clang::ArrayType::Normal, 0)); } else { return CompilerType( ast, ast->getConstantArrayType(ClangUtil::GetQualType(element_type), ap_element_count, clang::ArrayType::Normal, 0)); } } } return CompilerType(); } CompilerType ClangASTContext::CreateStructForIdentifier( const ConstString &type_name, const std::initializer_list> &type_fields, bool packed) { CompilerType type; if (!type_name.IsEmpty() && (type = GetTypeForIdentifier(type_name)) .IsValid()) { - lldbassert("Trying to create a type for an existing name"); + lldbassert(0 && "Trying to create a type for an existing name"); return type; } type = CreateRecordType(nullptr, lldb::eAccessPublic, type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); StartTagDeclarationDefinition(type); for (const auto &field : type_fields) AddFieldToRecordType(type, field.first, field.second, lldb::eAccessPublic, 0); if (packed) SetIsPacked(type); CompleteTagDeclarationDefinition(type); return type; } CompilerType ClangASTContext::GetOrCreateStructForIdentifier( const ConstString &type_name, const std::initializer_list> &type_fields, bool packed) { CompilerType type; if ((type = GetTypeForIdentifier(type_name)).IsValid()) return type; return CreateStructForIdentifier(type_name, type_fields, packed); } #pragma mark Enumeration Types CompilerType ClangASTContext::CreateEnumerationType(const char *name, DeclContext *decl_ctx, const Declaration &decl, const CompilerType &integer_clang_type) { // TODO: Do something intelligent with the Declaration object passed in // like maybe filling in the SourceLocation with it... ASTContext *ast = getASTContext(); // TODO: ask about these... // const bool IsScoped = false; // const bool IsFixed = false; EnumDecl *enum_decl = EnumDecl::Create( *ast, decl_ctx, SourceLocation(), SourceLocation(), name && name[0] ? &ast->Idents.get(name) : nullptr, nullptr, false, // IsScoped false, // IsScopedUsingClassTag false); // IsFixed if (enum_decl) { // TODO: check if we should be setting the promotion type too? enum_decl->setIntegerType(ClangUtil::GetQualType(integer_clang_type)); enum_decl->setAccess(AS_public); // TODO respect what's in the debug info return CompilerType(ast, ast->getTagDeclType(enum_decl)); } return CompilerType(); } // Disable this for now since I can't seem to get a nicely formatted float // out of the APFloat class without just getting the float, double or quad // and then using a formatted print on it which defeats the purpose. We ideally // would like to get perfect string values for any kind of float semantics // so we can support remote targets. The code below also requires a patch to // llvm::APInt. // bool // ClangASTContext::ConvertFloatValueToString (ASTContext *ast, // lldb::opaque_compiler_type_t clang_type, const uint8_t* bytes, size_t // byte_size, int apint_byte_order, std::string &float_str) //{ // uint32_t count = 0; // bool is_complex = false; // if (ClangASTContext::IsFloatingPointType (clang_type, count, is_complex)) // { // unsigned num_bytes_per_float = byte_size / count; // unsigned num_bits_per_float = num_bytes_per_float * 8; // // float_str.clear(); // uint32_t i; // for (i=0; i 0) // { // if (i > 0) // float_str.append(", "); // float_str.append(s); // if (i == 1 && is_complex) // float_str.append(1, 'i'); // } // } // return !float_str.empty(); // } // return false; //} CompilerType ClangASTContext::GetIntTypeFromBitSize(clang::ASTContext *ast, size_t bit_size, bool is_signed) { if (ast) { if (is_signed) { if (bit_size == ast->getTypeSize(ast->SignedCharTy)) return CompilerType(ast, ast->SignedCharTy); if (bit_size == ast->getTypeSize(ast->ShortTy)) return CompilerType(ast, ast->ShortTy); if (bit_size == ast->getTypeSize(ast->IntTy)) return CompilerType(ast, ast->IntTy); if (bit_size == ast->getTypeSize(ast->LongTy)) return CompilerType(ast, ast->LongTy); if (bit_size == ast->getTypeSize(ast->LongLongTy)) return CompilerType(ast, ast->LongLongTy); if (bit_size == ast->getTypeSize(ast->Int128Ty)) return CompilerType(ast, ast->Int128Ty); } else { if (bit_size == ast->getTypeSize(ast->UnsignedCharTy)) return CompilerType(ast, ast->UnsignedCharTy); if (bit_size == ast->getTypeSize(ast->UnsignedShortTy)) return CompilerType(ast, ast->UnsignedShortTy); if (bit_size == ast->getTypeSize(ast->UnsignedIntTy)) return CompilerType(ast, ast->UnsignedIntTy); if (bit_size == ast->getTypeSize(ast->UnsignedLongTy)) return CompilerType(ast, ast->UnsignedLongTy); if (bit_size == ast->getTypeSize(ast->UnsignedLongLongTy)) return CompilerType(ast, ast->UnsignedLongLongTy); if (bit_size == ast->getTypeSize(ast->UnsignedInt128Ty)) return CompilerType(ast, ast->UnsignedInt128Ty); } } return CompilerType(); } CompilerType ClangASTContext::GetPointerSizedIntType(clang::ASTContext *ast, bool is_signed) { if (ast) return GetIntTypeFromBitSize(ast, ast->getTypeSize(ast->VoidPtrTy), is_signed); return CompilerType(); } void ClangASTContext::DumpDeclContextHiearchy(clang::DeclContext *decl_ctx) { if (decl_ctx) { DumpDeclContextHiearchy(decl_ctx->getParent()); clang::NamedDecl *named_decl = llvm::dyn_cast(decl_ctx); if (named_decl) { printf("%20s: %s\n", decl_ctx->getDeclKindName(), named_decl->getDeclName().getAsString().c_str()); } else { printf("%20s\n", decl_ctx->getDeclKindName()); } } } void ClangASTContext::DumpDeclHiearchy(clang::Decl *decl) { if (decl == nullptr) return; DumpDeclContextHiearchy(decl->getDeclContext()); clang::RecordDecl *record_decl = llvm::dyn_cast(decl); if (record_decl) { printf("%20s: %s%s\n", decl->getDeclKindName(), record_decl->getDeclName().getAsString().c_str(), record_decl->isInjectedClassName() ? " (injected class name)" : ""); } else { clang::NamedDecl *named_decl = llvm::dyn_cast(decl); if (named_decl) { printf("%20s: %s\n", decl->getDeclKindName(), named_decl->getDeclName().getAsString().c_str()); } else { printf("%20s\n", decl->getDeclKindName()); } } } bool ClangASTContext::DeclsAreEquivalent(clang::Decl *lhs_decl, clang::Decl *rhs_decl) { if (lhs_decl && rhs_decl) { //---------------------------------------------------------------------- // Make sure the decl kinds match first //---------------------------------------------------------------------- const clang::Decl::Kind lhs_decl_kind = lhs_decl->getKind(); const clang::Decl::Kind rhs_decl_kind = rhs_decl->getKind(); if (lhs_decl_kind == rhs_decl_kind) { //------------------------------------------------------------------ // Now check that the decl contexts kinds are all equivalent // before we have to check any names of the decl contexts... //------------------------------------------------------------------ clang::DeclContext *lhs_decl_ctx = lhs_decl->getDeclContext(); clang::DeclContext *rhs_decl_ctx = rhs_decl->getDeclContext(); if (lhs_decl_ctx && rhs_decl_ctx) { while (1) { if (lhs_decl_ctx && rhs_decl_ctx) { const clang::Decl::Kind lhs_decl_ctx_kind = lhs_decl_ctx->getDeclKind(); const clang::Decl::Kind rhs_decl_ctx_kind = rhs_decl_ctx->getDeclKind(); if (lhs_decl_ctx_kind == rhs_decl_ctx_kind) { lhs_decl_ctx = lhs_decl_ctx->getParent(); rhs_decl_ctx = rhs_decl_ctx->getParent(); if (lhs_decl_ctx == nullptr && rhs_decl_ctx == nullptr) break; } else return false; } else return false; } //-------------------------------------------------------------- // Now make sure the name of the decls match //-------------------------------------------------------------- clang::NamedDecl *lhs_named_decl = llvm::dyn_cast(lhs_decl); clang::NamedDecl *rhs_named_decl = llvm::dyn_cast(rhs_decl); if (lhs_named_decl && rhs_named_decl) { clang::DeclarationName lhs_decl_name = lhs_named_decl->getDeclName(); clang::DeclarationName rhs_decl_name = rhs_named_decl->getDeclName(); if (lhs_decl_name.getNameKind() == rhs_decl_name.getNameKind()) { if (lhs_decl_name.getAsString() != rhs_decl_name.getAsString()) return false; } else return false; } else return false; //-------------------------------------------------------------- // We know that the decl context kinds all match, so now we need // to make sure the names match as well //-------------------------------------------------------------- lhs_decl_ctx = lhs_decl->getDeclContext(); rhs_decl_ctx = rhs_decl->getDeclContext(); while (1) { switch (lhs_decl_ctx->getDeclKind()) { case clang::Decl::TranslationUnit: // We don't care about the translation unit names return true; default: { clang::NamedDecl *lhs_named_decl = llvm::dyn_cast(lhs_decl_ctx); clang::NamedDecl *rhs_named_decl = llvm::dyn_cast(rhs_decl_ctx); if (lhs_named_decl && rhs_named_decl) { clang::DeclarationName lhs_decl_name = lhs_named_decl->getDeclName(); clang::DeclarationName rhs_decl_name = rhs_named_decl->getDeclName(); if (lhs_decl_name.getNameKind() == rhs_decl_name.getNameKind()) { if (lhs_decl_name.getAsString() != rhs_decl_name.getAsString()) return false; } else return false; } else return false; } break; } lhs_decl_ctx = lhs_decl_ctx->getParent(); rhs_decl_ctx = rhs_decl_ctx->getParent(); } } } } return false; } bool ClangASTContext::GetCompleteDecl(clang::ASTContext *ast, clang::Decl *decl) { if (!decl) return false; ExternalASTSource *ast_source = ast->getExternalSource(); if (!ast_source) return false; if (clang::TagDecl *tag_decl = llvm::dyn_cast(decl)) { if (tag_decl->isCompleteDefinition()) return true; if (!tag_decl->hasExternalLexicalStorage()) return false; ast_source->CompleteType(tag_decl); return !tag_decl->getTypeForDecl()->isIncompleteType(); } else if (clang::ObjCInterfaceDecl *objc_interface_decl = llvm::dyn_cast(decl)) { if (objc_interface_decl->getDefinition()) return true; if (!objc_interface_decl->hasExternalLexicalStorage()) return false; ast_source->CompleteType(objc_interface_decl); return !objc_interface_decl->getTypeForDecl()->isIncompleteType(); } else { return false; } } void ClangASTContext::SetMetadataAsUserID(const void *object, user_id_t user_id) { ClangASTMetadata meta_data; meta_data.SetUserID(user_id); SetMetadata(object, meta_data); } void ClangASTContext::SetMetadata(clang::ASTContext *ast, const void *object, ClangASTMetadata &metadata) { ClangExternalASTSourceCommon *external_source = ClangExternalASTSourceCommon::Lookup(ast->getExternalSource()); if (external_source) external_source->SetMetadata(object, metadata); } ClangASTMetadata *ClangASTContext::GetMetadata(clang::ASTContext *ast, const void *object) { ClangExternalASTSourceCommon *external_source = ClangExternalASTSourceCommon::Lookup(ast->getExternalSource()); if (external_source && external_source->HasMetadata(object)) return external_source->GetMetadata(object); else return nullptr; } clang::DeclContext * ClangASTContext::GetAsDeclContext(clang::CXXMethodDecl *cxx_method_decl) { return llvm::dyn_cast(cxx_method_decl); } clang::DeclContext * ClangASTContext::GetAsDeclContext(clang::ObjCMethodDecl *objc_method_decl) { return llvm::dyn_cast(objc_method_decl); } bool ClangASTContext::SetTagTypeKind(clang::QualType tag_qual_type, int kind) const { const clang::Type *clang_type = tag_qual_type.getTypePtr(); if (clang_type) { const clang::TagType *tag_type = llvm::dyn_cast(clang_type); if (tag_type) { clang::TagDecl *tag_decl = llvm::dyn_cast(tag_type->getDecl()); if (tag_decl) { tag_decl->setTagKind((clang::TagDecl::TagKind)kind); return true; } } } return false; } bool ClangASTContext::SetDefaultAccessForRecordFields( clang::RecordDecl *record_decl, int default_accessibility, int *assigned_accessibilities, size_t num_assigned_accessibilities) { if (record_decl) { uint32_t field_idx; clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(), field_idx = 0; field != field_end; ++field, ++field_idx) { // If no accessibility was assigned, assign the correct one if (field_idx < num_assigned_accessibilities && assigned_accessibilities[field_idx] == clang::AS_none) field->setAccess((clang::AccessSpecifier)default_accessibility); } return true; } return false; } clang::DeclContext * ClangASTContext::GetDeclContextForType(const CompilerType &type) { return GetDeclContextForType(ClangUtil::GetQualType(type)); } clang::DeclContext * ClangASTContext::GetDeclContextForType(clang::QualType type) { if (type.isNull()) return nullptr; clang::QualType qual_type = type.getCanonicalType(); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::ObjCInterface: return llvm::cast(qual_type.getTypePtr()) ->getInterface(); case clang::Type::ObjCObjectPointer: return GetDeclContextForType( llvm::cast(qual_type.getTypePtr()) ->getPointeeType()); case clang::Type::Record: return llvm::cast(qual_type)->getDecl(); case clang::Type::Enum: return llvm::cast(qual_type)->getDecl(); case clang::Type::Typedef: return GetDeclContextForType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()); case clang::Type::Auto: return GetDeclContextForType( llvm::cast(qual_type)->getDeducedType()); case clang::Type::Elaborated: return GetDeclContextForType( llvm::cast(qual_type)->getNamedType()); case clang::Type::Paren: return GetDeclContextForType( llvm::cast(qual_type)->desugar()); default: break; } // No DeclContext in this type... return nullptr; } static bool GetCompleteQualType(clang::ASTContext *ast, clang::QualType qual_type, bool allow_completion = true) { const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::ConstantArray: case clang::Type::IncompleteArray: case clang::Type::VariableArray: { const clang::ArrayType *array_type = llvm::dyn_cast(qual_type.getTypePtr()); if (array_type) return GetCompleteQualType(ast, array_type->getElementType(), allow_completion); } break; case clang::Type::Record: { clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { if (cxx_record_decl->hasExternalLexicalStorage()) { const bool is_complete = cxx_record_decl->isCompleteDefinition(); const bool fields_loaded = cxx_record_decl->hasLoadedFieldsFromExternalStorage(); if (is_complete && fields_loaded) return true; if (!allow_completion) return false; // Call the field_begin() accessor to for it to use the external source // to load the fields... clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); if (external_ast_source) { external_ast_source->CompleteType(cxx_record_decl); if (cxx_record_decl->isCompleteDefinition()) { cxx_record_decl->field_begin(); cxx_record_decl->setHasLoadedFieldsFromExternalStorage(true); } } } } const clang::TagType *tag_type = llvm::cast(qual_type.getTypePtr()); return !tag_type->isIncompleteType(); } break; case clang::Type::Enum: { const clang::TagType *tag_type = llvm::dyn_cast(qual_type.getTypePtr()); if (tag_type) { clang::TagDecl *tag_decl = tag_type->getDecl(); if (tag_decl) { if (tag_decl->getDefinition()) return true; if (!allow_completion) return false; if (tag_decl->hasExternalLexicalStorage()) { if (ast) { clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); if (external_ast_source) { external_ast_source->CompleteType(tag_decl); return !tag_type->isIncompleteType(); } } } return false; } } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); // We currently can't complete objective C types through the newly added // ASTContext // because it only supports TagDecl objects right now... if (class_interface_decl) { if (class_interface_decl->getDefinition()) return true; if (!allow_completion) return false; if (class_interface_decl->hasExternalLexicalStorage()) { if (ast) { clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); if (external_ast_source) { external_ast_source->CompleteType(class_interface_decl); return !objc_class_type->isIncompleteType(); } } } return false; } } } break; case clang::Type::Typedef: return GetCompleteQualType(ast, llvm::cast(qual_type) ->getDecl() ->getUnderlyingType(), allow_completion); case clang::Type::Auto: return GetCompleteQualType( ast, llvm::cast(qual_type)->getDeducedType(), allow_completion); case clang::Type::Elaborated: return GetCompleteQualType( ast, llvm::cast(qual_type)->getNamedType(), allow_completion); case clang::Type::Paren: return GetCompleteQualType( ast, llvm::cast(qual_type)->desugar(), allow_completion); case clang::Type::Attributed: return GetCompleteQualType( ast, llvm::cast(qual_type)->getModifiedType(), allow_completion); default: break; } return true; } static clang::ObjCIvarDecl::AccessControl ConvertAccessTypeToObjCIvarAccessControl(AccessType access) { switch (access) { case eAccessNone: return clang::ObjCIvarDecl::None; case eAccessPublic: return clang::ObjCIvarDecl::Public; case eAccessPrivate: return clang::ObjCIvarDecl::Private; case eAccessProtected: return clang::ObjCIvarDecl::Protected; case eAccessPackage: return clang::ObjCIvarDecl::Package; } return clang::ObjCIvarDecl::None; } //---------------------------------------------------------------------- // Tests //---------------------------------------------------------------------- bool ClangASTContext::IsAggregateType(lldb::opaque_compiler_type_t type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::IncompleteArray: case clang::Type::VariableArray: case clang::Type::ConstantArray: case clang::Type::ExtVector: case clang::Type::Vector: case clang::Type::Record: case clang::Type::ObjCObject: case clang::Type::ObjCInterface: return true; case clang::Type::Auto: return IsAggregateType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr()); case clang::Type::Elaborated: return IsAggregateType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr()); case clang::Type::Typedef: return IsAggregateType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr()); case clang::Type::Paren: return IsAggregateType( llvm::cast(qual_type)->desugar().getAsOpaquePtr()); default: break; } // The clang type does have a value return false; } bool ClangASTContext::IsAnonymousType(lldb::opaque_compiler_type_t type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: { if (const clang::RecordType *record_type = llvm::dyn_cast_or_null( qual_type.getTypePtrOrNull())) { if (const clang::RecordDecl *record_decl = record_type->getDecl()) { return record_decl->isAnonymousStructOrUnion(); } } break; } case clang::Type::Auto: return IsAnonymousType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr()); case clang::Type::Elaborated: return IsAnonymousType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr()); case clang::Type::Typedef: return IsAnonymousType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr()); case clang::Type::Paren: return IsAnonymousType( llvm::cast(qual_type)->desugar().getAsOpaquePtr()); default: break; } // The clang type does have a value return false; } bool ClangASTContext::IsArrayType(lldb::opaque_compiler_type_t type, CompilerType *element_type_ptr, uint64_t *size, bool *is_incomplete) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { default: break; case clang::Type::ConstantArray: if (element_type_ptr) element_type_ptr->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getElementType()); if (size) *size = llvm::cast(qual_type) ->getSize() .getLimitedValue(ULLONG_MAX); if (is_incomplete) *is_incomplete = false; return true; case clang::Type::IncompleteArray: if (element_type_ptr) element_type_ptr->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getElementType()); if (size) *size = 0; if (is_incomplete) *is_incomplete = true; return true; case clang::Type::VariableArray: if (element_type_ptr) element_type_ptr->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getElementType()); if (size) *size = 0; if (is_incomplete) *is_incomplete = false; return true; case clang::Type::DependentSizedArray: if (element_type_ptr) element_type_ptr->SetCompilerType( getASTContext(), llvm::cast(qual_type) ->getElementType()); if (size) *size = 0; if (is_incomplete) *is_incomplete = false; return true; case clang::Type::Typedef: return IsArrayType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), element_type_ptr, size, is_incomplete); case clang::Type::Auto: return IsArrayType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), element_type_ptr, size, is_incomplete); case clang::Type::Elaborated: return IsArrayType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), element_type_ptr, size, is_incomplete); case clang::Type::Paren: return IsArrayType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), element_type_ptr, size, is_incomplete); } if (element_type_ptr) element_type_ptr->Clear(); if (size) *size = 0; if (is_incomplete) *is_incomplete = false; return false; } bool ClangASTContext::IsVectorType(lldb::opaque_compiler_type_t type, CompilerType *element_type, uint64_t *size) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Vector: { const clang::VectorType *vector_type = qual_type->getAs(); if (vector_type) { if (size) *size = vector_type->getNumElements(); if (element_type) *element_type = CompilerType(getASTContext(), vector_type->getElementType()); } return true; } break; case clang::Type::ExtVector: { const clang::ExtVectorType *ext_vector_type = qual_type->getAs(); if (ext_vector_type) { if (size) *size = ext_vector_type->getNumElements(); if (element_type) *element_type = CompilerType(getASTContext(), ext_vector_type->getElementType()); } return true; } default: break; } return false; } bool ClangASTContext::IsRuntimeGeneratedType( lldb::opaque_compiler_type_t type) { clang::DeclContext *decl_ctx = ClangASTContext::GetASTContext(getASTContext()) ->GetDeclContextForType(GetQualType(type)); if (!decl_ctx) return false; if (!llvm::isa(decl_ctx)) return false; clang::ObjCInterfaceDecl *result_iface_decl = llvm::dyn_cast(decl_ctx); ClangASTMetadata *ast_metadata = ClangASTContext::GetMetadata(getASTContext(), result_iface_decl); if (!ast_metadata) return false; return (ast_metadata->GetISAPtr() != 0); } bool ClangASTContext::IsCharType(lldb::opaque_compiler_type_t type) { return GetQualType(type).getUnqualifiedType()->isCharType(); } bool ClangASTContext::IsCompleteType(lldb::opaque_compiler_type_t type) { const bool allow_completion = false; return GetCompleteQualType(getASTContext(), GetQualType(type), allow_completion); } bool ClangASTContext::IsConst(lldb::opaque_compiler_type_t type) { return GetQualType(type).isConstQualified(); } bool ClangASTContext::IsCStringType(lldb::opaque_compiler_type_t type, uint32_t &length) { CompilerType pointee_or_element_clang_type; length = 0; Flags type_flags(GetTypeInfo(type, &pointee_or_element_clang_type)); if (!pointee_or_element_clang_type.IsValid()) return false; if (type_flags.AnySet(eTypeIsArray | eTypeIsPointer)) { if (pointee_or_element_clang_type.IsCharType()) { if (type_flags.Test(eTypeIsArray)) { // We know the size of the array and it could be a C string // since it is an array of characters length = llvm::cast( GetCanonicalQualType(type).getTypePtr()) ->getSize() .getLimitedValue(); } return true; } } return false; } bool ClangASTContext::IsFunctionType(lldb::opaque_compiler_type_t type, bool *is_variadic_ptr) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); if (qual_type->isFunctionType()) { if (is_variadic_ptr) { const clang::FunctionProtoType *function_proto_type = llvm::dyn_cast(qual_type.getTypePtr()); if (function_proto_type) *is_variadic_ptr = function_proto_type->isVariadic(); else *is_variadic_ptr = false; } return true; } const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { default: break; case clang::Type::Typedef: return IsFunctionType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), nullptr); case clang::Type::Auto: return IsFunctionType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), nullptr); case clang::Type::Elaborated: return IsFunctionType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), nullptr); case clang::Type::Paren: return IsFunctionType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), nullptr); case clang::Type::LValueReference: case clang::Type::RValueReference: { const clang::ReferenceType *reference_type = llvm::cast(qual_type.getTypePtr()); if (reference_type) return IsFunctionType(reference_type->getPointeeType().getAsOpaquePtr(), nullptr); } break; } } return false; } // Used to detect "Homogeneous Floating-point Aggregates" uint32_t ClangASTContext::IsHomogeneousAggregate(lldb::opaque_compiler_type_t type, CompilerType *base_type_ptr) { if (!type) return 0; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { if (cxx_record_decl->getNumBases() || cxx_record_decl->isDynamicClass()) return 0; } const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); if (record_type) { const clang::RecordDecl *record_decl = record_type->getDecl(); if (record_decl) { // We are looking for a structure that contains only floating point // types clang::RecordDecl::field_iterator field_pos, field_end = record_decl->field_end(); uint32_t num_fields = 0; bool is_hva = false; bool is_hfa = false; clang::QualType base_qual_type; uint64_t base_bitwidth = 0; for (field_pos = record_decl->field_begin(); field_pos != field_end; ++field_pos) { clang::QualType field_qual_type = field_pos->getType(); uint64_t field_bitwidth = getASTContext()->getTypeSize(qual_type); if (field_qual_type->isFloatingType()) { if (field_qual_type->isComplexType()) return 0; else { if (num_fields == 0) base_qual_type = field_qual_type; else { if (is_hva) return 0; is_hfa = true; if (field_qual_type.getTypePtr() != base_qual_type.getTypePtr()) return 0; } } } else if (field_qual_type->isVectorType() || field_qual_type->isExtVectorType()) { if (num_fields == 0) { base_qual_type = field_qual_type; base_bitwidth = field_bitwidth; } else { if (is_hfa) return 0; is_hva = true; if (base_bitwidth != field_bitwidth) return 0; if (field_qual_type.getTypePtr() != base_qual_type.getTypePtr()) return 0; } } else return 0; ++num_fields; } if (base_type_ptr) *base_type_ptr = CompilerType(getASTContext(), base_qual_type); return num_fields; } } } break; case clang::Type::Typedef: return IsHomogeneousAggregate(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), base_type_ptr); case clang::Type::Auto: return IsHomogeneousAggregate(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), base_type_ptr); case clang::Type::Elaborated: return IsHomogeneousAggregate(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), base_type_ptr); default: break; } return 0; } size_t ClangASTContext::GetNumberOfFunctionArguments( lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::FunctionProtoType *func = llvm::dyn_cast(qual_type.getTypePtr()); if (func) return func->getNumParams(); } return 0; } CompilerType ClangASTContext::GetFunctionArgumentAtIndex(lldb::opaque_compiler_type_t type, const size_t index) { if (type) { clang::QualType qual_type(GetQualType(type)); const clang::FunctionProtoType *func = llvm::dyn_cast(qual_type.getTypePtr()); if (func) { if (index < func->getNumParams()) return CompilerType(getASTContext(), func->getParamType(index)); } } return CompilerType(); } bool ClangASTContext::IsFunctionPointerType(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); if (qual_type->isFunctionPointerType()) return true; const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { default: break; case clang::Type::Typedef: return IsFunctionPointerType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr()); case clang::Type::Auto: return IsFunctionPointerType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr()); case clang::Type::Elaborated: return IsFunctionPointerType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr()); case clang::Type::Paren: return IsFunctionPointerType( llvm::cast(qual_type)->desugar().getAsOpaquePtr()); case clang::Type::LValueReference: case clang::Type::RValueReference: { const clang::ReferenceType *reference_type = llvm::cast(qual_type.getTypePtr()); if (reference_type) return IsFunctionPointerType( reference_type->getPointeeType().getAsOpaquePtr()); } break; } } return false; } bool ClangASTContext::IsBlockPointerType( lldb::opaque_compiler_type_t type, CompilerType *function_pointer_type_ptr) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); if (qual_type->isBlockPointerType()) { if (function_pointer_type_ptr) { const clang::BlockPointerType *block_pointer_type = qual_type->getAs(); QualType pointee_type = block_pointer_type->getPointeeType(); QualType function_pointer_type = m_ast_ap->getPointerType(pointee_type); *function_pointer_type_ptr = CompilerType(getASTContext(), function_pointer_type); } return true; } const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { default: break; case clang::Type::Typedef: return IsBlockPointerType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), function_pointer_type_ptr); case clang::Type::Auto: return IsBlockPointerType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), function_pointer_type_ptr); case clang::Type::Elaborated: return IsBlockPointerType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), function_pointer_type_ptr); case clang::Type::Paren: return IsBlockPointerType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), function_pointer_type_ptr); case clang::Type::LValueReference: case clang::Type::RValueReference: { const clang::ReferenceType *reference_type = llvm::cast(qual_type.getTypePtr()); if (reference_type) return IsBlockPointerType( reference_type->getPointeeType().getAsOpaquePtr(), function_pointer_type_ptr); } break; } } return false; } bool ClangASTContext::IsIntegerType(lldb::opaque_compiler_type_t type, bool &is_signed) { if (!type) return false; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::BuiltinType *builtin_type = llvm::dyn_cast(qual_type->getCanonicalTypeInternal()); if (builtin_type) { if (builtin_type->isInteger()) { is_signed = builtin_type->isSignedInteger(); return true; } } return false; } bool ClangASTContext::IsEnumerationType(lldb::opaque_compiler_type_t type, bool &is_signed) { if (type) { const clang::EnumType *enum_type = llvm::dyn_cast( GetCanonicalQualType(type)->getCanonicalTypeInternal()); if (enum_type) { IsIntegerType(enum_type->getDecl()->getIntegerType().getAsOpaquePtr(), is_signed); return true; } } return false; } bool ClangASTContext::IsPointerType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { default: break; case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCClass: return true; } return false; case clang::Type::ObjCObjectPointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type) ->getPointeeType()); return true; case clang::Type::BlockPointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getPointeeType()); return true; case clang::Type::Pointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getPointeeType()); return true; case clang::Type::MemberPointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getPointeeType()); return true; case clang::Type::Typedef: return IsPointerType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), pointee_type); case clang::Type::Auto: return IsPointerType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), pointee_type); case clang::Type::Elaborated: return IsPointerType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), pointee_type); case clang::Type::Paren: return IsPointerType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), pointee_type); default: break; } } if (pointee_type) pointee_type->Clear(); return false; } bool ClangASTContext::IsPointerOrReferenceType( lldb::opaque_compiler_type_t type, CompilerType *pointee_type) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { default: break; case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCClass: return true; } return false; case clang::Type::ObjCObjectPointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type) ->getPointeeType()); return true; case clang::Type::BlockPointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getPointeeType()); return true; case clang::Type::Pointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getPointeeType()); return true; case clang::Type::MemberPointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getPointeeType()); return true; case clang::Type::LValueReference: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->desugar()); return true; case clang::Type::RValueReference: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->desugar()); return true; case clang::Type::Typedef: return IsPointerOrReferenceType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), pointee_type); case clang::Type::Auto: return IsPointerOrReferenceType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), pointee_type); case clang::Type::Elaborated: return IsPointerOrReferenceType( llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), pointee_type); case clang::Type::Paren: return IsPointerOrReferenceType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), pointee_type); default: break; } } if (pointee_type) pointee_type->Clear(); return false; } bool ClangASTContext::IsReferenceType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type, bool *is_rvalue) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::LValueReference: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->desugar()); if (is_rvalue) *is_rvalue = false; return true; case clang::Type::RValueReference: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->desugar()); if (is_rvalue) *is_rvalue = true; return true; case clang::Type::Typedef: return IsReferenceType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), pointee_type, is_rvalue); case clang::Type::Auto: return IsReferenceType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), pointee_type, is_rvalue); case clang::Type::Elaborated: return IsReferenceType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), pointee_type, is_rvalue); case clang::Type::Paren: return IsReferenceType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), pointee_type, is_rvalue); default: break; } } if (pointee_type) pointee_type->Clear(); return false; } bool ClangASTContext::IsFloatingPointType(lldb::opaque_compiler_type_t type, uint32_t &count, bool &is_complex) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); if (const clang::BuiltinType *BT = llvm::dyn_cast( qual_type->getCanonicalTypeInternal())) { clang::BuiltinType::Kind kind = BT->getKind(); if (kind >= clang::BuiltinType::Float && kind <= clang::BuiltinType::LongDouble) { count = 1; is_complex = false; return true; } } else if (const clang::ComplexType *CT = llvm::dyn_cast( qual_type->getCanonicalTypeInternal())) { if (IsFloatingPointType(CT->getElementType().getAsOpaquePtr(), count, is_complex)) { count = 2; is_complex = true; return true; } } else if (const clang::VectorType *VT = llvm::dyn_cast( qual_type->getCanonicalTypeInternal())) { if (IsFloatingPointType(VT->getElementType().getAsOpaquePtr(), count, is_complex)) { count = VT->getNumElements(); is_complex = false; return true; } } } count = 0; is_complex = false; return false; } bool ClangASTContext::IsDefined(lldb::opaque_compiler_type_t type) { if (!type) return false; clang::QualType qual_type(GetQualType(type)); const clang::TagType *tag_type = llvm::dyn_cast(qual_type.getTypePtr()); if (tag_type) { clang::TagDecl *tag_decl = tag_type->getDecl(); if (tag_decl) return tag_decl->isCompleteDefinition(); return false; } else { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) return class_interface_decl->getDefinition() != nullptr; return false; } } return true; } bool ClangASTContext::IsObjCClassType(const CompilerType &type) { if (type) { clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type)); const clang::ObjCObjectPointerType *obj_pointer_type = llvm::dyn_cast(qual_type); if (obj_pointer_type) return obj_pointer_type->isObjCClassType(); } return false; } bool ClangASTContext::IsObjCObjectOrInterfaceType(const CompilerType &type) { if (ClangUtil::IsClangType(type)) return ClangUtil::GetCanonicalQualType(type)->isObjCObjectOrInterfaceType(); return false; } bool ClangASTContext::IsClassType(lldb::opaque_compiler_type_t type) { if (!type) return false; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); return (type_class == clang::Type::Record); } bool ClangASTContext::IsEnumType(lldb::opaque_compiler_type_t type) { if (!type) return false; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); return (type_class == clang::Type::Enum); } bool ClangASTContext::IsPolymorphicClass(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); if (record_decl) { const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) return cxx_record_decl->isPolymorphic(); } } break; default: break; } } return false; } bool ClangASTContext::IsPossibleDynamicType(lldb::opaque_compiler_type_t type, CompilerType *dynamic_pointee_type, bool check_cplusplus, bool check_objc) { clang::QualType pointee_qual_type; if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); bool success = false; const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Builtin: if (check_objc && llvm::cast(qual_type)->getKind() == clang::BuiltinType::ObjCId) { if (dynamic_pointee_type) dynamic_pointee_type->SetCompilerType(this, type); return true; } break; case clang::Type::ObjCObjectPointer: if (check_objc) { if (auto objc_pointee_type = qual_type->getPointeeType().getTypePtrOrNull()) { if (auto objc_object_type = llvm::dyn_cast_or_null( objc_pointee_type)) { if (objc_object_type->isObjCClass()) return false; } } if (dynamic_pointee_type) dynamic_pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type) ->getPointeeType()); return true; } break; case clang::Type::Pointer: pointee_qual_type = llvm::cast(qual_type)->getPointeeType(); success = true; break; case clang::Type::LValueReference: case clang::Type::RValueReference: pointee_qual_type = llvm::cast(qual_type)->getPointeeType(); success = true; break; case clang::Type::Typedef: return IsPossibleDynamicType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), dynamic_pointee_type, check_cplusplus, check_objc); case clang::Type::Auto: return IsPossibleDynamicType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), dynamic_pointee_type, check_cplusplus, check_objc); case clang::Type::Elaborated: return IsPossibleDynamicType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), dynamic_pointee_type, check_cplusplus, check_objc); case clang::Type::Paren: return IsPossibleDynamicType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), dynamic_pointee_type, check_cplusplus, check_objc); default: break; } if (success) { // Check to make sure what we are pointing too is a possible dynamic C++ // type // We currently accept any "void *" (in case we have a class that has been // watered down to an opaque pointer) and virtual C++ classes. const clang::Type::TypeClass pointee_type_class = pointee_qual_type.getCanonicalType()->getTypeClass(); switch (pointee_type_class) { case clang::Type::Builtin: switch (llvm::cast(pointee_qual_type)->getKind()) { case clang::BuiltinType::UnknownAny: case clang::BuiltinType::Void: if (dynamic_pointee_type) dynamic_pointee_type->SetCompilerType(getASTContext(), pointee_qual_type); return true; default: break; } break; case clang::Type::Record: if (check_cplusplus) { clang::CXXRecordDecl *cxx_record_decl = pointee_qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { bool is_complete = cxx_record_decl->isCompleteDefinition(); if (is_complete) success = cxx_record_decl->isDynamicClass(); else { ClangASTMetadata *metadata = ClangASTContext::GetMetadata( getASTContext(), cxx_record_decl); if (metadata) success = metadata->GetIsDynamicCXXType(); else { is_complete = CompilerType(getASTContext(), pointee_qual_type) .GetCompleteType(); if (is_complete) success = cxx_record_decl->isDynamicClass(); else success = false; } } if (success) { if (dynamic_pointee_type) dynamic_pointee_type->SetCompilerType(getASTContext(), pointee_qual_type); return true; } } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (check_objc) { if (dynamic_pointee_type) dynamic_pointee_type->SetCompilerType(getASTContext(), pointee_qual_type); return true; } break; default: break; } } } if (dynamic_pointee_type) dynamic_pointee_type->Clear(); return false; } bool ClangASTContext::IsScalarType(lldb::opaque_compiler_type_t type) { if (!type) return false; return (GetTypeInfo(type, nullptr) & eTypeIsScalar) != 0; } bool ClangASTContext::IsTypedefType(lldb::opaque_compiler_type_t type) { if (!type) return false; return GetQualType(type)->getTypeClass() == clang::Type::Typedef; } bool ClangASTContext::IsVoidType(lldb::opaque_compiler_type_t type) { if (!type) return false; return GetCanonicalQualType(type)->isVoidType(); } bool ClangASTContext::SupportsLanguage(lldb::LanguageType language) { return ClangASTContextSupportsLanguage(language); } bool ClangASTContext::GetCXXClassName(const CompilerType &type, std::string &class_name) { if (type) { clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type)); if (!qual_type.isNull()) { clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { class_name.assign(cxx_record_decl->getIdentifier()->getNameStart()); return true; } } } class_name.clear(); return false; } bool ClangASTContext::IsCXXClassType(const CompilerType &type) { if (!type) return false; clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type)); if (!qual_type.isNull() && qual_type->getAsCXXRecordDecl() != nullptr) return true; return false; } bool ClangASTContext::IsBeingDefined(lldb::opaque_compiler_type_t type) { if (!type) return false; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::TagType *tag_type = llvm::dyn_cast(qual_type); if (tag_type) return tag_type->isBeingDefined(); return false; } bool ClangASTContext::IsObjCObjectPointerType(const CompilerType &type, CompilerType *class_type_ptr) { if (!type) return false; clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type)); if (!qual_type.isNull() && qual_type->isObjCObjectPointerType()) { if (class_type_ptr) { if (!qual_type->isObjCClassType() && !qual_type->isObjCIdType()) { const clang::ObjCObjectPointerType *obj_pointer_type = llvm::dyn_cast(qual_type); if (obj_pointer_type == nullptr) class_type_ptr->Clear(); else class_type_ptr->SetCompilerType( type.GetTypeSystem(), clang::QualType(obj_pointer_type->getInterfaceType(), 0) .getAsOpaquePtr()); } } return true; } if (class_type_ptr) class_type_ptr->Clear(); return false; } bool ClangASTContext::GetObjCClassName(const CompilerType &type, std::string &class_name) { if (!type) return false; clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type)); const clang::ObjCObjectType *object_type = llvm::dyn_cast(qual_type); if (object_type) { const clang::ObjCInterfaceDecl *interface = object_type->getInterface(); if (interface) { class_name = interface->getNameAsString(); return true; } } return false; } //---------------------------------------------------------------------- // Type Completion //---------------------------------------------------------------------- bool ClangASTContext::GetCompleteType(lldb::opaque_compiler_type_t type) { if (!type) return false; const bool allow_completion = true; return GetCompleteQualType(getASTContext(), GetQualType(type), allow_completion); } ConstString ClangASTContext::GetTypeName(lldb::opaque_compiler_type_t type) { std::string type_name; if (type) { clang::PrintingPolicy printing_policy(getASTContext()->getPrintingPolicy()); clang::QualType qual_type(GetQualType(type)); printing_policy.SuppressTagKeyword = true; const clang::TypedefType *typedef_type = qual_type->getAs(); if (typedef_type) { const clang::TypedefNameDecl *typedef_decl = typedef_type->getDecl(); type_name = typedef_decl->getQualifiedNameAsString(); } else { type_name = qual_type.getAsString(printing_policy); } } return ConstString(type_name); } uint32_t ClangASTContext::GetTypeInfo(lldb::opaque_compiler_type_t type, CompilerType *pointee_or_element_clang_type) { if (!type) return 0; if (pointee_or_element_clang_type) pointee_or_element_clang_type->Clear(); clang::QualType qual_type(GetQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Builtin: { const clang::BuiltinType *builtin_type = llvm::dyn_cast( qual_type->getCanonicalTypeInternal()); uint32_t builtin_type_flags = eTypeIsBuiltIn | eTypeHasValue; switch (builtin_type->getKind()) { case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCClass: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), getASTContext()->ObjCBuiltinClassTy); builtin_type_flags |= eTypeIsPointer | eTypeIsObjC; break; case clang::BuiltinType::ObjCSel: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType(getASTContext(), getASTContext()->CharTy); builtin_type_flags |= eTypeIsPointer | eTypeIsObjC; break; case clang::BuiltinType::Bool: case clang::BuiltinType::Char_U: case clang::BuiltinType::UChar: case clang::BuiltinType::WChar_U: case clang::BuiltinType::Char16: case clang::BuiltinType::Char32: case clang::BuiltinType::UShort: case clang::BuiltinType::UInt: case clang::BuiltinType::ULong: case clang::BuiltinType::ULongLong: case clang::BuiltinType::UInt128: case clang::BuiltinType::Char_S: case clang::BuiltinType::SChar: case clang::BuiltinType::WChar_S: case clang::BuiltinType::Short: case clang::BuiltinType::Int: case clang::BuiltinType::Long: case clang::BuiltinType::LongLong: case clang::BuiltinType::Int128: case clang::BuiltinType::Float: case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: builtin_type_flags |= eTypeIsScalar; if (builtin_type->isInteger()) { builtin_type_flags |= eTypeIsInteger; if (builtin_type->isSignedInteger()) builtin_type_flags |= eTypeIsSigned; } else if (builtin_type->isFloatingPoint()) builtin_type_flags |= eTypeIsFloat; break; default: break; } return builtin_type_flags; } case clang::Type::BlockPointer: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), qual_type->getPointeeType()); return eTypeIsPointer | eTypeHasChildren | eTypeIsBlock; case clang::Type::Complex: { uint32_t complex_type_flags = eTypeIsBuiltIn | eTypeHasValue | eTypeIsComplex; const clang::ComplexType *complex_type = llvm::dyn_cast( qual_type->getCanonicalTypeInternal()); if (complex_type) { clang::QualType complex_element_type(complex_type->getElementType()); if (complex_element_type->isIntegerType()) complex_type_flags |= eTypeIsFloat; else if (complex_element_type->isFloatingType()) complex_type_flags |= eTypeIsInteger; } return complex_type_flags; } break; case clang::Type::ConstantArray: case clang::Type::DependentSizedArray: case clang::Type::IncompleteArray: case clang::Type::VariableArray: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), llvm::cast(qual_type.getTypePtr()) ->getElementType()); return eTypeHasChildren | eTypeIsArray; case clang::Type::DependentName: return 0; case clang::Type::DependentSizedExtVector: return eTypeHasChildren | eTypeIsVector; case clang::Type::DependentTemplateSpecialization: return eTypeIsTemplate; case clang::Type::Decltype: return 0; case clang::Type::Enum: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getDecl()->getIntegerType()); return eTypeIsEnumeration | eTypeHasValue; case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetTypeInfo(pointee_or_element_clang_type); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetTypeInfo(pointee_or_element_clang_type); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetTypeInfo(pointee_or_element_clang_type); case clang::Type::FunctionProto: return eTypeIsFuncPrototype | eTypeHasValue; case clang::Type::FunctionNoProto: return eTypeIsFuncPrototype | eTypeHasValue; case clang::Type::InjectedClassName: return 0; case clang::Type::LValueReference: case clang::Type::RValueReference: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), llvm::cast(qual_type.getTypePtr()) ->getPointeeType()); return eTypeHasChildren | eTypeIsReference | eTypeHasValue; case clang::Type::MemberPointer: return eTypeIsPointer | eTypeIsMember | eTypeHasValue; case clang::Type::ObjCObjectPointer: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), qual_type->getPointeeType()); return eTypeHasChildren | eTypeIsObjC | eTypeIsClass | eTypeIsPointer | eTypeHasValue; case clang::Type::ObjCObject: return eTypeHasChildren | eTypeIsObjC | eTypeIsClass; case clang::Type::ObjCInterface: return eTypeHasChildren | eTypeIsObjC | eTypeIsClass; case clang::Type::Pointer: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), qual_type->getPointeeType()); return eTypeHasChildren | eTypeIsPointer | eTypeHasValue; case clang::Type::Record: if (qual_type->getAsCXXRecordDecl()) return eTypeHasChildren | eTypeIsClass | eTypeIsCPlusPlus; else return eTypeHasChildren | eTypeIsStructUnion; break; case clang::Type::SubstTemplateTypeParm: return eTypeIsTemplate; case clang::Type::TemplateTypeParm: return eTypeIsTemplate; case clang::Type::TemplateSpecialization: return eTypeIsTemplate; case clang::Type::Typedef: return eTypeIsTypedef | CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetTypeInfo(pointee_or_element_clang_type); case clang::Type::TypeOfExpr: return 0; case clang::Type::TypeOf: return 0; case clang::Type::UnresolvedUsing: return 0; case clang::Type::ExtVector: case clang::Type::Vector: { uint32_t vector_type_flags = eTypeHasChildren | eTypeIsVector; const clang::VectorType *vector_type = llvm::dyn_cast( qual_type->getCanonicalTypeInternal()); if (vector_type) { if (vector_type->isIntegerType()) vector_type_flags |= eTypeIsFloat; else if (vector_type->isFloatingType()) vector_type_flags |= eTypeIsInteger; } return vector_type_flags; } default: return 0; } return 0; } lldb::LanguageType ClangASTContext::GetMinimumLanguage(lldb::opaque_compiler_type_t type) { if (!type) return lldb::eLanguageTypeC; // If the type is a reference, then resolve it to what it refers to first: clang::QualType qual_type(GetCanonicalQualType(type).getNonReferenceType()); if (qual_type->isAnyPointerType()) { if (qual_type->isObjCObjectPointerType()) return lldb::eLanguageTypeObjC; clang::QualType pointee_type(qual_type->getPointeeType()); if (pointee_type->getPointeeCXXRecordDecl() != nullptr) return lldb::eLanguageTypeC_plus_plus; if (pointee_type->isObjCObjectOrInterfaceType()) return lldb::eLanguageTypeObjC; if (pointee_type->isObjCClassType()) return lldb::eLanguageTypeObjC; if (pointee_type.getTypePtr() == getASTContext()->ObjCBuiltinIdTy.getTypePtr()) return lldb::eLanguageTypeObjC; } else { if (qual_type->isObjCObjectOrInterfaceType()) return lldb::eLanguageTypeObjC; if (qual_type->getAsCXXRecordDecl()) return lldb::eLanguageTypeC_plus_plus; switch (qual_type->getTypeClass()) { default: break; case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { default: case clang::BuiltinType::Void: case clang::BuiltinType::Bool: case clang::BuiltinType::Char_U: case clang::BuiltinType::UChar: case clang::BuiltinType::WChar_U: case clang::BuiltinType::Char16: case clang::BuiltinType::Char32: case clang::BuiltinType::UShort: case clang::BuiltinType::UInt: case clang::BuiltinType::ULong: case clang::BuiltinType::ULongLong: case clang::BuiltinType::UInt128: case clang::BuiltinType::Char_S: case clang::BuiltinType::SChar: case clang::BuiltinType::WChar_S: case clang::BuiltinType::Short: case clang::BuiltinType::Int: case clang::BuiltinType::Long: case clang::BuiltinType::LongLong: case clang::BuiltinType::Int128: case clang::BuiltinType::Float: case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: break; case clang::BuiltinType::NullPtr: return eLanguageTypeC_plus_plus; case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCClass: case clang::BuiltinType::ObjCSel: return eLanguageTypeObjC; case clang::BuiltinType::Dependent: case clang::BuiltinType::Overload: case clang::BuiltinType::BoundMember: case clang::BuiltinType::UnknownAny: break; } break; case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetMinimumLanguage(); } } return lldb::eLanguageTypeC; } lldb::TypeClass ClangASTContext::GetTypeClass(lldb::opaque_compiler_type_t type) { if (!type) return lldb::eTypeClassInvalid; clang::QualType qual_type(GetQualType(type)); switch (qual_type->getTypeClass()) { case clang::Type::UnaryTransform: break; case clang::Type::FunctionNoProto: return lldb::eTypeClassFunction; case clang::Type::FunctionProto: return lldb::eTypeClassFunction; case clang::Type::IncompleteArray: return lldb::eTypeClassArray; case clang::Type::VariableArray: return lldb::eTypeClassArray; case clang::Type::ConstantArray: return lldb::eTypeClassArray; case clang::Type::DependentSizedArray: return lldb::eTypeClassArray; case clang::Type::DependentSizedExtVector: return lldb::eTypeClassVector; case clang::Type::ExtVector: return lldb::eTypeClassVector; case clang::Type::Vector: return lldb::eTypeClassVector; case clang::Type::Builtin: return lldb::eTypeClassBuiltin; case clang::Type::ObjCObjectPointer: return lldb::eTypeClassObjCObjectPointer; case clang::Type::BlockPointer: return lldb::eTypeClassBlockPointer; case clang::Type::Pointer: return lldb::eTypeClassPointer; case clang::Type::LValueReference: return lldb::eTypeClassReference; case clang::Type::RValueReference: return lldb::eTypeClassReference; case clang::Type::MemberPointer: return lldb::eTypeClassMemberPointer; case clang::Type::Complex: if (qual_type->isComplexType()) return lldb::eTypeClassComplexFloat; else return lldb::eTypeClassComplexInteger; case clang::Type::ObjCObject: return lldb::eTypeClassObjCObject; case clang::Type::ObjCInterface: return lldb::eTypeClassObjCInterface; case clang::Type::Record: { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); if (record_decl->isUnion()) return lldb::eTypeClassUnion; else if (record_decl->isStruct()) return lldb::eTypeClassStruct; else return lldb::eTypeClassClass; } break; case clang::Type::Enum: return lldb::eTypeClassEnumeration; case clang::Type::Typedef: return lldb::eTypeClassTypedef; case clang::Type::UnresolvedUsing: break; case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetTypeClass(); case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetTypeClass(); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetTypeClass(); case clang::Type::Attributed: break; case clang::Type::TemplateTypeParm: break; case clang::Type::SubstTemplateTypeParm: break; case clang::Type::SubstTemplateTypeParmPack: break; case clang::Type::InjectedClassName: break; case clang::Type::DependentName: break; case clang::Type::DependentTemplateSpecialization: break; case clang::Type::PackExpansion: break; case clang::Type::TypeOfExpr: break; case clang::Type::TypeOf: break; case clang::Type::Decltype: break; case clang::Type::TemplateSpecialization: break; case clang::Type::Atomic: break; case clang::Type::Pipe: break; // pointer type decayed from an array or function type. case clang::Type::Decayed: break; case clang::Type::Adjusted: break; case clang::Type::ObjCTypeParam: break; } // We don't know hot to display this type... return lldb::eTypeClassOther; } unsigned ClangASTContext::GetTypeQualifiers(lldb::opaque_compiler_type_t type) { if (type) return GetQualType(type).getQualifiers().getCVRQualifiers(); return 0; } //---------------------------------------------------------------------- // Creating related types //---------------------------------------------------------------------- CompilerType ClangASTContext::GetArrayElementType(lldb::opaque_compiler_type_t type, uint64_t *stride) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type *array_eletype = qual_type.getTypePtr()->getArrayElementTypeNoTypeQual(); if (!array_eletype) return CompilerType(); CompilerType element_type(getASTContext(), array_eletype->getCanonicalTypeUnqualified()); // TODO: the real stride will be >= this value.. find the real one! if (stride) *stride = element_type.GetByteSize(nullptr); return element_type; } return CompilerType(); } CompilerType ClangASTContext::GetArrayType(lldb::opaque_compiler_type_t type, uint64_t size) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); if (clang::ASTContext *ast_ctx = getASTContext()) { if (size != 0) return CompilerType( ast_ctx, ast_ctx->getConstantArrayType( qual_type, llvm::APInt(64, size), clang::ArrayType::ArraySizeModifier::Normal, 0)); else return CompilerType( ast_ctx, ast_ctx->getIncompleteArrayType( qual_type, clang::ArrayType::ArraySizeModifier::Normal, 0)); } } return CompilerType(); } CompilerType ClangASTContext::GetCanonicalType(lldb::opaque_compiler_type_t type) { if (type) return CompilerType(getASTContext(), GetCanonicalQualType(type)); return CompilerType(); } static clang::QualType GetFullyUnqualifiedType_Impl(clang::ASTContext *ast, clang::QualType qual_type) { if (qual_type->isPointerType()) qual_type = ast->getPointerType( GetFullyUnqualifiedType_Impl(ast, qual_type->getPointeeType())); else qual_type = qual_type.getUnqualifiedType(); qual_type.removeLocalConst(); qual_type.removeLocalRestrict(); qual_type.removeLocalVolatile(); return qual_type; } CompilerType ClangASTContext::GetFullyUnqualifiedType(lldb::opaque_compiler_type_t type) { if (type) return CompilerType( getASTContext(), GetFullyUnqualifiedType_Impl(getASTContext(), GetQualType(type))); return CompilerType(); } int ClangASTContext::GetFunctionArgumentCount( lldb::opaque_compiler_type_t type) { if (type) { const clang::FunctionProtoType *func = llvm::dyn_cast(GetCanonicalQualType(type)); if (func) return func->getNumParams(); } return -1; } CompilerType ClangASTContext::GetFunctionArgumentTypeAtIndex( lldb::opaque_compiler_type_t type, size_t idx) { if (type) { const clang::FunctionProtoType *func = llvm::dyn_cast(GetQualType(type)); if (func) { const uint32_t num_args = func->getNumParams(); if (idx < num_args) return CompilerType(getASTContext(), func->getParamType(idx)); } } return CompilerType(); } CompilerType ClangASTContext::GetFunctionReturnType(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetQualType(type)); const clang::FunctionProtoType *func = llvm::dyn_cast(qual_type.getTypePtr()); if (func) return CompilerType(getASTContext(), func->getReturnType()); } return CompilerType(); } size_t ClangASTContext::GetNumMemberFunctions(lldb::opaque_compiler_type_t type) { size_t num_functions = 0; if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); switch (qual_type->getTypeClass()) { case clang::Type::Record: if (GetCompleteQualType(getASTContext(), qual_type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) num_functions = std::distance(cxx_record_decl->method_begin(), cxx_record_decl->method_end()); } break; case clang::Type::ObjCObjectPointer: { const clang::ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); const clang::ObjCInterfaceType *objc_interface_type = objc_class_type->getInterfaceType(); if (objc_interface_type && GetCompleteType((lldb::opaque_compiler_type_t)objc_interface_type)) { clang::ObjCInterfaceDecl *class_interface_decl = objc_interface_type->getDecl(); if (class_interface_decl) { num_functions = std::distance(class_interface_decl->meth_begin(), class_interface_decl->meth_end()); } } break; } case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) num_functions = std::distance(class_interface_decl->meth_begin(), class_interface_decl->meth_end()); } } break; case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetNumMemberFunctions(); case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetNumMemberFunctions(); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetNumMemberFunctions(); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetNumMemberFunctions(); default: break; } } return num_functions; } TypeMemberFunctionImpl ClangASTContext::GetMemberFunctionAtIndex(lldb::opaque_compiler_type_t type, size_t idx) { std::string name; MemberFunctionKind kind(MemberFunctionKind::eMemberFunctionKindUnknown); CompilerType clang_type; CompilerDecl clang_decl; if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); switch (qual_type->getTypeClass()) { case clang::Type::Record: if (GetCompleteQualType(getASTContext(), qual_type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) { auto method_iter = cxx_record_decl->method_begin(); auto method_end = cxx_record_decl->method_end(); if (idx < static_cast(std::distance(method_iter, method_end))) { std::advance(method_iter, idx); clang::CXXMethodDecl *cxx_method_decl = method_iter->getCanonicalDecl(); if (cxx_method_decl) { name = cxx_method_decl->getDeclName().getAsString(); if (cxx_method_decl->isStatic()) kind = lldb::eMemberFunctionKindStaticMethod; else if (llvm::isa(cxx_method_decl)) kind = lldb::eMemberFunctionKindConstructor; else if (llvm::isa(cxx_method_decl)) kind = lldb::eMemberFunctionKindDestructor; else kind = lldb::eMemberFunctionKindInstanceMethod; clang_type = CompilerType( this, cxx_method_decl->getType().getAsOpaquePtr()); clang_decl = CompilerDecl(this, cxx_method_decl); } } } } break; case clang::Type::ObjCObjectPointer: { const clang::ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); const clang::ObjCInterfaceType *objc_interface_type = objc_class_type->getInterfaceType(); if (objc_interface_type && GetCompleteType((lldb::opaque_compiler_type_t)objc_interface_type)) { clang::ObjCInterfaceDecl *class_interface_decl = objc_interface_type->getDecl(); if (class_interface_decl) { auto method_iter = class_interface_decl->meth_begin(); auto method_end = class_interface_decl->meth_end(); if (idx < static_cast(std::distance(method_iter, method_end))) { std::advance(method_iter, idx); clang::ObjCMethodDecl *objc_method_decl = method_iter->getCanonicalDecl(); if (objc_method_decl) { clang_decl = CompilerDecl(this, objc_method_decl); name = objc_method_decl->getSelector().getAsString(); if (objc_method_decl->isClassMethod()) kind = lldb::eMemberFunctionKindStaticMethod; else kind = lldb::eMemberFunctionKindInstanceMethod; } } } } break; } case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { auto method_iter = class_interface_decl->meth_begin(); auto method_end = class_interface_decl->meth_end(); if (idx < static_cast(std::distance(method_iter, method_end))) { std::advance(method_iter, idx); clang::ObjCMethodDecl *objc_method_decl = method_iter->getCanonicalDecl(); if (objc_method_decl) { clang_decl = CompilerDecl(this, objc_method_decl); name = objc_method_decl->getSelector().getAsString(); if (objc_method_decl->isClassMethod()) kind = lldb::eMemberFunctionKindStaticMethod; else kind = lldb::eMemberFunctionKindInstanceMethod; } } } } } break; case clang::Type::Typedef: return GetMemberFunctionAtIndex(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), idx); case clang::Type::Auto: return GetMemberFunctionAtIndex(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), idx); case clang::Type::Elaborated: return GetMemberFunctionAtIndex( llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), idx); case clang::Type::Paren: return GetMemberFunctionAtIndex( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), idx); default: break; } } if (kind == eMemberFunctionKindUnknown) return TypeMemberFunctionImpl(); else return TypeMemberFunctionImpl(clang_type, clang_decl, name, kind); } CompilerType ClangASTContext::GetNonReferenceType(lldb::opaque_compiler_type_t type) { if (type) return CompilerType(getASTContext(), GetQualType(type).getNonReferenceType()); return CompilerType(); } CompilerType ClangASTContext::CreateTypedefType( const CompilerType &type, const char *typedef_name, const CompilerDeclContext &compiler_decl_ctx) { if (type && typedef_name && typedef_name[0]) { ClangASTContext *ast = llvm::dyn_cast(type.GetTypeSystem()); if (!ast) return CompilerType(); clang::ASTContext *clang_ast = ast->getASTContext(); clang::QualType qual_type(ClangUtil::GetQualType(type)); clang::DeclContext *decl_ctx = ClangASTContext::DeclContextGetAsDeclContext(compiler_decl_ctx); if (decl_ctx == nullptr) decl_ctx = ast->getASTContext()->getTranslationUnitDecl(); clang::TypedefDecl *decl = clang::TypedefDecl::Create( *clang_ast, decl_ctx, clang::SourceLocation(), clang::SourceLocation(), &clang_ast->Idents.get(typedef_name), clang_ast->getTrivialTypeSourceInfo(qual_type)); decl->setAccess(clang::AS_public); // TODO respect proper access specifier // Get a uniqued clang::QualType for the typedef decl type return CompilerType(clang_ast, clang_ast->getTypedefType(decl)); } return CompilerType(); } CompilerType ClangASTContext::GetPointeeType(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetQualType(type)); return CompilerType(getASTContext(), qual_type.getTypePtr()->getPointeeType()); } return CompilerType(); } CompilerType ClangASTContext::GetPointerType(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::ObjCObject: case clang::Type::ObjCInterface: return CompilerType(getASTContext(), getASTContext()->getObjCObjectPointerType(qual_type)); default: return CompilerType(getASTContext(), getASTContext()->getPointerType(qual_type)); } } return CompilerType(); } CompilerType ClangASTContext::GetLValueReferenceType(lldb::opaque_compiler_type_t type) { if (type) return CompilerType(this, getASTContext() ->getLValueReferenceType(GetQualType(type)) .getAsOpaquePtr()); else return CompilerType(); } CompilerType ClangASTContext::GetRValueReferenceType(lldb::opaque_compiler_type_t type) { if (type) return CompilerType(this, getASTContext() ->getRValueReferenceType(GetQualType(type)) .getAsOpaquePtr()); else return CompilerType(); } CompilerType ClangASTContext::AddConstModifier(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType result(GetQualType(type)); result.addConst(); return CompilerType(this, result.getAsOpaquePtr()); } return CompilerType(); } CompilerType ClangASTContext::AddVolatileModifier(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType result(GetQualType(type)); result.addVolatile(); return CompilerType(this, result.getAsOpaquePtr()); } return CompilerType(); } CompilerType ClangASTContext::AddRestrictModifier(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType result(GetQualType(type)); result.addRestrict(); return CompilerType(this, result.getAsOpaquePtr()); } return CompilerType(); } CompilerType ClangASTContext::CreateTypedef(lldb::opaque_compiler_type_t type, const char *typedef_name, const CompilerDeclContext &compiler_decl_ctx) { if (type) { clang::ASTContext *clang_ast = getASTContext(); clang::QualType qual_type(GetQualType(type)); clang::DeclContext *decl_ctx = ClangASTContext::DeclContextGetAsDeclContext(compiler_decl_ctx); if (decl_ctx == nullptr) decl_ctx = getASTContext()->getTranslationUnitDecl(); clang::TypedefDecl *decl = clang::TypedefDecl::Create( *clang_ast, decl_ctx, clang::SourceLocation(), clang::SourceLocation(), &clang_ast->Idents.get(typedef_name), clang_ast->getTrivialTypeSourceInfo(qual_type)); clang::TagDecl *tdecl = nullptr; if (!qual_type.isNull()) { if (const clang::RecordType *rt = qual_type->getAs()) tdecl = rt->getDecl(); if (const clang::EnumType *et = qual_type->getAs()) tdecl = et->getDecl(); } // Check whether this declaration is an anonymous struct, union, or enum, // hidden behind a typedef. If so, we // try to check whether we have a typedef tag to attach to the original // record declaration if (tdecl && !tdecl->getIdentifier() && !tdecl->getTypedefNameForAnonDecl()) tdecl->setTypedefNameForAnonDecl(decl); decl->setAccess(clang::AS_public); // TODO respect proper access specifier // Get a uniqued clang::QualType for the typedef decl type return CompilerType(this, clang_ast->getTypedefType(decl).getAsOpaquePtr()); } return CompilerType(); } CompilerType ClangASTContext::GetTypedefedType(lldb::opaque_compiler_type_t type) { if (type) { const clang::TypedefType *typedef_type = llvm::dyn_cast(GetQualType(type)); if (typedef_type) return CompilerType(getASTContext(), typedef_type->getDecl()->getUnderlyingType()); } return CompilerType(); } //---------------------------------------------------------------------- // Create related types using the current type's AST //---------------------------------------------------------------------- CompilerType ClangASTContext::GetBasicTypeFromAST(lldb::BasicType basic_type) { return ClangASTContext::GetBasicType(getASTContext(), basic_type); } //---------------------------------------------------------------------- // Exploring the type //---------------------------------------------------------------------- uint64_t ClangASTContext::GetBitSize(lldb::opaque_compiler_type_t type, ExecutionContextScope *exe_scope) { if (GetCompleteType(type)) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) return getASTContext()->getTypeSize(qual_type); else return 0; break; case clang::Type::ObjCInterface: case clang::Type::ObjCObject: { ExecutionContext exe_ctx(exe_scope); Process *process = exe_ctx.GetProcessPtr(); if (process) { ObjCLanguageRuntime *objc_runtime = process->GetObjCLanguageRuntime(); if (objc_runtime) { uint64_t bit_size = 0; if (objc_runtime->GetTypeBitSize( CompilerType(getASTContext(), qual_type), bit_size)) return bit_size; } } else { static bool g_printed = false; if (!g_printed) { StreamString s; DumpTypeDescription(type, &s); llvm::outs() << "warning: trying to determine the size of type "; llvm::outs() << s.GetString() << "\n"; llvm::outs() << "without a valid ExecutionContext. this is not " "reliable. please file a bug against LLDB.\n"; llvm::outs() << "backtrace:\n"; llvm::sys::PrintStackTrace(llvm::outs()); llvm::outs() << "\n"; g_printed = true; } } } LLVM_FALLTHROUGH; default: const uint32_t bit_size = getASTContext()->getTypeSize(qual_type); if (bit_size == 0) { if (qual_type->isIncompleteArrayType()) return getASTContext()->getTypeSize( qual_type->getArrayElementTypeNoTypeQual() ->getCanonicalTypeUnqualified()); } if (qual_type->isObjCObjectOrInterfaceType()) return bit_size + getASTContext()->getTypeSize( getASTContext()->ObjCBuiltinClassTy); return bit_size; } } return 0; } size_t ClangASTContext::GetTypeBitAlign(lldb::opaque_compiler_type_t type) { if (GetCompleteType(type)) return getASTContext()->getTypeAlign(GetQualType(type)); return 0; } lldb::Encoding ClangASTContext::GetEncoding(lldb::opaque_compiler_type_t type, uint64_t &count) { if (!type) return lldb::eEncodingInvalid; count = 1; clang::QualType qual_type(GetCanonicalQualType(type)); switch (qual_type->getTypeClass()) { case clang::Type::UnaryTransform: break; case clang::Type::FunctionNoProto: case clang::Type::FunctionProto: break; case clang::Type::IncompleteArray: case clang::Type::VariableArray: break; case clang::Type::ConstantArray: break; case clang::Type::ExtVector: case clang::Type::Vector: // TODO: Set this to more than one??? break; case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { case clang::BuiltinType::Void: break; case clang::BuiltinType::Bool: case clang::BuiltinType::Char_S: case clang::BuiltinType::SChar: case clang::BuiltinType::WChar_S: case clang::BuiltinType::Char16: case clang::BuiltinType::Char32: case clang::BuiltinType::Short: case clang::BuiltinType::Int: case clang::BuiltinType::Long: case clang::BuiltinType::LongLong: case clang::BuiltinType::Int128: return lldb::eEncodingSint; case clang::BuiltinType::Char_U: case clang::BuiltinType::UChar: case clang::BuiltinType::WChar_U: case clang::BuiltinType::UShort: case clang::BuiltinType::UInt: case clang::BuiltinType::ULong: case clang::BuiltinType::ULongLong: case clang::BuiltinType::UInt128: return lldb::eEncodingUint; case clang::BuiltinType::Half: case clang::BuiltinType::Float: case clang::BuiltinType::Float128: case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: return lldb::eEncodingIEEE754; case clang::BuiltinType::ObjCClass: case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCSel: return lldb::eEncodingUint; case clang::BuiltinType::NullPtr: return lldb::eEncodingUint; case clang::BuiltinType::Kind::ARCUnbridgedCast: case clang::BuiltinType::Kind::BoundMember: case clang::BuiltinType::Kind::BuiltinFn: case clang::BuiltinType::Kind::Dependent: case clang::BuiltinType::Kind::OCLClkEvent: case clang::BuiltinType::Kind::OCLEvent: case clang::BuiltinType::Kind::OCLImage1dRO: case clang::BuiltinType::Kind::OCLImage1dWO: case clang::BuiltinType::Kind::OCLImage1dRW: case clang::BuiltinType::Kind::OCLImage1dArrayRO: case clang::BuiltinType::Kind::OCLImage1dArrayWO: case clang::BuiltinType::Kind::OCLImage1dArrayRW: case clang::BuiltinType::Kind::OCLImage1dBufferRO: case clang::BuiltinType::Kind::OCLImage1dBufferWO: case clang::BuiltinType::Kind::OCLImage1dBufferRW: case clang::BuiltinType::Kind::OCLImage2dRO: case clang::BuiltinType::Kind::OCLImage2dWO: case clang::BuiltinType::Kind::OCLImage2dRW: case clang::BuiltinType::Kind::OCLImage2dArrayRO: case clang::BuiltinType::Kind::OCLImage2dArrayWO: case clang::BuiltinType::Kind::OCLImage2dArrayRW: case clang::BuiltinType::Kind::OCLImage2dArrayDepthRO: case clang::BuiltinType::Kind::OCLImage2dArrayDepthWO: case clang::BuiltinType::Kind::OCLImage2dArrayDepthRW: case clang::BuiltinType::Kind::OCLImage2dArrayMSAARO: case clang::BuiltinType::Kind::OCLImage2dArrayMSAAWO: case clang::BuiltinType::Kind::OCLImage2dArrayMSAARW: case clang::BuiltinType::Kind::OCLImage2dArrayMSAADepthRO: case clang::BuiltinType::Kind::OCLImage2dArrayMSAADepthWO: case clang::BuiltinType::Kind::OCLImage2dArrayMSAADepthRW: case clang::BuiltinType::Kind::OCLImage2dDepthRO: case clang::BuiltinType::Kind::OCLImage2dDepthWO: case clang::BuiltinType::Kind::OCLImage2dDepthRW: case clang::BuiltinType::Kind::OCLImage2dMSAARO: case clang::BuiltinType::Kind::OCLImage2dMSAAWO: case clang::BuiltinType::Kind::OCLImage2dMSAARW: case clang::BuiltinType::Kind::OCLImage2dMSAADepthRO: case clang::BuiltinType::Kind::OCLImage2dMSAADepthWO: case clang::BuiltinType::Kind::OCLImage2dMSAADepthRW: case clang::BuiltinType::Kind::OCLImage3dRO: case clang::BuiltinType::Kind::OCLImage3dWO: case clang::BuiltinType::Kind::OCLImage3dRW: case clang::BuiltinType::Kind::OCLQueue: case clang::BuiltinType::Kind::OCLNDRange: case clang::BuiltinType::Kind::OCLReserveID: case clang::BuiltinType::Kind::OCLSampler: case clang::BuiltinType::Kind::OMPArraySection: case clang::BuiltinType::Kind::Overload: case clang::BuiltinType::Kind::PseudoObject: case clang::BuiltinType::Kind::UnknownAny: break; } break; // All pointer types are represented as unsigned integer encodings. // We may nee to add a eEncodingPointer if we ever need to know the // difference case clang::Type::ObjCObjectPointer: case clang::Type::BlockPointer: case clang::Type::Pointer: case clang::Type::LValueReference: case clang::Type::RValueReference: case clang::Type::MemberPointer: return lldb::eEncodingUint; case clang::Type::Complex: { lldb::Encoding encoding = lldb::eEncodingIEEE754; if (qual_type->isComplexType()) encoding = lldb::eEncodingIEEE754; else { const clang::ComplexType *complex_type = qual_type->getAsComplexIntegerType(); if (complex_type) encoding = CompilerType(getASTContext(), complex_type->getElementType()) .GetEncoding(count); else encoding = lldb::eEncodingSint; } count = 2; return encoding; } case clang::Type::ObjCInterface: break; case clang::Type::Record: break; case clang::Type::Enum: return lldb::eEncodingSint; case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetEncoding(count); case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetEncoding(count); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetEncoding(count); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetEncoding(count); case clang::Type::DependentSizedArray: case clang::Type::DependentSizedExtVector: case clang::Type::UnresolvedUsing: case clang::Type::Attributed: case clang::Type::TemplateTypeParm: case clang::Type::SubstTemplateTypeParm: case clang::Type::SubstTemplateTypeParmPack: case clang::Type::InjectedClassName: case clang::Type::DependentName: case clang::Type::DependentTemplateSpecialization: case clang::Type::PackExpansion: case clang::Type::ObjCObject: case clang::Type::TypeOfExpr: case clang::Type::TypeOf: case clang::Type::Decltype: case clang::Type::TemplateSpecialization: case clang::Type::Atomic: case clang::Type::Adjusted: case clang::Type::Pipe: break; // pointer type decayed from an array or function type. case clang::Type::Decayed: break; case clang::Type::ObjCTypeParam: break; } count = 0; return lldb::eEncodingInvalid; } lldb::Format ClangASTContext::GetFormat(lldb::opaque_compiler_type_t type) { if (!type) return lldb::eFormatDefault; clang::QualType qual_type(GetCanonicalQualType(type)); switch (qual_type->getTypeClass()) { case clang::Type::UnaryTransform: break; case clang::Type::FunctionNoProto: case clang::Type::FunctionProto: break; case clang::Type::IncompleteArray: case clang::Type::VariableArray: break; case clang::Type::ConstantArray: return lldb::eFormatVoid; // no value case clang::Type::ExtVector: case clang::Type::Vector: break; case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { // default: assert(0 && "Unknown builtin type!"); case clang::BuiltinType::UnknownAny: case clang::BuiltinType::Void: case clang::BuiltinType::BoundMember: break; case clang::BuiltinType::Bool: return lldb::eFormatBoolean; case clang::BuiltinType::Char_S: case clang::BuiltinType::SChar: case clang::BuiltinType::WChar_S: case clang::BuiltinType::Char_U: case clang::BuiltinType::UChar: case clang::BuiltinType::WChar_U: return lldb::eFormatChar; case clang::BuiltinType::Char16: return lldb::eFormatUnicode16; case clang::BuiltinType::Char32: return lldb::eFormatUnicode32; case clang::BuiltinType::UShort: return lldb::eFormatUnsigned; case clang::BuiltinType::Short: return lldb::eFormatDecimal; case clang::BuiltinType::UInt: return lldb::eFormatUnsigned; case clang::BuiltinType::Int: return lldb::eFormatDecimal; case clang::BuiltinType::ULong: return lldb::eFormatUnsigned; case clang::BuiltinType::Long: return lldb::eFormatDecimal; case clang::BuiltinType::ULongLong: return lldb::eFormatUnsigned; case clang::BuiltinType::LongLong: return lldb::eFormatDecimal; case clang::BuiltinType::UInt128: return lldb::eFormatUnsigned; case clang::BuiltinType::Int128: return lldb::eFormatDecimal; case clang::BuiltinType::Half: case clang::BuiltinType::Float: case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: return lldb::eFormatFloat; default: return lldb::eFormatHex; } break; case clang::Type::ObjCObjectPointer: return lldb::eFormatHex; case clang::Type::BlockPointer: return lldb::eFormatHex; case clang::Type::Pointer: return lldb::eFormatHex; case clang::Type::LValueReference: case clang::Type::RValueReference: return lldb::eFormatHex; case clang::Type::MemberPointer: break; case clang::Type::Complex: { if (qual_type->isComplexType()) return lldb::eFormatComplex; else return lldb::eFormatComplexInteger; } case clang::Type::ObjCInterface: break; case clang::Type::Record: break; case clang::Type::Enum: return lldb::eFormatEnum; case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetFormat(); case clang::Type::Auto: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetFormat(); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetFormat(); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetFormat(); case clang::Type::DependentSizedArray: case clang::Type::DependentSizedExtVector: case clang::Type::UnresolvedUsing: case clang::Type::Attributed: case clang::Type::TemplateTypeParm: case clang::Type::SubstTemplateTypeParm: case clang::Type::SubstTemplateTypeParmPack: case clang::Type::InjectedClassName: case clang::Type::DependentName: case clang::Type::DependentTemplateSpecialization: case clang::Type::PackExpansion: case clang::Type::ObjCObject: case clang::Type::TypeOfExpr: case clang::Type::TypeOf: case clang::Type::Decltype: case clang::Type::TemplateSpecialization: case clang::Type::Atomic: case clang::Type::Adjusted: case clang::Type::Pipe: break; // pointer type decayed from an array or function type. case clang::Type::Decayed: break; case clang::Type::ObjCTypeParam: break; } // We don't know hot to display this type... return lldb::eFormatBytes; } static bool ObjCDeclHasIVars(clang::ObjCInterfaceDecl *class_interface_decl, bool check_superclass) { while (class_interface_decl) { if (class_interface_decl->ivar_size() > 0) return true; if (check_superclass) class_interface_decl = class_interface_decl->getSuperClass(); else break; } return false; } uint32_t ClangASTContext::GetNumChildren(lldb::opaque_compiler_type_t type, bool omit_empty_base_classes) { if (!type) return 0; uint32_t num_children = 0; clang::QualType qual_type(GetQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { case clang::BuiltinType::ObjCId: // child is Class case clang::BuiltinType::ObjCClass: // child is Class num_children = 1; break; default: break; } break; case clang::Type::Complex: return 0; case clang::Type::Record: if (GetCompleteQualType(getASTContext(), qual_type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) { if (omit_empty_base_classes) { // Check each base classes to see if it or any of its // base classes contain any fields. This can help // limit the noise in variable views by not having to // show base classes that contain no members. clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { const clang::CXXRecordDecl *base_class_decl = llvm::cast( base_class->getType() ->getAs() ->getDecl()); // Skip empty base classes if (ClangASTContext::RecordHasFields(base_class_decl) == false) continue; num_children++; } } else { // Include all base classes num_children += cxx_record_decl->getNumBases(); } } clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field) ++num_children; } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteQualType(getASTContext(), qual_type)) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); if (superclass_interface_decl) { if (omit_empty_base_classes) { if (ObjCDeclHasIVars(superclass_interface_decl, true)) ++num_children; } else ++num_children; } num_children += class_interface_decl->ivar_size(); } } } break; case clang::Type::ObjCObjectPointer: { const clang::ObjCObjectPointerType *pointer_type = llvm::cast(qual_type.getTypePtr()); clang::QualType pointee_type = pointer_type->getPointeeType(); uint32_t num_pointee_children = CompilerType(getASTContext(), pointee_type) .GetNumChildren(omit_empty_base_classes); // If this type points to a simple type, then it has 1 child if (num_pointee_children == 0) num_children = 1; else num_children = num_pointee_children; } break; case clang::Type::Vector: case clang::Type::ExtVector: num_children = llvm::cast(qual_type.getTypePtr())->getNumElements(); break; case clang::Type::ConstantArray: num_children = llvm::cast(qual_type.getTypePtr()) ->getSize() .getLimitedValue(); break; case clang::Type::Pointer: { const clang::PointerType *pointer_type = llvm::cast(qual_type.getTypePtr()); clang::QualType pointee_type(pointer_type->getPointeeType()); uint32_t num_pointee_children = CompilerType(getASTContext(), pointee_type) .GetNumChildren(omit_empty_base_classes); if (num_pointee_children == 0) { // We have a pointer to a pointee type that claims it has no children. // We will want to look at num_children = GetNumPointeeChildren(pointee_type); } else num_children = num_pointee_children; } break; case clang::Type::LValueReference: case clang::Type::RValueReference: { const clang::ReferenceType *reference_type = llvm::cast(qual_type.getTypePtr()); clang::QualType pointee_type = reference_type->getPointeeType(); uint32_t num_pointee_children = CompilerType(getASTContext(), pointee_type) .GetNumChildren(omit_empty_base_classes); // If this type points to a simple type, then it has 1 child if (num_pointee_children == 0) num_children = 1; else num_children = num_pointee_children; } break; case clang::Type::Typedef: num_children = CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetNumChildren(omit_empty_base_classes); break; case clang::Type::Auto: num_children = CompilerType(getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetNumChildren(omit_empty_base_classes); break; case clang::Type::Elaborated: num_children = CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetNumChildren(omit_empty_base_classes); break; case clang::Type::Paren: num_children = CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetNumChildren(omit_empty_base_classes); break; default: break; } return num_children; } CompilerType ClangASTContext::GetBuiltinTypeByName(const ConstString &name) { return GetBasicType(GetBasicTypeEnumeration(name)); } lldb::BasicType ClangASTContext::GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); if (type_class == clang::Type::Builtin) { switch (llvm::cast(qual_type)->getKind()) { case clang::BuiltinType::Void: return eBasicTypeVoid; case clang::BuiltinType::Bool: return eBasicTypeBool; case clang::BuiltinType::Char_S: return eBasicTypeSignedChar; case clang::BuiltinType::Char_U: return eBasicTypeUnsignedChar; case clang::BuiltinType::Char16: return eBasicTypeChar16; case clang::BuiltinType::Char32: return eBasicTypeChar32; case clang::BuiltinType::UChar: return eBasicTypeUnsignedChar; case clang::BuiltinType::SChar: return eBasicTypeSignedChar; case clang::BuiltinType::WChar_S: return eBasicTypeSignedWChar; case clang::BuiltinType::WChar_U: return eBasicTypeUnsignedWChar; case clang::BuiltinType::Short: return eBasicTypeShort; case clang::BuiltinType::UShort: return eBasicTypeUnsignedShort; case clang::BuiltinType::Int: return eBasicTypeInt; case clang::BuiltinType::UInt: return eBasicTypeUnsignedInt; case clang::BuiltinType::Long: return eBasicTypeLong; case clang::BuiltinType::ULong: return eBasicTypeUnsignedLong; case clang::BuiltinType::LongLong: return eBasicTypeLongLong; case clang::BuiltinType::ULongLong: return eBasicTypeUnsignedLongLong; case clang::BuiltinType::Int128: return eBasicTypeInt128; case clang::BuiltinType::UInt128: return eBasicTypeUnsignedInt128; case clang::BuiltinType::Half: return eBasicTypeHalf; case clang::BuiltinType::Float: return eBasicTypeFloat; case clang::BuiltinType::Double: return eBasicTypeDouble; case clang::BuiltinType::LongDouble: return eBasicTypeLongDouble; case clang::BuiltinType::NullPtr: return eBasicTypeNullPtr; case clang::BuiltinType::ObjCId: return eBasicTypeObjCID; case clang::BuiltinType::ObjCClass: return eBasicTypeObjCClass; case clang::BuiltinType::ObjCSel: return eBasicTypeObjCSel; default: return eBasicTypeOther; } } } return eBasicTypeInvalid; } void ClangASTContext::ForEachEnumerator( lldb::opaque_compiler_type_t type, std::function const &callback) { const clang::EnumType *enum_type = llvm::dyn_cast(GetCanonicalQualType(type)); if (enum_type) { const clang::EnumDecl *enum_decl = enum_type->getDecl(); if (enum_decl) { CompilerType integer_type(this, enum_decl->getIntegerType().getAsOpaquePtr()); clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos; for (enum_pos = enum_decl->enumerator_begin(), enum_end_pos = enum_decl->enumerator_end(); enum_pos != enum_end_pos; ++enum_pos) { ConstString name(enum_pos->getNameAsString().c_str()); if (!callback(integer_type, name, enum_pos->getInitVal())) break; } } } } #pragma mark Aggregate Types uint32_t ClangASTContext::GetNumFields(lldb::opaque_compiler_type_t type) { if (!type) return 0; uint32_t count = 0; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::dyn_cast(qual_type.getTypePtr()); if (record_type) { clang::RecordDecl *record_decl = record_type->getDecl(); if (record_decl) { uint32_t field_idx = 0; clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field) ++field_idx; count = field_idx; } } } break; case clang::Type::Typedef: count = CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetNumFields(); break; case clang::Type::Auto: count = CompilerType(getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetNumFields(); break; case clang::Type::Elaborated: count = CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetNumFields(); break; case clang::Type::Paren: count = CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetNumFields(); break; case clang::Type::ObjCObjectPointer: { const clang::ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); const clang::ObjCInterfaceType *objc_interface_type = objc_class_type->getInterfaceType(); if (objc_interface_type && GetCompleteType((lldb::opaque_compiler_type_t)objc_interface_type)) { clang::ObjCInterfaceDecl *class_interface_decl = objc_interface_type->getDecl(); if (class_interface_decl) { count = class_interface_decl->ivar_size(); } } break; } case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) count = class_interface_decl->ivar_size(); } } break; default: break; } return count; } static lldb::opaque_compiler_type_t GetObjCFieldAtIndex(clang::ASTContext *ast, clang::ObjCInterfaceDecl *class_interface_decl, size_t idx, std::string &name, uint64_t *bit_offset_ptr, uint32_t *bitfield_bit_size_ptr, bool *is_bitfield_ptr) { if (class_interface_decl) { if (idx < (class_interface_decl->ivar_size())) { clang::ObjCInterfaceDecl::ivar_iterator ivar_pos, ivar_end = class_interface_decl->ivar_end(); uint32_t ivar_idx = 0; for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end; ++ivar_pos, ++ivar_idx) { if (ivar_idx == idx) { const clang::ObjCIvarDecl *ivar_decl = *ivar_pos; clang::QualType ivar_qual_type(ivar_decl->getType()); name.assign(ivar_decl->getNameAsString()); if (bit_offset_ptr) { const clang::ASTRecordLayout &interface_layout = ast->getASTObjCInterfaceLayout(class_interface_decl); *bit_offset_ptr = interface_layout.getFieldOffset(ivar_idx); } const bool is_bitfield = ivar_pos->isBitField(); if (bitfield_bit_size_ptr) { *bitfield_bit_size_ptr = 0; if (is_bitfield && ast) { clang::Expr *bitfield_bit_size_expr = ivar_pos->getBitWidth(); llvm::APSInt bitfield_apsint; if (bitfield_bit_size_expr && bitfield_bit_size_expr->EvaluateAsInt(bitfield_apsint, *ast)) { *bitfield_bit_size_ptr = bitfield_apsint.getLimitedValue(); } } } if (is_bitfield_ptr) *is_bitfield_ptr = is_bitfield; return ivar_qual_type.getAsOpaquePtr(); } } } } return nullptr; } CompilerType ClangASTContext::GetFieldAtIndex(lldb::opaque_compiler_type_t type, size_t idx, std::string &name, uint64_t *bit_offset_ptr, uint32_t *bitfield_bit_size_ptr, bool *is_bitfield_ptr) { if (!type) return CompilerType(); clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); uint32_t field_idx = 0; clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++field_idx) { if (idx == field_idx) { // Print the member type if requested // Print the member name and equal sign name.assign(field->getNameAsString()); // Figure out the type byte size (field_type_info.first) and // alignment (field_type_info.second) from the AST context. if (bit_offset_ptr) { const clang::ASTRecordLayout &record_layout = getASTContext()->getASTRecordLayout(record_decl); *bit_offset_ptr = record_layout.getFieldOffset(field_idx); } const bool is_bitfield = field->isBitField(); if (bitfield_bit_size_ptr) { *bitfield_bit_size_ptr = 0; if (is_bitfield) { clang::Expr *bitfield_bit_size_expr = field->getBitWidth(); llvm::APSInt bitfield_apsint; if (bitfield_bit_size_expr && bitfield_bit_size_expr->EvaluateAsInt(bitfield_apsint, *getASTContext())) { *bitfield_bit_size_ptr = bitfield_apsint.getLimitedValue(); } } } if (is_bitfield_ptr) *is_bitfield_ptr = is_bitfield; return CompilerType(getASTContext(), field->getType()); } } } break; case clang::Type::ObjCObjectPointer: { const clang::ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); const clang::ObjCInterfaceType *objc_interface_type = objc_class_type->getInterfaceType(); if (objc_interface_type && GetCompleteType((lldb::opaque_compiler_type_t)objc_interface_type)) { clang::ObjCInterfaceDecl *class_interface_decl = objc_interface_type->getDecl(); if (class_interface_decl) { return CompilerType( this, GetObjCFieldAtIndex(getASTContext(), class_interface_decl, idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr)); } } break; } case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); return CompilerType( this, GetObjCFieldAtIndex(getASTContext(), class_interface_decl, idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr)); } } break; case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetFieldAtIndex(idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr); case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetFieldAtIndex(idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetFieldAtIndex(idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetFieldAtIndex(idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr); default: break; } return CompilerType(); } uint32_t ClangASTContext::GetNumDirectBaseClasses(lldb::opaque_compiler_type_t type) { uint32_t count = 0; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) count = cxx_record_decl->getNumBases(); } break; case clang::Type::ObjCObjectPointer: count = GetPointeeType(type).GetNumDirectBaseClasses(); break; case clang::Type::ObjCObject: if (GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = qual_type->getAsObjCQualifiedInterfaceType(); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl && class_interface_decl->getSuperClass()) count = 1; } } break; case clang::Type::ObjCInterface: if (GetCompleteType(type)) { const clang::ObjCInterfaceType *objc_interface_type = qual_type->getAs(); if (objc_interface_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_interface_type->getInterface(); if (class_interface_decl && class_interface_decl->getSuperClass()) count = 1; } } break; case clang::Type::Typedef: count = GetNumDirectBaseClasses(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr()); break; case clang::Type::Auto: count = GetNumDirectBaseClasses(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr()); break; case clang::Type::Elaborated: count = GetNumDirectBaseClasses(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr()); break; case clang::Type::Paren: return GetNumDirectBaseClasses( llvm::cast(qual_type)->desugar().getAsOpaquePtr()); default: break; } return count; } uint32_t ClangASTContext::GetNumVirtualBaseClasses(lldb::opaque_compiler_type_t type) { uint32_t count = 0; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) count = cxx_record_decl->getNumVBases(); } break; case clang::Type::Typedef: count = GetNumVirtualBaseClasses(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr()); break; case clang::Type::Auto: count = GetNumVirtualBaseClasses(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr()); break; case clang::Type::Elaborated: count = GetNumVirtualBaseClasses(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr()); break; case clang::Type::Paren: count = GetNumVirtualBaseClasses( llvm::cast(qual_type)->desugar().getAsOpaquePtr()); break; default: break; } return count; } CompilerType ClangASTContext::GetDirectBaseClassAtIndex( lldb::opaque_compiler_type_t type, size_t idx, uint32_t *bit_offset_ptr) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { uint32_t curr_idx = 0; clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class, ++curr_idx) { if (curr_idx == idx) { if (bit_offset_ptr) { const clang::ASTRecordLayout &record_layout = getASTContext()->getASTRecordLayout(cxx_record_decl); const clang::CXXRecordDecl *base_class_decl = llvm::cast( base_class->getType() ->getAs() ->getDecl()); if (base_class->isVirtual()) *bit_offset_ptr = record_layout.getVBaseClassOffset(base_class_decl) .getQuantity() * 8; else *bit_offset_ptr = record_layout.getBaseClassOffset(base_class_decl) .getQuantity() * 8; } return CompilerType(this, base_class->getType().getAsOpaquePtr()); } } } } break; case clang::Type::ObjCObjectPointer: return GetPointeeType(type).GetDirectBaseClassAtIndex(idx, bit_offset_ptr); case clang::Type::ObjCObject: if (idx == 0 && GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = qual_type->getAsObjCQualifiedInterfaceType(); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); if (superclass_interface_decl) { if (bit_offset_ptr) *bit_offset_ptr = 0; return CompilerType(getASTContext(), getASTContext()->getObjCInterfaceType( superclass_interface_decl)); } } } } break; case clang::Type::ObjCInterface: if (idx == 0 && GetCompleteType(type)) { const clang::ObjCObjectType *objc_interface_type = qual_type->getAs(); if (objc_interface_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_interface_type->getInterface(); if (class_interface_decl) { clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); if (superclass_interface_decl) { if (bit_offset_ptr) *bit_offset_ptr = 0; return CompilerType(getASTContext(), getASTContext()->getObjCInterfaceType( superclass_interface_decl)); } } } } break; case clang::Type::Typedef: return GetDirectBaseClassAtIndex(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), idx, bit_offset_ptr); case clang::Type::Auto: return GetDirectBaseClassAtIndex(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), idx, bit_offset_ptr); case clang::Type::Elaborated: return GetDirectBaseClassAtIndex( llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), idx, bit_offset_ptr); case clang::Type::Paren: return GetDirectBaseClassAtIndex( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), idx, bit_offset_ptr); default: break; } return CompilerType(); } CompilerType ClangASTContext::GetVirtualBaseClassAtIndex( lldb::opaque_compiler_type_t type, size_t idx, uint32_t *bit_offset_ptr) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { uint32_t curr_idx = 0; clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->vbases_begin(), base_class_end = cxx_record_decl->vbases_end(); base_class != base_class_end; ++base_class, ++curr_idx) { if (curr_idx == idx) { if (bit_offset_ptr) { const clang::ASTRecordLayout &record_layout = getASTContext()->getASTRecordLayout(cxx_record_decl); const clang::CXXRecordDecl *base_class_decl = llvm::cast( base_class->getType() ->getAs() ->getDecl()); *bit_offset_ptr = record_layout.getVBaseClassOffset(base_class_decl) .getQuantity() * 8; } return CompilerType(this, base_class->getType().getAsOpaquePtr()); } } } } break; case clang::Type::Typedef: return GetVirtualBaseClassAtIndex(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), idx, bit_offset_ptr); case clang::Type::Auto: return GetVirtualBaseClassAtIndex(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), idx, bit_offset_ptr); case clang::Type::Elaborated: return GetVirtualBaseClassAtIndex( llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), idx, bit_offset_ptr); case clang::Type::Paren: return GetVirtualBaseClassAtIndex( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), idx, bit_offset_ptr); default: break; } return CompilerType(); } // If a pointer to a pointee type (the clang_type arg) says that it has no // children, then we either need to trust it, or override it and return a // different result. For example, an "int *" has one child that is an integer, // but a function pointer doesn't have any children. Likewise if a Record type // claims it has no children, then there really is nothing to show. uint32_t ClangASTContext::GetNumPointeeChildren(clang::QualType type) { if (type.isNull()) return 0; clang::QualType qual_type(type.getCanonicalType()); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { case clang::BuiltinType::UnknownAny: case clang::BuiltinType::Void: case clang::BuiltinType::NullPtr: case clang::BuiltinType::OCLEvent: case clang::BuiltinType::OCLImage1dRO: case clang::BuiltinType::OCLImage1dWO: case clang::BuiltinType::OCLImage1dRW: case clang::BuiltinType::OCLImage1dArrayRO: case clang::BuiltinType::OCLImage1dArrayWO: case clang::BuiltinType::OCLImage1dArrayRW: case clang::BuiltinType::OCLImage1dBufferRO: case clang::BuiltinType::OCLImage1dBufferWO: case clang::BuiltinType::OCLImage1dBufferRW: case clang::BuiltinType::OCLImage2dRO: case clang::BuiltinType::OCLImage2dWO: case clang::BuiltinType::OCLImage2dRW: case clang::BuiltinType::OCLImage2dArrayRO: case clang::BuiltinType::OCLImage2dArrayWO: case clang::BuiltinType::OCLImage2dArrayRW: case clang::BuiltinType::OCLImage3dRO: case clang::BuiltinType::OCLImage3dWO: case clang::BuiltinType::OCLImage3dRW: case clang::BuiltinType::OCLSampler: return 0; case clang::BuiltinType::Bool: case clang::BuiltinType::Char_U: case clang::BuiltinType::UChar: case clang::BuiltinType::WChar_U: case clang::BuiltinType::Char16: case clang::BuiltinType::Char32: case clang::BuiltinType::UShort: case clang::BuiltinType::UInt: case clang::BuiltinType::ULong: case clang::BuiltinType::ULongLong: case clang::BuiltinType::UInt128: case clang::BuiltinType::Char_S: case clang::BuiltinType::SChar: case clang::BuiltinType::WChar_S: case clang::BuiltinType::Short: case clang::BuiltinType::Int: case clang::BuiltinType::Long: case clang::BuiltinType::LongLong: case clang::BuiltinType::Int128: case clang::BuiltinType::Float: case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: case clang::BuiltinType::Dependent: case clang::BuiltinType::Overload: case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCClass: case clang::BuiltinType::ObjCSel: case clang::BuiltinType::BoundMember: case clang::BuiltinType::Half: case clang::BuiltinType::ARCUnbridgedCast: case clang::BuiltinType::PseudoObject: case clang::BuiltinType::BuiltinFn: case clang::BuiltinType::OMPArraySection: return 1; default: return 0; } break; case clang::Type::Complex: return 1; case clang::Type::Pointer: return 1; case clang::Type::BlockPointer: return 0; // If block pointers don't have debug info, then no children for // them case clang::Type::LValueReference: return 1; case clang::Type::RValueReference: return 1; case clang::Type::MemberPointer: return 0; case clang::Type::ConstantArray: return 0; case clang::Type::IncompleteArray: return 0; case clang::Type::VariableArray: return 0; case clang::Type::DependentSizedArray: return 0; case clang::Type::DependentSizedExtVector: return 0; case clang::Type::Vector: return 0; case clang::Type::ExtVector: return 0; case clang::Type::FunctionProto: return 0; // When we function pointers, they have no children... case clang::Type::FunctionNoProto: return 0; // When we function pointers, they have no children... case clang::Type::UnresolvedUsing: return 0; case clang::Type::Paren: return GetNumPointeeChildren( llvm::cast(qual_type)->desugar()); case clang::Type::Typedef: return GetNumPointeeChildren(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()); case clang::Type::Auto: return GetNumPointeeChildren( llvm::cast(qual_type)->getDeducedType()); case clang::Type::Elaborated: return GetNumPointeeChildren( llvm::cast(qual_type)->getNamedType()); case clang::Type::TypeOfExpr: return 0; case clang::Type::TypeOf: return 0; case clang::Type::Decltype: return 0; case clang::Type::Record: return 0; case clang::Type::Enum: return 1; case clang::Type::TemplateTypeParm: return 1; case clang::Type::SubstTemplateTypeParm: return 1; case clang::Type::TemplateSpecialization: return 1; case clang::Type::InjectedClassName: return 0; case clang::Type::DependentName: return 1; case clang::Type::DependentTemplateSpecialization: return 1; case clang::Type::ObjCObject: return 0; case clang::Type::ObjCInterface: return 0; case clang::Type::ObjCObjectPointer: return 1; default: break; } return 0; } CompilerType ClangASTContext::GetChildCompilerTypeAtIndex( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers, bool omit_empty_base_classes, bool ignore_array_bounds, std::string &child_name, uint32_t &child_byte_size, int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, bool &child_is_deref_of_parent, ValueObject *valobj, uint64_t &language_flags) { if (!type) return CompilerType(); clang::QualType parent_qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass parent_type_class = parent_qual_type->getTypeClass(); child_bitfield_bit_size = 0; child_bitfield_bit_offset = 0; child_is_base_class = false; language_flags = 0; const bool idx_is_valid = idx < GetNumChildren(type, omit_empty_base_classes); uint32_t bit_offset; switch (parent_type_class) { case clang::Type::Builtin: if (idx_is_valid) { switch (llvm::cast(parent_qual_type)->getKind()) { case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCClass: child_name = "isa"; child_byte_size = getASTContext()->getTypeSize(getASTContext()->ObjCBuiltinClassTy) / CHAR_BIT; return CompilerType(getASTContext(), getASTContext()->ObjCBuiltinClassTy); default: break; } } break; case clang::Type::Record: if (idx_is_valid && GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast(parent_qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); const clang::ASTRecordLayout &record_layout = getASTContext()->getASTRecordLayout(record_decl); uint32_t child_idx = 0; const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) { // We might have base classes to print out first clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { const clang::CXXRecordDecl *base_class_decl = nullptr; // Skip empty base classes if (omit_empty_base_classes) { base_class_decl = llvm::cast( base_class->getType()->getAs()->getDecl()); if (ClangASTContext::RecordHasFields(base_class_decl) == false) continue; } if (idx == child_idx) { if (base_class_decl == nullptr) base_class_decl = llvm::cast( base_class->getType()->getAs()->getDecl()); if (base_class->isVirtual()) { bool handled = false; if (valobj) { Error err; AddressType addr_type = eAddressTypeInvalid; lldb::addr_t vtable_ptr_addr = valobj->GetCPPVTableAddress(addr_type); if (vtable_ptr_addr != LLDB_INVALID_ADDRESS && addr_type == eAddressTypeLoad) { ExecutionContext exe_ctx(valobj->GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); if (process) { clang::VTableContextBase *vtable_ctx = getASTContext()->getVTableContext(); if (vtable_ctx) { if (vtable_ctx->isMicrosoft()) { clang::MicrosoftVTableContext *msoft_vtable_ctx = static_cast( vtable_ctx); if (vtable_ptr_addr) { const lldb::addr_t vbtable_ptr_addr = vtable_ptr_addr + record_layout.getVBPtrOffset().getQuantity(); const lldb::addr_t vbtable_ptr = process->ReadPointerFromMemory(vbtable_ptr_addr, err); if (vbtable_ptr != LLDB_INVALID_ADDRESS) { // Get the index into the virtual base table. The // index is the index in uint32_t from vbtable_ptr const unsigned vbtable_index = msoft_vtable_ctx->getVBTableIndex( cxx_record_decl, base_class_decl); const lldb::addr_t base_offset_addr = vbtable_ptr + vbtable_index * 4; const uint32_t base_offset = process->ReadUnsignedIntegerFromMemory( base_offset_addr, 4, UINT32_MAX, err); if (base_offset != UINT32_MAX) { handled = true; bit_offset = base_offset * 8; } } } } else { clang::ItaniumVTableContext *itanium_vtable_ctx = static_cast( vtable_ctx); if (vtable_ptr_addr) { const lldb::addr_t vtable_ptr = process->ReadPointerFromMemory(vtable_ptr_addr, err); if (vtable_ptr != LLDB_INVALID_ADDRESS) { clang::CharUnits base_offset_offset = itanium_vtable_ctx->getVirtualBaseOffsetOffset( cxx_record_decl, base_class_decl); const lldb::addr_t base_offset_addr = vtable_ptr + base_offset_offset.getQuantity(); const uint32_t base_offset_size = process->GetAddressByteSize(); const uint64_t base_offset = process->ReadUnsignedIntegerFromMemory( base_offset_addr, base_offset_size, UINT32_MAX, err); if (base_offset < UINT32_MAX) { handled = true; bit_offset = base_offset * 8; } } } } } } } } if (!handled) bit_offset = record_layout.getVBaseClassOffset(base_class_decl) .getQuantity() * 8; } else bit_offset = record_layout.getBaseClassOffset(base_class_decl) .getQuantity() * 8; // Base classes should be a multiple of 8 bits in size child_byte_offset = bit_offset / 8; CompilerType base_class_clang_type(getASTContext(), base_class->getType()); child_name = base_class_clang_type.GetTypeName().AsCString(""); uint64_t base_class_clang_type_bit_size = base_class_clang_type.GetBitSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); // Base classes bit sizes should be a multiple of 8 bits in size assert(base_class_clang_type_bit_size % 8 == 0); child_byte_size = base_class_clang_type_bit_size / 8; child_is_base_class = true; return base_class_clang_type; } // We don't increment the child index in the for loop since we might // be skipping empty base classes ++child_idx; } } // Make sure index is in range... uint32_t field_idx = 0; clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++field_idx, ++child_idx) { if (idx == child_idx) { // Print the member type if requested // Print the member name and equal sign child_name.assign(field->getNameAsString()); // Figure out the type byte size (field_type_info.first) and // alignment (field_type_info.second) from the AST context. CompilerType field_clang_type(getASTContext(), field->getType()); assert(field_idx < record_layout.getFieldCount()); child_byte_size = field_clang_type.GetByteSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); const uint32_t child_bit_size = child_byte_size * 8; // Figure out the field offset within the current struct/union/class // type bit_offset = record_layout.getFieldOffset(field_idx); if (ClangASTContext::FieldIsBitfield(getASTContext(), *field, child_bitfield_bit_size)) { child_bitfield_bit_offset = bit_offset % child_bit_size; const uint32_t child_bit_offset = bit_offset - child_bitfield_bit_offset; child_byte_offset = child_bit_offset / 8; } else { child_byte_offset = bit_offset / 8; } return field_clang_type; } } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (idx_is_valid && GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(parent_qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { uint32_t child_idx = 0; clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { const clang::ASTRecordLayout &interface_layout = getASTContext()->getASTObjCInterfaceLayout(class_interface_decl); clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); if (superclass_interface_decl) { if (omit_empty_base_classes) { CompilerType base_class_clang_type( getASTContext(), getASTContext()->getObjCInterfaceType( superclass_interface_decl)); if (base_class_clang_type.GetNumChildren( omit_empty_base_classes) > 0) { if (idx == 0) { clang::QualType ivar_qual_type( getASTContext()->getObjCInterfaceType( superclass_interface_decl)); child_name.assign( superclass_interface_decl->getNameAsString()); clang::TypeInfo ivar_type_info = getASTContext()->getTypeInfo(ivar_qual_type.getTypePtr()); child_byte_size = ivar_type_info.Width / 8; child_byte_offset = 0; child_is_base_class = true; return CompilerType(getASTContext(), ivar_qual_type); } ++child_idx; } } else ++child_idx; } const uint32_t superclass_idx = child_idx; if (idx < (child_idx + class_interface_decl->ivar_size())) { clang::ObjCInterfaceDecl::ivar_iterator ivar_pos, ivar_end = class_interface_decl->ivar_end(); for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end; ++ivar_pos) { if (child_idx == idx) { clang::ObjCIvarDecl *ivar_decl = *ivar_pos; clang::QualType ivar_qual_type(ivar_decl->getType()); child_name.assign(ivar_decl->getNameAsString()); clang::TypeInfo ivar_type_info = getASTContext()->getTypeInfo(ivar_qual_type.getTypePtr()); child_byte_size = ivar_type_info.Width / 8; // Figure out the field offset within the current // struct/union/class type // For ObjC objects, we can't trust the bit offset we get from // the Clang AST, since // that doesn't account for the space taken up by unbacked // properties, or from // the changing size of base classes that are newer than this // class. // So if we have a process around that we can ask about this // object, do so. child_byte_offset = LLDB_INVALID_IVAR_OFFSET; Process *process = nullptr; if (exe_ctx) process = exe_ctx->GetProcessPtr(); if (process) { ObjCLanguageRuntime *objc_runtime = process->GetObjCLanguageRuntime(); if (objc_runtime != nullptr) { CompilerType parent_ast_type(getASTContext(), parent_qual_type); child_byte_offset = objc_runtime->GetByteOffsetForIvar( parent_ast_type, ivar_decl->getNameAsString().c_str()); } } // Setting this to UINT32_MAX to make sure we don't compute it // twice... bit_offset = UINT32_MAX; if (child_byte_offset == static_cast(LLDB_INVALID_IVAR_OFFSET)) { bit_offset = interface_layout.getFieldOffset(child_idx - superclass_idx); child_byte_offset = bit_offset / 8; } // Note, the ObjC Ivar Byte offset is just that, it doesn't // account for the bit offset // of a bitfield within its containing object. So regardless of // where we get the byte // offset from, we still need to get the bit offset for // bitfields from the layout. if (ClangASTContext::FieldIsBitfield(getASTContext(), ivar_decl, child_bitfield_bit_size)) { if (bit_offset == UINT32_MAX) bit_offset = interface_layout.getFieldOffset( child_idx - superclass_idx); child_bitfield_bit_offset = bit_offset % 8; } return CompilerType(getASTContext(), ivar_qual_type); } ++child_idx; } } } } } break; case clang::Type::ObjCObjectPointer: if (idx_is_valid) { CompilerType pointee_clang_type(GetPointeeType(type)); if (transparent_pointers && pointee_clang_type.IsAggregateType()) { child_is_deref_of_parent = false; bool tmp_child_is_deref_of_parent = false; return pointee_clang_type.GetChildCompilerTypeAtIndex( exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, tmp_child_is_deref_of_parent, valobj, language_flags); } else { child_is_deref_of_parent = true; const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL; if (parent_name) { child_name.assign(1, '*'); child_name += parent_name; } // We have a pointer to an simple type if (idx == 0 && pointee_clang_type.GetCompleteType()) { child_byte_size = pointee_clang_type.GetByteSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = 0; return pointee_clang_type; } } } break; case clang::Type::Vector: case clang::Type::ExtVector: if (idx_is_valid) { const clang::VectorType *array = llvm::cast(parent_qual_type.getTypePtr()); if (array) { CompilerType element_type(getASTContext(), array->getElementType()); if (element_type.GetCompleteType()) { char element_name[64]; ::snprintf(element_name, sizeof(element_name), "[%" PRIu64 "]", static_cast(idx)); child_name.assign(element_name); child_byte_size = element_type.GetByteSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = (int32_t)idx * (int32_t)child_byte_size; return element_type; } } } break; case clang::Type::ConstantArray: case clang::Type::IncompleteArray: if (ignore_array_bounds || idx_is_valid) { const clang::ArrayType *array = GetQualType(type)->getAsArrayTypeUnsafe(); if (array) { CompilerType element_type(getASTContext(), array->getElementType()); if (element_type.GetCompleteType()) { child_name = llvm::formatv("[{0}]", idx); child_byte_size = element_type.GetByteSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = (int32_t)idx * (int32_t)child_byte_size; return element_type; } } } break; case clang::Type::Pointer: if (idx_is_valid) { CompilerType pointee_clang_type(GetPointeeType(type)); // Don't dereference "void *" pointers if (pointee_clang_type.IsVoidType()) return CompilerType(); if (transparent_pointers && pointee_clang_type.IsAggregateType()) { child_is_deref_of_parent = false; bool tmp_child_is_deref_of_parent = false; return pointee_clang_type.GetChildCompilerTypeAtIndex( exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, tmp_child_is_deref_of_parent, valobj, language_flags); } else { child_is_deref_of_parent = true; const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL; if (parent_name) { child_name.assign(1, '*'); child_name += parent_name; } // We have a pointer to an simple type if (idx == 0) { child_byte_size = pointee_clang_type.GetByteSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = 0; return pointee_clang_type; } } } break; case clang::Type::LValueReference: case clang::Type::RValueReference: if (idx_is_valid) { const clang::ReferenceType *reference_type = llvm::cast(parent_qual_type.getTypePtr()); CompilerType pointee_clang_type(getASTContext(), reference_type->getPointeeType()); if (transparent_pointers && pointee_clang_type.IsAggregateType()) { child_is_deref_of_parent = false; bool tmp_child_is_deref_of_parent = false; return pointee_clang_type.GetChildCompilerTypeAtIndex( exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, tmp_child_is_deref_of_parent, valobj, language_flags); } else { const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL; if (parent_name) { child_name.assign(1, '&'); child_name += parent_name; } // We have a pointer to an simple type if (idx == 0) { child_byte_size = pointee_clang_type.GetByteSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = 0; return pointee_clang_type; } } } break; case clang::Type::Typedef: { CompilerType typedefed_clang_type( getASTContext(), llvm::cast(parent_qual_type) ->getDecl() ->getUnderlyingType()); return typedefed_clang_type.GetChildCompilerTypeAtIndex( exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, child_is_deref_of_parent, valobj, language_flags); } break; case clang::Type::Auto: { CompilerType elaborated_clang_type( getASTContext(), llvm::cast(parent_qual_type)->getDeducedType()); return elaborated_clang_type.GetChildCompilerTypeAtIndex( exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, child_is_deref_of_parent, valobj, language_flags); } case clang::Type::Elaborated: { CompilerType elaborated_clang_type( getASTContext(), llvm::cast(parent_qual_type)->getNamedType()); return elaborated_clang_type.GetChildCompilerTypeAtIndex( exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, child_is_deref_of_parent, valobj, language_flags); } case clang::Type::Paren: { CompilerType paren_clang_type( getASTContext(), llvm::cast(parent_qual_type)->desugar()); return paren_clang_type.GetChildCompilerTypeAtIndex( exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, child_is_deref_of_parent, valobj, language_flags); } default: break; } return CompilerType(); } static uint32_t GetIndexForRecordBase(const clang::RecordDecl *record_decl, const clang::CXXBaseSpecifier *base_spec, bool omit_empty_base_classes) { uint32_t child_idx = 0; const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); // const char *super_name = record_decl->getNameAsCString(); // const char *base_name = // base_spec->getType()->getAs()->getDecl()->getNameAsCString(); // printf ("GetIndexForRecordChild (%s, %s)\n", super_name, base_name); // if (cxx_record_decl) { clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { if (omit_empty_base_classes) { if (BaseSpecifierIsEmpty(base_class)) continue; } // printf ("GetIndexForRecordChild (%s, %s) base[%u] = %s\n", // super_name, base_name, // child_idx, // base_class->getType()->getAs()->getDecl()->getNameAsCString()); // // if (base_class == base_spec) return child_idx; ++child_idx; } } return UINT32_MAX; } static uint32_t GetIndexForRecordChild(const clang::RecordDecl *record_decl, clang::NamedDecl *canonical_decl, bool omit_empty_base_classes) { uint32_t child_idx = ClangASTContext::GetNumBaseClasses( llvm::dyn_cast(record_decl), omit_empty_base_classes); clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++child_idx) { if (field->getCanonicalDecl() == canonical_decl) return child_idx; } return UINT32_MAX; } // Look for a child member (doesn't include base classes, but it does include // their members) in the type hierarchy. Returns an index path into "clang_type" // on how to reach the appropriate member. // // class A // { // public: // int m_a; // int m_b; // }; // // class B // { // }; // // class C : // public B, // public A // { // }; // // If we have a clang type that describes "class C", and we wanted to looked // "m_b" in it: // // With omit_empty_base_classes == false we would get an integer array back // with: // { 1, 1 } // The first index 1 is the child index for "class A" within class C // The second index 1 is the child index for "m_b" within class A // // With omit_empty_base_classes == true we would get an integer array back with: // { 0, 1 } // The first index 0 is the child index for "class A" within class C (since // class B doesn't have any members it doesn't count) // The second index 1 is the child index for "m_b" within class A size_t ClangASTContext::GetIndexOfChildMemberWithName( lldb::opaque_compiler_type_t type, const char *name, bool omit_empty_base_classes, std::vector &child_indexes) { if (type && name && name[0]) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); uint32_t child_idx = 0; const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); // Try and find a field that matches NAME clang::RecordDecl::field_iterator field, field_end; llvm::StringRef name_sref(name); for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++child_idx) { llvm::StringRef field_name = field->getName(); if (field_name.empty()) { CompilerType field_type(getASTContext(), field->getType()); child_indexes.push_back(child_idx); if (field_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes)) return child_indexes.size(); child_indexes.pop_back(); } else if (field_name.equals(name_sref)) { // We have to add on the number of base classes to this index! child_indexes.push_back( child_idx + ClangASTContext::GetNumBaseClasses( cxx_record_decl, omit_empty_base_classes)); return child_indexes.size(); } } if (cxx_record_decl) { const clang::RecordDecl *parent_record_decl = cxx_record_decl; // printf ("parent = %s\n", parent_record_decl->getNameAsCString()); // const Decl *root_cdecl = cxx_record_decl->getCanonicalDecl(); // Didn't find things easily, lets let clang do its thang... clang::IdentifierInfo &ident_ref = getASTContext()->Idents.get(name_sref); clang::DeclarationName decl_name(&ident_ref); clang::CXXBasePaths paths; if (cxx_record_decl->lookupInBases( [decl_name](const clang::CXXBaseSpecifier *specifier, clang::CXXBasePath &path) { return clang::CXXRecordDecl::FindOrdinaryMember( specifier, path, decl_name); }, paths)) { clang::CXXBasePaths::const_paths_iterator path, path_end = paths.end(); for (path = paths.begin(); path != path_end; ++path) { const size_t num_path_elements = path->size(); for (size_t e = 0; e < num_path_elements; ++e) { clang::CXXBasePathElement elem = (*path)[e]; child_idx = GetIndexForRecordBase(parent_record_decl, elem.Base, omit_empty_base_classes); if (child_idx == UINT32_MAX) { child_indexes.clear(); return 0; } else { child_indexes.push_back(child_idx); parent_record_decl = llvm::cast( elem.Base->getType() ->getAs() ->getDecl()); } } for (clang::NamedDecl *path_decl : path->Decls) { child_idx = GetIndexForRecordChild( parent_record_decl, path_decl, omit_empty_base_classes); if (child_idx == UINT32_MAX) { child_indexes.clear(); return 0; } else { child_indexes.push_back(child_idx); } } } return child_indexes.size(); } } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteType(type)) { llvm::StringRef name_sref(name); const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { uint32_t child_idx = 0; clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { clang::ObjCInterfaceDecl::ivar_iterator ivar_pos, ivar_end = class_interface_decl->ivar_end(); clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end; ++ivar_pos, ++child_idx) { const clang::ObjCIvarDecl *ivar_decl = *ivar_pos; if (ivar_decl->getName().equals(name_sref)) { if ((!omit_empty_base_classes && superclass_interface_decl) || (omit_empty_base_classes && ObjCDeclHasIVars(superclass_interface_decl, true))) ++child_idx; child_indexes.push_back(child_idx); return child_indexes.size(); } } if (superclass_interface_decl) { // The super class index is always zero for ObjC classes, // so we push it onto the child indexes in case we find // an ivar in our superclass... child_indexes.push_back(0); CompilerType superclass_clang_type( getASTContext(), getASTContext()->getObjCInterfaceType( superclass_interface_decl)); if (superclass_clang_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes)) { // We did find an ivar in a superclass so just // return the results! return child_indexes.size(); } // We didn't find an ivar matching "name" in our // superclass, pop the superclass zero index that // we pushed on above. child_indexes.pop_back(); } } } } break; case clang::Type::ObjCObjectPointer: { CompilerType objc_object_clang_type( getASTContext(), llvm::cast(qual_type.getTypePtr()) ->getPointeeType()); return objc_object_clang_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes); } break; case clang::Type::ConstantArray: { // const clang::ConstantArrayType *array = // llvm::cast(parent_qual_type.getTypePtr()); // const uint64_t element_count = // array->getSize().getLimitedValue(); // // if (idx < element_count) // { // std::pair field_type_info = // ast->getTypeInfo(array->getElementType()); // // char element_name[32]; // ::snprintf (element_name, sizeof (element_name), // "%s[%u]", parent_name ? parent_name : "", idx); // // child_name.assign(element_name); // assert(field_type_info.first % 8 == 0); // child_byte_size = field_type_info.first / 8; // child_byte_offset = idx * child_byte_size; // return array->getElementType().getAsOpaquePtr(); // } } break; // case clang::Type::MemberPointerType: // { // MemberPointerType *mem_ptr_type = // llvm::cast(qual_type.getTypePtr()); // clang::QualType pointee_type = // mem_ptr_type->getPointeeType(); // // if (ClangASTContext::IsAggregateType // (pointee_type.getAsOpaquePtr())) // { // return GetIndexOfChildWithName (ast, // mem_ptr_type->getPointeeType().getAsOpaquePtr(), // name); // } // } // break; // case clang::Type::LValueReference: case clang::Type::RValueReference: { const clang::ReferenceType *reference_type = llvm::cast(qual_type.getTypePtr()); clang::QualType pointee_type(reference_type->getPointeeType()); CompilerType pointee_clang_type(getASTContext(), pointee_type); if (pointee_clang_type.IsAggregateType()) { return pointee_clang_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes); } } break; case clang::Type::Pointer: { CompilerType pointee_clang_type(GetPointeeType(type)); if (pointee_clang_type.IsAggregateType()) { return pointee_clang_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes); } } break; case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetIndexOfChildMemberWithName(name, omit_empty_base_classes, child_indexes); case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetIndexOfChildMemberWithName(name, omit_empty_base_classes, child_indexes); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetIndexOfChildMemberWithName(name, omit_empty_base_classes, child_indexes); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetIndexOfChildMemberWithName(name, omit_empty_base_classes, child_indexes); default: break; } } return 0; } // Get the index of the child of "clang_type" whose name matches. This function // doesn't descend into the children, but only looks one level deep and name // matches can include base class names. uint32_t ClangASTContext::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type, const char *name, bool omit_empty_base_classes) { if (type && name && name[0]) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); uint32_t child_idx = 0; const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) { clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { // Skip empty base classes clang::CXXRecordDecl *base_class_decl = llvm::cast( base_class->getType() ->getAs() ->getDecl()); if (omit_empty_base_classes && ClangASTContext::RecordHasFields(base_class_decl) == false) continue; CompilerType base_class_clang_type(getASTContext(), base_class->getType()); std::string base_class_type_name( base_class_clang_type.GetTypeName().AsCString("")); if (base_class_type_name.compare(name) == 0) return child_idx; ++child_idx; } } // Try and find a field that matches NAME clang::RecordDecl::field_iterator field, field_end; llvm::StringRef name_sref(name); for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++child_idx) { if (field->getName().equals(name_sref)) return child_idx; } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteType(type)) { llvm::StringRef name_sref(name); const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { uint32_t child_idx = 0; clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { clang::ObjCInterfaceDecl::ivar_iterator ivar_pos, ivar_end = class_interface_decl->ivar_end(); clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end; ++ivar_pos, ++child_idx) { const clang::ObjCIvarDecl *ivar_decl = *ivar_pos; if (ivar_decl->getName().equals(name_sref)) { if ((!omit_empty_base_classes && superclass_interface_decl) || (omit_empty_base_classes && ObjCDeclHasIVars(superclass_interface_decl, true))) ++child_idx; return child_idx; } } if (superclass_interface_decl) { if (superclass_interface_decl->getName().equals(name_sref)) return 0; } } } } break; case clang::Type::ObjCObjectPointer: { CompilerType pointee_clang_type( getASTContext(), llvm::cast(qual_type.getTypePtr()) ->getPointeeType()); return pointee_clang_type.GetIndexOfChildWithName( name, omit_empty_base_classes); } break; case clang::Type::ConstantArray: { // const clang::ConstantArrayType *array = // llvm::cast(parent_qual_type.getTypePtr()); // const uint64_t element_count = // array->getSize().getLimitedValue(); // // if (idx < element_count) // { // std::pair field_type_info = // ast->getTypeInfo(array->getElementType()); // // char element_name[32]; // ::snprintf (element_name, sizeof (element_name), // "%s[%u]", parent_name ? parent_name : "", idx); // // child_name.assign(element_name); // assert(field_type_info.first % 8 == 0); // child_byte_size = field_type_info.first / 8; // child_byte_offset = idx * child_byte_size; // return array->getElementType().getAsOpaquePtr(); // } } break; // case clang::Type::MemberPointerType: // { // MemberPointerType *mem_ptr_type = // llvm::cast(qual_type.getTypePtr()); // clang::QualType pointee_type = // mem_ptr_type->getPointeeType(); // // if (ClangASTContext::IsAggregateType // (pointee_type.getAsOpaquePtr())) // { // return GetIndexOfChildWithName (ast, // mem_ptr_type->getPointeeType().getAsOpaquePtr(), // name); // } // } // break; // case clang::Type::LValueReference: case clang::Type::RValueReference: { const clang::ReferenceType *reference_type = llvm::cast(qual_type.getTypePtr()); CompilerType pointee_type(getASTContext(), reference_type->getPointeeType()); if (pointee_type.IsAggregateType()) { return pointee_type.GetIndexOfChildWithName(name, omit_empty_base_classes); } } break; case clang::Type::Pointer: { const clang::PointerType *pointer_type = llvm::cast(qual_type.getTypePtr()); CompilerType pointee_type(getASTContext(), pointer_type->getPointeeType()); if (pointee_type.IsAggregateType()) { return pointee_type.GetIndexOfChildWithName(name, omit_empty_base_classes); } else { // if (parent_name) // { // child_name.assign(1, '*'); // child_name += parent_name; // } // // // We have a pointer to an simple type // if (idx == 0) // { // std::pair clang_type_info // = ast->getTypeInfo(pointee_type); // assert(clang_type_info.first % 8 == 0); // child_byte_size = clang_type_info.first / 8; // child_byte_offset = 0; // return pointee_type.getAsOpaquePtr(); // } } } break; case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetIndexOfChildWithName(name, omit_empty_base_classes); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetIndexOfChildWithName(name, omit_empty_base_classes); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetIndexOfChildWithName(name, omit_empty_base_classes); case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetIndexOfChildWithName(name, omit_empty_base_classes); default: break; } } return UINT32_MAX; } size_t ClangASTContext::GetNumTemplateArguments(lldb::opaque_compiler_type_t type) { if (!type) return 0; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { const clang::ClassTemplateSpecializationDecl *template_decl = llvm::dyn_cast( cxx_record_decl); if (template_decl) return template_decl->getTemplateArgs().size(); } } break; case clang::Type::Typedef: return (CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType())) .GetNumTemplateArguments(); case clang::Type::Auto: return (CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType())) .GetNumTemplateArguments(); case clang::Type::Elaborated: return (CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType())) .GetNumTemplateArguments(); case clang::Type::Paren: return (CompilerType(getASTContext(), llvm::cast(qual_type)->desugar())) .GetNumTemplateArguments(); default: break; } return 0; } CompilerType ClangASTContext::GetTemplateArgument(lldb::opaque_compiler_type_t type, size_t arg_idx, lldb::TemplateArgumentKind &kind) { if (!type) return CompilerType(); clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { const clang::ClassTemplateSpecializationDecl *template_decl = llvm::dyn_cast( cxx_record_decl); if (template_decl && arg_idx < template_decl->getTemplateArgs().size()) { const clang::TemplateArgument &template_arg = template_decl->getTemplateArgs()[arg_idx]; switch (template_arg.getKind()) { case clang::TemplateArgument::Null: kind = eTemplateArgumentKindNull; return CompilerType(); case clang::TemplateArgument::Type: kind = eTemplateArgumentKindType; return CompilerType(getASTContext(), template_arg.getAsType()); case clang::TemplateArgument::Declaration: kind = eTemplateArgumentKindDeclaration; return CompilerType(); case clang::TemplateArgument::Integral: kind = eTemplateArgumentKindIntegral; return CompilerType(getASTContext(), template_arg.getIntegralType()); case clang::TemplateArgument::Template: kind = eTemplateArgumentKindTemplate; return CompilerType(); case clang::TemplateArgument::TemplateExpansion: kind = eTemplateArgumentKindTemplateExpansion; return CompilerType(); case clang::TemplateArgument::Expression: kind = eTemplateArgumentKindExpression; return CompilerType(); case clang::TemplateArgument::Pack: kind = eTemplateArgumentKindPack; return CompilerType(); default: - assert(!"Unhandled clang::TemplateArgument::ArgKind"); - break; + llvm_unreachable("Unhandled clang::TemplateArgument::ArgKind"); } } } } break; case clang::Type::Typedef: return (CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType())) .GetTemplateArgument(arg_idx, kind); case clang::Type::Auto: return (CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType())) .GetTemplateArgument(arg_idx, kind); case clang::Type::Elaborated: return (CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType())) .GetTemplateArgument(arg_idx, kind); case clang::Type::Paren: return (CompilerType(getASTContext(), llvm::cast(qual_type)->desugar())) .GetTemplateArgument(arg_idx, kind); default: break; } kind = eTemplateArgumentKindNull; return CompilerType(); } CompilerType ClangASTContext::GetTypeForFormatters(void *type) { if (type) return ClangUtil::RemoveFastQualifiers(CompilerType(this, type)); return CompilerType(); } clang::EnumDecl *ClangASTContext::GetAsEnumDecl(const CompilerType &type) { const clang::EnumType *enutype = llvm::dyn_cast(ClangUtil::GetCanonicalQualType(type)); if (enutype) return enutype->getDecl(); return NULL; } clang::RecordDecl *ClangASTContext::GetAsRecordDecl(const CompilerType &type) { const clang::RecordType *record_type = llvm::dyn_cast(ClangUtil::GetCanonicalQualType(type)); if (record_type) return record_type->getDecl(); return nullptr; } clang::TagDecl *ClangASTContext::GetAsTagDecl(const CompilerType &type) { clang::QualType qual_type = ClangUtil::GetCanonicalQualType(type); if (qual_type.isNull()) return nullptr; else return qual_type->getAsTagDecl(); } clang::CXXRecordDecl * ClangASTContext::GetAsCXXRecordDecl(lldb::opaque_compiler_type_t type) { return GetCanonicalQualType(type)->getAsCXXRecordDecl(); } clang::ObjCInterfaceDecl * ClangASTContext::GetAsObjCInterfaceDecl(const CompilerType &type) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast( ClangUtil::GetCanonicalQualType(type)); if (objc_class_type) return objc_class_type->getInterface(); return nullptr; } clang::FieldDecl *ClangASTContext::AddFieldToRecordType( const CompilerType &type, const char *name, const CompilerType &field_clang_type, AccessType access, uint32_t bitfield_bit_size) { if (!type.IsValid() || !field_clang_type.IsValid()) return nullptr; ClangASTContext *ast = llvm::dyn_cast_or_null(type.GetTypeSystem()); if (!ast) return nullptr; clang::ASTContext *clang_ast = ast->getASTContext(); clang::FieldDecl *field = nullptr; clang::Expr *bit_width = nullptr; if (bitfield_bit_size != 0) { llvm::APInt bitfield_bit_size_apint( clang_ast->getTypeSize(clang_ast->IntTy), bitfield_bit_size); bit_width = new (*clang_ast) clang::IntegerLiteral(*clang_ast, bitfield_bit_size_apint, clang_ast->IntTy, clang::SourceLocation()); } clang::RecordDecl *record_decl = ast->GetAsRecordDecl(type); if (record_decl) { field = clang::FieldDecl::Create( *clang_ast, record_decl, clang::SourceLocation(), clang::SourceLocation(), name ? &clang_ast->Idents.get(name) : nullptr, // Identifier ClangUtil::GetQualType(field_clang_type), // Field type nullptr, // TInfo * bit_width, // BitWidth false, // Mutable clang::ICIS_NoInit); // HasInit if (!name) { // Determine whether this field corresponds to an anonymous // struct or union. if (const clang::TagType *TagT = field->getType()->getAs()) { if (clang::RecordDecl *Rec = llvm::dyn_cast(TagT->getDecl())) if (!Rec->getDeclName()) { Rec->setAnonymousStructOrUnion(true); field->setImplicit(); } } } if (field) { field->setAccess( ClangASTContext::ConvertAccessTypeToAccessSpecifier(access)); record_decl->addDecl(field); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(field); #endif } } else { clang::ObjCInterfaceDecl *class_interface_decl = ast->GetAsObjCInterfaceDecl(type); if (class_interface_decl) { const bool is_synthesized = false; field_clang_type.GetCompleteType(); field = clang::ObjCIvarDecl::Create( *clang_ast, class_interface_decl, clang::SourceLocation(), clang::SourceLocation(), name ? &clang_ast->Idents.get(name) : nullptr, // Identifier ClangUtil::GetQualType(field_clang_type), // Field type nullptr, // TypeSourceInfo * ConvertAccessTypeToObjCIvarAccessControl(access), bit_width, is_synthesized); if (field) { class_interface_decl->addDecl(field); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(field); #endif } } } return field; } void ClangASTContext::BuildIndirectFields(const CompilerType &type) { if (!type) return; ClangASTContext *ast = llvm::dyn_cast(type.GetTypeSystem()); if (!ast) return; clang::RecordDecl *record_decl = ast->GetAsRecordDecl(type); if (!record_decl) return; typedef llvm::SmallVector IndirectFieldVector; IndirectFieldVector indirect_fields; clang::RecordDecl::field_iterator field_pos; clang::RecordDecl::field_iterator field_end_pos = record_decl->field_end(); clang::RecordDecl::field_iterator last_field_pos = field_end_pos; for (field_pos = record_decl->field_begin(); field_pos != field_end_pos; last_field_pos = field_pos++) { if (field_pos->isAnonymousStructOrUnion()) { clang::QualType field_qual_type = field_pos->getType(); const clang::RecordType *field_record_type = field_qual_type->getAs(); if (!field_record_type) continue; clang::RecordDecl *field_record_decl = field_record_type->getDecl(); if (!field_record_decl) continue; for (clang::RecordDecl::decl_iterator di = field_record_decl->decls_begin(), de = field_record_decl->decls_end(); di != de; ++di) { if (clang::FieldDecl *nested_field_decl = llvm::dyn_cast(*di)) { clang::NamedDecl **chain = new (*ast->getASTContext()) clang::NamedDecl *[2]; chain[0] = *field_pos; chain[1] = nested_field_decl; clang::IndirectFieldDecl *indirect_field = clang::IndirectFieldDecl::Create( *ast->getASTContext(), record_decl, clang::SourceLocation(), nested_field_decl->getIdentifier(), nested_field_decl->getType(), {chain, 2}); indirect_field->setImplicit(); indirect_field->setAccess(ClangASTContext::UnifyAccessSpecifiers( field_pos->getAccess(), nested_field_decl->getAccess())); indirect_fields.push_back(indirect_field); } else if (clang::IndirectFieldDecl *nested_indirect_field_decl = llvm::dyn_cast(*di)) { size_t nested_chain_size = nested_indirect_field_decl->getChainingSize(); clang::NamedDecl **chain = new (*ast->getASTContext()) clang::NamedDecl *[nested_chain_size + 1]; chain[0] = *field_pos; int chain_index = 1; for (clang::IndirectFieldDecl::chain_iterator nci = nested_indirect_field_decl->chain_begin(), nce = nested_indirect_field_decl->chain_end(); nci < nce; ++nci) { chain[chain_index] = *nci; chain_index++; } clang::IndirectFieldDecl *indirect_field = clang::IndirectFieldDecl::Create( *ast->getASTContext(), record_decl, clang::SourceLocation(), nested_indirect_field_decl->getIdentifier(), nested_indirect_field_decl->getType(), {chain, nested_chain_size + 1}); indirect_field->setImplicit(); indirect_field->setAccess(ClangASTContext::UnifyAccessSpecifiers( field_pos->getAccess(), nested_indirect_field_decl->getAccess())); indirect_fields.push_back(indirect_field); } } } } // Check the last field to see if it has an incomplete array type as its // last member and if it does, the tell the record decl about it if (last_field_pos != field_end_pos) { if (last_field_pos->getType()->isIncompleteArrayType()) record_decl->hasFlexibleArrayMember(); } for (IndirectFieldVector::iterator ifi = indirect_fields.begin(), ife = indirect_fields.end(); ifi < ife; ++ifi) { record_decl->addDecl(*ifi); } } void ClangASTContext::SetIsPacked(const CompilerType &type) { if (type) { ClangASTContext *ast = llvm::dyn_cast(type.GetTypeSystem()); if (ast) { clang::RecordDecl *record_decl = GetAsRecordDecl(type); if (!record_decl) return; record_decl->addAttr( clang::PackedAttr::CreateImplicit(*ast->getASTContext())); } } } clang::VarDecl *ClangASTContext::AddVariableToRecordType( const CompilerType &type, const char *name, const CompilerType &var_type, AccessType access) { clang::VarDecl *var_decl = nullptr; if (!type.IsValid() || !var_type.IsValid()) return nullptr; ClangASTContext *ast = llvm::dyn_cast(type.GetTypeSystem()); if (!ast) return nullptr; clang::RecordDecl *record_decl = ast->GetAsRecordDecl(type); if (record_decl) { var_decl = clang::VarDecl::Create( *ast->getASTContext(), // ASTContext & record_decl, // DeclContext * clang::SourceLocation(), // clang::SourceLocation StartLoc clang::SourceLocation(), // clang::SourceLocation IdLoc name ? &ast->getASTContext()->Idents.get(name) : nullptr, // clang::IdentifierInfo * ClangUtil::GetQualType(var_type), // Variable clang::QualType nullptr, // TypeSourceInfo * clang::SC_Static); // StorageClass if (var_decl) { var_decl->setAccess( ClangASTContext::ConvertAccessTypeToAccessSpecifier(access)); record_decl->addDecl(var_decl); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(var_decl); #endif } } return var_decl; } clang::CXXMethodDecl *ClangASTContext::AddMethodToCXXRecordType( lldb::opaque_compiler_type_t type, const char *name, const CompilerType &method_clang_type, lldb::AccessType access, bool is_virtual, bool is_static, bool is_inline, bool is_explicit, bool is_attr_used, bool is_artificial) { if (!type || !method_clang_type.IsValid() || name == nullptr || name[0] == '\0') return nullptr; clang::QualType record_qual_type(GetCanonicalQualType(type)); clang::CXXRecordDecl *cxx_record_decl = record_qual_type->getAsCXXRecordDecl(); if (cxx_record_decl == nullptr) return nullptr; clang::QualType method_qual_type(ClangUtil::GetQualType(method_clang_type)); clang::CXXMethodDecl *cxx_method_decl = nullptr; clang::DeclarationName decl_name(&getASTContext()->Idents.get(name)); const clang::FunctionType *function_type = llvm::dyn_cast(method_qual_type.getTypePtr()); if (function_type == nullptr) return nullptr; const clang::FunctionProtoType *method_function_prototype( llvm::dyn_cast(function_type)); if (!method_function_prototype) return nullptr; unsigned int num_params = method_function_prototype->getNumParams(); clang::CXXDestructorDecl *cxx_dtor_decl(nullptr); clang::CXXConstructorDecl *cxx_ctor_decl(nullptr); if (is_artificial) return nullptr; // skip everything artificial if (name[0] == '~') { cxx_dtor_decl = clang::CXXDestructorDecl::Create( *getASTContext(), cxx_record_decl, clang::SourceLocation(), clang::DeclarationNameInfo( getASTContext()->DeclarationNames.getCXXDestructorName( getASTContext()->getCanonicalType(record_qual_type)), clang::SourceLocation()), method_qual_type, nullptr, is_inline, is_artificial); cxx_method_decl = cxx_dtor_decl; } else if (decl_name == cxx_record_decl->getDeclName()) { cxx_ctor_decl = clang::CXXConstructorDecl::Create( *getASTContext(), cxx_record_decl, clang::SourceLocation(), clang::DeclarationNameInfo( getASTContext()->DeclarationNames.getCXXConstructorName( getASTContext()->getCanonicalType(record_qual_type)), clang::SourceLocation()), method_qual_type, nullptr, // TypeSourceInfo * is_explicit, is_inline, is_artificial, false /*is_constexpr*/); cxx_method_decl = cxx_ctor_decl; } else { clang::StorageClass SC = is_static ? clang::SC_Static : clang::SC_None; clang::OverloadedOperatorKind op_kind = clang::NUM_OVERLOADED_OPERATORS; if (IsOperator(name, op_kind)) { if (op_kind != clang::NUM_OVERLOADED_OPERATORS) { // Check the number of operator parameters. Sometimes we have // seen bad DWARF that doesn't correctly describe operators and // if we try to create a method and add it to the class, clang // will assert and crash, so we need to make sure things are // acceptable. const bool is_method = true; if (!ClangASTContext::CheckOverloadedOperatorKindParameterCount( is_method, op_kind, num_params)) return nullptr; cxx_method_decl = clang::CXXMethodDecl::Create( *getASTContext(), cxx_record_decl, clang::SourceLocation(), clang::DeclarationNameInfo( getASTContext()->DeclarationNames.getCXXOperatorName(op_kind), clang::SourceLocation()), method_qual_type, nullptr, // TypeSourceInfo * SC, is_inline, false /*is_constexpr*/, clang::SourceLocation()); } else if (num_params == 0) { // Conversion operators don't take params... cxx_method_decl = clang::CXXConversionDecl::Create( *getASTContext(), cxx_record_decl, clang::SourceLocation(), clang::DeclarationNameInfo( getASTContext()->DeclarationNames.getCXXConversionFunctionName( getASTContext()->getCanonicalType( function_type->getReturnType())), clang::SourceLocation()), method_qual_type, nullptr, // TypeSourceInfo * is_inline, is_explicit, false /*is_constexpr*/, clang::SourceLocation()); } } if (cxx_method_decl == nullptr) { cxx_method_decl = clang::CXXMethodDecl::Create( *getASTContext(), cxx_record_decl, clang::SourceLocation(), clang::DeclarationNameInfo(decl_name, clang::SourceLocation()), method_qual_type, nullptr, // TypeSourceInfo * SC, is_inline, false /*is_constexpr*/, clang::SourceLocation()); } } clang::AccessSpecifier access_specifier = ClangASTContext::ConvertAccessTypeToAccessSpecifier(access); cxx_method_decl->setAccess(access_specifier); cxx_method_decl->setVirtualAsWritten(is_virtual); if (is_attr_used) cxx_method_decl->addAttr(clang::UsedAttr::CreateImplicit(*getASTContext())); // Populate the method decl with parameter decls llvm::SmallVector params; for (unsigned param_index = 0; param_index < num_params; ++param_index) { params.push_back(clang::ParmVarDecl::Create( *getASTContext(), cxx_method_decl, clang::SourceLocation(), clang::SourceLocation(), nullptr, // anonymous method_function_prototype->getParamType(param_index), nullptr, clang::SC_None, nullptr)); } cxx_method_decl->setParams(llvm::ArrayRef(params)); cxx_record_decl->addDecl(cxx_method_decl); // Sometimes the debug info will mention a constructor (default/copy/move), // destructor, or assignment operator (copy/move) but there won't be any // version of this in the code. So we check if the function was artificially // generated and if it is trivial and this lets the compiler/backend know // that it can inline the IR for these when it needs to and we can avoid a // "missing function" error when running expressions. if (is_artificial) { if (cxx_ctor_decl && ((cxx_ctor_decl->isDefaultConstructor() && cxx_record_decl->hasTrivialDefaultConstructor()) || (cxx_ctor_decl->isCopyConstructor() && cxx_record_decl->hasTrivialCopyConstructor()) || (cxx_ctor_decl->isMoveConstructor() && cxx_record_decl->hasTrivialMoveConstructor()))) { cxx_ctor_decl->setDefaulted(); cxx_ctor_decl->setTrivial(true); } else if (cxx_dtor_decl) { if (cxx_record_decl->hasTrivialDestructor()) { cxx_dtor_decl->setDefaulted(); cxx_dtor_decl->setTrivial(true); } } else if ((cxx_method_decl->isCopyAssignmentOperator() && cxx_record_decl->hasTrivialCopyAssignment()) || (cxx_method_decl->isMoveAssignmentOperator() && cxx_record_decl->hasTrivialMoveAssignment())) { cxx_method_decl->setDefaulted(); cxx_method_decl->setTrivial(true); } } #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(cxx_method_decl); #endif // printf ("decl->isPolymorphic() = %i\n", // cxx_record_decl->isPolymorphic()); // printf ("decl->isAggregate() = %i\n", // cxx_record_decl->isAggregate()); // printf ("decl->isPOD() = %i\n", // cxx_record_decl->isPOD()); // printf ("decl->isEmpty() = %i\n", // cxx_record_decl->isEmpty()); // printf ("decl->isAbstract() = %i\n", // cxx_record_decl->isAbstract()); // printf ("decl->hasTrivialConstructor() = %i\n", // cxx_record_decl->hasTrivialConstructor()); // printf ("decl->hasTrivialCopyConstructor() = %i\n", // cxx_record_decl->hasTrivialCopyConstructor()); // printf ("decl->hasTrivialCopyAssignment() = %i\n", // cxx_record_decl->hasTrivialCopyAssignment()); // printf ("decl->hasTrivialDestructor() = %i\n", // cxx_record_decl->hasTrivialDestructor()); return cxx_method_decl; } #pragma mark C++ Base Classes clang::CXXBaseSpecifier * ClangASTContext::CreateBaseClassSpecifier(lldb::opaque_compiler_type_t type, AccessType access, bool is_virtual, bool base_of_class) { if (type) return new clang::CXXBaseSpecifier( clang::SourceRange(), is_virtual, base_of_class, ClangASTContext::ConvertAccessTypeToAccessSpecifier(access), getASTContext()->getTrivialTypeSourceInfo(GetQualType(type)), clang::SourceLocation()); return nullptr; } void ClangASTContext::DeleteBaseClassSpecifiers( clang::CXXBaseSpecifier **base_classes, unsigned num_base_classes) { for (unsigned i = 0; i < num_base_classes; ++i) { delete base_classes[i]; base_classes[i] = nullptr; } } bool ClangASTContext::SetBaseClassesForClassType( lldb::opaque_compiler_type_t type, clang::CXXBaseSpecifier const *const *base_classes, unsigned num_base_classes) { if (type) { clang::CXXRecordDecl *cxx_record_decl = GetAsCXXRecordDecl(type); if (cxx_record_decl) { cxx_record_decl->setBases(base_classes, num_base_classes); return true; } } return false; } bool ClangASTContext::SetObjCSuperClass( const CompilerType &type, const CompilerType &superclass_clang_type) { ClangASTContext *ast = llvm::dyn_cast_or_null(type.GetTypeSystem()); if (!ast) return false; clang::ASTContext *clang_ast = ast->getASTContext(); if (type && superclass_clang_type.IsValid() && superclass_clang_type.GetTypeSystem() == type.GetTypeSystem()) { clang::ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(type); clang::ObjCInterfaceDecl *super_interface_decl = GetAsObjCInterfaceDecl(superclass_clang_type); if (class_interface_decl && super_interface_decl) { class_interface_decl->setSuperClass(clang_ast->getTrivialTypeSourceInfo( clang_ast->getObjCInterfaceType(super_interface_decl))); return true; } } return false; } bool ClangASTContext::AddObjCClassProperty( const CompilerType &type, const char *property_name, const CompilerType &property_clang_type, clang::ObjCIvarDecl *ivar_decl, const char *property_setter_name, const char *property_getter_name, uint32_t property_attributes, ClangASTMetadata *metadata) { if (!type || !property_clang_type.IsValid() || property_name == nullptr || property_name[0] == '\0') return false; ClangASTContext *ast = llvm::dyn_cast(type.GetTypeSystem()); if (!ast) return false; clang::ASTContext *clang_ast = ast->getASTContext(); clang::ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(type); if (class_interface_decl) { CompilerType property_clang_type_to_access; if (property_clang_type.IsValid()) property_clang_type_to_access = property_clang_type; else if (ivar_decl) property_clang_type_to_access = CompilerType(clang_ast, ivar_decl->getType()); if (class_interface_decl && property_clang_type_to_access.IsValid()) { clang::TypeSourceInfo *prop_type_source; if (ivar_decl) prop_type_source = clang_ast->getTrivialTypeSourceInfo(ivar_decl->getType()); else prop_type_source = clang_ast->getTrivialTypeSourceInfo( ClangUtil::GetQualType(property_clang_type)); clang::ObjCPropertyDecl *property_decl = clang::ObjCPropertyDecl::Create( *clang_ast, class_interface_decl, clang::SourceLocation(), // Source Location &clang_ast->Idents.get(property_name), clang::SourceLocation(), // Source Location for AT clang::SourceLocation(), // Source location for ( ivar_decl ? ivar_decl->getType() : ClangUtil::GetQualType(property_clang_type), prop_type_source); if (property_decl) { if (metadata) ClangASTContext::SetMetadata(clang_ast, property_decl, *metadata); class_interface_decl->addDecl(property_decl); clang::Selector setter_sel, getter_sel; if (property_setter_name != nullptr) { std::string property_setter_no_colon( property_setter_name, strlen(property_setter_name) - 1); clang::IdentifierInfo *setter_ident = &clang_ast->Idents.get(property_setter_no_colon); setter_sel = clang_ast->Selectors.getSelector(1, &setter_ident); } else if (!(property_attributes & DW_APPLE_PROPERTY_readonly)) { std::string setter_sel_string("set"); setter_sel_string.push_back(::toupper(property_name[0])); setter_sel_string.append(&property_name[1]); clang::IdentifierInfo *setter_ident = &clang_ast->Idents.get(setter_sel_string); setter_sel = clang_ast->Selectors.getSelector(1, &setter_ident); } property_decl->setSetterName(setter_sel); property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_setter); if (property_getter_name != nullptr) { clang::IdentifierInfo *getter_ident = &clang_ast->Idents.get(property_getter_name); getter_sel = clang_ast->Selectors.getSelector(0, &getter_ident); } else { clang::IdentifierInfo *getter_ident = &clang_ast->Idents.get(property_name); getter_sel = clang_ast->Selectors.getSelector(0, &getter_ident); } property_decl->setGetterName(getter_sel); property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_getter); if (ivar_decl) property_decl->setPropertyIvarDecl(ivar_decl); if (property_attributes & DW_APPLE_PROPERTY_readonly) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_readonly); if (property_attributes & DW_APPLE_PROPERTY_readwrite) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_readwrite); if (property_attributes & DW_APPLE_PROPERTY_assign) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_assign); if (property_attributes & DW_APPLE_PROPERTY_retain) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_retain); if (property_attributes & DW_APPLE_PROPERTY_copy) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_copy); if (property_attributes & DW_APPLE_PROPERTY_nonatomic) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_nonatomic); if (property_attributes & clang::ObjCPropertyDecl::OBJC_PR_nullability) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_nullability); if (property_attributes & clang::ObjCPropertyDecl::OBJC_PR_null_resettable) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_null_resettable); if (property_attributes & clang::ObjCPropertyDecl::OBJC_PR_class) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_class); const bool isInstance = (property_attributes & clang::ObjCPropertyDecl::OBJC_PR_class) == 0; if (!getter_sel.isNull() && !(isInstance ? class_interface_decl->lookupInstanceMethod(getter_sel) : class_interface_decl->lookupClassMethod(getter_sel))) { const bool isVariadic = false; const bool isSynthesized = false; const bool isImplicitlyDeclared = true; const bool isDefined = false; const clang::ObjCMethodDecl::ImplementationControl impControl = clang::ObjCMethodDecl::None; const bool HasRelatedResultType = false; clang::ObjCMethodDecl *getter = clang::ObjCMethodDecl::Create( *clang_ast, clang::SourceLocation(), clang::SourceLocation(), getter_sel, ClangUtil::GetQualType(property_clang_type_to_access), nullptr, class_interface_decl, isInstance, isVariadic, isSynthesized, isImplicitlyDeclared, isDefined, impControl, HasRelatedResultType); if (getter && metadata) ClangASTContext::SetMetadata(clang_ast, getter, *metadata); if (getter) { getter->setMethodParams(*clang_ast, llvm::ArrayRef(), llvm::ArrayRef()); class_interface_decl->addDecl(getter); } } if (!setter_sel.isNull() && !(isInstance ? class_interface_decl->lookupInstanceMethod(setter_sel) : class_interface_decl->lookupClassMethod(setter_sel))) { clang::QualType result_type = clang_ast->VoidTy; const bool isVariadic = false; const bool isSynthesized = false; const bool isImplicitlyDeclared = true; const bool isDefined = false; const clang::ObjCMethodDecl::ImplementationControl impControl = clang::ObjCMethodDecl::None; const bool HasRelatedResultType = false; clang::ObjCMethodDecl *setter = clang::ObjCMethodDecl::Create( *clang_ast, clang::SourceLocation(), clang::SourceLocation(), setter_sel, result_type, nullptr, class_interface_decl, isInstance, isVariadic, isSynthesized, isImplicitlyDeclared, isDefined, impControl, HasRelatedResultType); if (setter && metadata) ClangASTContext::SetMetadata(clang_ast, setter, *metadata); llvm::SmallVector params; params.push_back(clang::ParmVarDecl::Create( *clang_ast, setter, clang::SourceLocation(), clang::SourceLocation(), nullptr, // anonymous ClangUtil::GetQualType(property_clang_type_to_access), nullptr, clang::SC_Auto, nullptr)); if (setter) { setter->setMethodParams( *clang_ast, llvm::ArrayRef(params), llvm::ArrayRef()); class_interface_decl->addDecl(setter); } } return true; } } } return false; } bool ClangASTContext::IsObjCClassTypeAndHasIVars(const CompilerType &type, bool check_superclass) { clang::ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(type); if (class_interface_decl) return ObjCDeclHasIVars(class_interface_decl, check_superclass); return false; } clang::ObjCMethodDecl *ClangASTContext::AddMethodToObjCObjectType( const CompilerType &type, const char *name, // the full symbol name as seen in the symbol table // (lldb::opaque_compiler_type_t type, "-[NString // stringWithCString:]") const CompilerType &method_clang_type, lldb::AccessType access, bool is_artificial, bool is_variadic) { if (!type || !method_clang_type.IsValid()) return nullptr; clang::ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(type); if (class_interface_decl == nullptr) return nullptr; ClangASTContext *lldb_ast = llvm::dyn_cast(type.GetTypeSystem()); if (lldb_ast == nullptr) return nullptr; clang::ASTContext *ast = lldb_ast->getASTContext(); const char *selector_start = ::strchr(name, ' '); if (selector_start == nullptr) return nullptr; selector_start++; llvm::SmallVector selector_idents; size_t len = 0; const char *start; // printf ("name = '%s'\n", name); unsigned num_selectors_with_args = 0; for (start = selector_start; start && *start != '\0' && *start != ']'; start += len) { len = ::strcspn(start, ":]"); bool has_arg = (start[len] == ':'); if (has_arg) ++num_selectors_with_args; selector_idents.push_back(&ast->Idents.get(llvm::StringRef(start, len))); if (has_arg) len += 1; } if (selector_idents.size() == 0) return nullptr; clang::Selector method_selector = ast->Selectors.getSelector( num_selectors_with_args ? selector_idents.size() : 0, selector_idents.data()); clang::QualType method_qual_type(ClangUtil::GetQualType(method_clang_type)); // Populate the method decl with parameter decls const clang::Type *method_type(method_qual_type.getTypePtr()); if (method_type == nullptr) return nullptr; const clang::FunctionProtoType *method_function_prototype( llvm::dyn_cast(method_type)); if (!method_function_prototype) return nullptr; bool is_synthesized = false; bool is_defined = false; clang::ObjCMethodDecl::ImplementationControl imp_control = clang::ObjCMethodDecl::None; const unsigned num_args = method_function_prototype->getNumParams(); if (num_args != num_selectors_with_args) return nullptr; // some debug information is corrupt. We are not going to // deal with it. clang::ObjCMethodDecl *objc_method_decl = clang::ObjCMethodDecl::Create( *ast, clang::SourceLocation(), // beginLoc, clang::SourceLocation(), // endLoc, method_selector, method_function_prototype->getReturnType(), nullptr, // TypeSourceInfo *ResultTInfo, ClangASTContext::GetASTContext(ast)->GetDeclContextForType( ClangUtil::GetQualType(type)), name[0] == '-', is_variadic, is_synthesized, true, // is_implicitly_declared; we force this to true because we don't // have source locations is_defined, imp_control, false /*has_related_result_type*/); if (objc_method_decl == nullptr) return nullptr; if (num_args > 0) { llvm::SmallVector params; for (unsigned param_index = 0; param_index < num_args; ++param_index) { params.push_back(clang::ParmVarDecl::Create( *ast, objc_method_decl, clang::SourceLocation(), clang::SourceLocation(), nullptr, // anonymous method_function_prototype->getParamType(param_index), nullptr, clang::SC_Auto, nullptr)); } objc_method_decl->setMethodParams( *ast, llvm::ArrayRef(params), llvm::ArrayRef()); } class_interface_decl->addDecl(objc_method_decl); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(objc_method_decl); #endif return objc_method_decl; } bool ClangASTContext::GetHasExternalStorage(const CompilerType &type) { if (ClangUtil::IsClangType(type)) return false; clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: { clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) return cxx_record_decl->hasExternalLexicalStorage() || cxx_record_decl->hasExternalVisibleStorage(); } break; case clang::Type::Enum: { clang::EnumDecl *enum_decl = llvm::cast(qual_type)->getDecl(); if (enum_decl) return enum_decl->hasExternalLexicalStorage() || enum_decl->hasExternalVisibleStorage(); } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) return class_interface_decl->hasExternalLexicalStorage() || class_interface_decl->hasExternalVisibleStorage(); } } break; case clang::Type::Typedef: return GetHasExternalStorage(CompilerType( type.GetTypeSystem(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr())); case clang::Type::Auto: return GetHasExternalStorage(CompilerType( type.GetTypeSystem(), llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr())); case clang::Type::Elaborated: return GetHasExternalStorage(CompilerType( type.GetTypeSystem(), llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr())); case clang::Type::Paren: return GetHasExternalStorage(CompilerType( type.GetTypeSystem(), llvm::cast(qual_type)->desugar().getAsOpaquePtr())); default: break; } return false; } bool ClangASTContext::SetHasExternalStorage(lldb::opaque_compiler_type_t type, bool has_extern) { if (!type) return false; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: { clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { cxx_record_decl->setHasExternalLexicalStorage(has_extern); cxx_record_decl->setHasExternalVisibleStorage(has_extern); return true; } } break; case clang::Type::Enum: { clang::EnumDecl *enum_decl = llvm::cast(qual_type)->getDecl(); if (enum_decl) { enum_decl->setHasExternalLexicalStorage(has_extern); enum_decl->setHasExternalVisibleStorage(has_extern); return true; } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { class_interface_decl->setHasExternalLexicalStorage(has_extern); class_interface_decl->setHasExternalVisibleStorage(has_extern); return true; } } } break; case clang::Type::Typedef: return SetHasExternalStorage(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), has_extern); case clang::Type::Auto: return SetHasExternalStorage(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), has_extern); case clang::Type::Elaborated: return SetHasExternalStorage(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), has_extern); case clang::Type::Paren: return SetHasExternalStorage( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), has_extern); default: break; } return false; } #pragma mark TagDecl bool ClangASTContext::StartTagDeclarationDefinition(const CompilerType &type) { clang::QualType qual_type(ClangUtil::GetQualType(type)); if (!qual_type.isNull()) { const clang::TagType *tag_type = qual_type->getAs(); if (tag_type) { clang::TagDecl *tag_decl = tag_type->getDecl(); if (tag_decl) { tag_decl->startDefinition(); return true; } } const clang::ObjCObjectType *object_type = qual_type->getAs(); if (object_type) { clang::ObjCInterfaceDecl *interface_decl = object_type->getInterface(); if (interface_decl) { interface_decl->startDefinition(); return true; } } } return false; } bool ClangASTContext::CompleteTagDeclarationDefinition( const CompilerType &type) { clang::QualType qual_type(ClangUtil::GetQualType(type)); if (!qual_type.isNull()) { // Make sure we use the same methodology as // ClangASTContext::StartTagDeclarationDefinition() // as to how we start/end the definition. Previously we were calling const clang::TagType *tag_type = qual_type->getAs(); if (tag_type) { clang::TagDecl *tag_decl = tag_type->getDecl(); if (tag_decl) { clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast_or_null(tag_decl); if (cxx_record_decl) { if (!cxx_record_decl->isCompleteDefinition()) cxx_record_decl->completeDefinition(); cxx_record_decl->setHasLoadedFieldsFromExternalStorage(true); cxx_record_decl->setHasExternalLexicalStorage(false); cxx_record_decl->setHasExternalVisibleStorage(false); return true; } } } const clang::EnumType *enutype = qual_type->getAs(); if (enutype) { clang::EnumDecl *enum_decl = enutype->getDecl(); if (enum_decl) { if (!enum_decl->isCompleteDefinition()) { ClangASTContext *lldb_ast = llvm::dyn_cast(type.GetTypeSystem()); if (lldb_ast == nullptr) return false; clang::ASTContext *ast = lldb_ast->getASTContext(); /// TODO This really needs to be fixed. QualType integer_type(enum_decl->getIntegerType()); if (!integer_type.isNull()) { unsigned NumPositiveBits = 1; unsigned NumNegativeBits = 0; clang::QualType promotion_qual_type; // If the enum integer type is less than an integer in bit width, // then we must promote it to an integer size. if (ast->getTypeSize(enum_decl->getIntegerType()) < ast->getTypeSize(ast->IntTy)) { if (enum_decl->getIntegerType()->isSignedIntegerType()) promotion_qual_type = ast->IntTy; else promotion_qual_type = ast->UnsignedIntTy; } else promotion_qual_type = enum_decl->getIntegerType(); enum_decl->completeDefinition(enum_decl->getIntegerType(), promotion_qual_type, NumPositiveBits, NumNegativeBits); } } return true; } } } return false; } bool ClangASTContext::AddEnumerationValueToEnumerationType( lldb::opaque_compiler_type_t type, const CompilerType &enumerator_clang_type, const Declaration &decl, const char *name, int64_t enum_value, uint32_t enum_value_bit_size) { if (type && enumerator_clang_type.IsValid() && name && name[0]) { clang::QualType enum_qual_type(GetCanonicalQualType(type)); bool is_signed = false; enumerator_clang_type.IsIntegerType(is_signed); const clang::Type *clang_type = enum_qual_type.getTypePtr(); if (clang_type) { const clang::EnumType *enutype = llvm::dyn_cast(clang_type); if (enutype) { llvm::APSInt enum_llvm_apsint(enum_value_bit_size, is_signed); enum_llvm_apsint = enum_value; clang::EnumConstantDecl *enumerator_decl = clang::EnumConstantDecl::Create( *getASTContext(), enutype->getDecl(), clang::SourceLocation(), name ? &getASTContext()->Idents.get(name) : nullptr, // Identifier ClangUtil::GetQualType(enumerator_clang_type), nullptr, enum_llvm_apsint); if (enumerator_decl) { enutype->getDecl()->addDecl(enumerator_decl); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(enumerator_decl); #endif return true; } } } } return false; } CompilerType ClangASTContext::GetEnumerationIntegerType(lldb::opaque_compiler_type_t type) { clang::QualType enum_qual_type(GetCanonicalQualType(type)); const clang::Type *clang_type = enum_qual_type.getTypePtr(); if (clang_type) { const clang::EnumType *enutype = llvm::dyn_cast(clang_type); if (enutype) { clang::EnumDecl *enum_decl = enutype->getDecl(); if (enum_decl) return CompilerType(getASTContext(), enum_decl->getIntegerType()); } } return CompilerType(); } CompilerType ClangASTContext::CreateMemberPointerType(const CompilerType &type, const CompilerType &pointee_type) { if (type && pointee_type.IsValid() && type.GetTypeSystem() == pointee_type.GetTypeSystem()) { ClangASTContext *ast = llvm::dyn_cast(type.GetTypeSystem()); if (!ast) return CompilerType(); return CompilerType(ast->getASTContext(), ast->getASTContext()->getMemberPointerType( ClangUtil::GetQualType(pointee_type), ClangUtil::GetQualType(type).getTypePtr())); } return CompilerType(); } size_t ClangASTContext::ConvertStringToFloatValue(lldb::opaque_compiler_type_t type, const char *s, uint8_t *dst, size_t dst_size) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); uint32_t count = 0; bool is_complex = false; if (IsFloatingPointType(type, count, is_complex)) { // TODO: handle complex and vector types if (count != 1) return false; llvm::StringRef s_sref(s); llvm::APFloat ap_float(getASTContext()->getFloatTypeSemantics(qual_type), s_sref); const uint64_t bit_size = getASTContext()->getTypeSize(qual_type); const uint64_t byte_size = bit_size / 8; if (dst_size >= byte_size) { Scalar scalar = ap_float.bitcastToAPInt().zextOrTrunc( llvm::NextPowerOf2(byte_size) * 8); lldb_private::Error get_data_error; if (scalar.GetAsMemoryData(dst, byte_size, lldb_private::endian::InlHostByteOrder(), get_data_error)) return byte_size; } } } return 0; } //---------------------------------------------------------------------- // Dumping types //---------------------------------------------------------------------- #define DEPTH_INCREMENT 2 void ClangASTContext::DumpValue( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, Stream *s, lldb::Format format, const lldb_private::DataExtractor &data, lldb::offset_t data_byte_offset, size_t data_byte_size, uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset, bool show_types, bool show_summary, bool verbose, uint32_t depth) { if (!type) return; clang::QualType qual_type(GetQualType(type)); switch (qual_type->getTypeClass()) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); uint32_t field_bit_offset = 0; uint32_t field_byte_offset = 0; const clang::ASTRecordLayout &record_layout = getASTContext()->getASTRecordLayout(record_decl); uint32_t child_idx = 0; const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) { // We might have base classes to print out first clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { const clang::CXXRecordDecl *base_class_decl = llvm::cast( base_class->getType()->getAs()->getDecl()); // Skip empty base classes if (verbose == false && ClangASTContext::RecordHasFields(base_class_decl) == false) continue; if (base_class->isVirtual()) field_bit_offset = record_layout.getVBaseClassOffset(base_class_decl) .getQuantity() * 8; else field_bit_offset = record_layout.getBaseClassOffset(base_class_decl) .getQuantity() * 8; field_byte_offset = field_bit_offset / 8; assert(field_bit_offset % 8 == 0); if (child_idx == 0) s->PutChar('{'); else s->PutChar(','); clang::QualType base_class_qual_type = base_class->getType(); std::string base_class_type_name(base_class_qual_type.getAsString()); // Indent and print the base class type name s->Format("\n{0}{1}", llvm::fmt_repeat(" ", depth + DEPTH_INCREMENT), base_class_type_name); clang::TypeInfo base_class_type_info = getASTContext()->getTypeInfo(base_class_qual_type); // Dump the value of the member CompilerType base_clang_type(getASTContext(), base_class_qual_type); base_clang_type.DumpValue( exe_ctx, s, // Stream to dump to base_clang_type .GetFormat(), // The format with which to display the member data, // Data buffer containing all bytes for this type data_byte_offset + field_byte_offset, // Offset into "data" where // to grab value from base_class_type_info.Width / 8, // Size of this type in bytes 0, // Bitfield bit size 0, // Bitfield bit offset show_types, // Boolean indicating if we should show the variable // types show_summary, // Boolean indicating if we should show a summary // for the current type verbose, // Verbose output? depth + DEPTH_INCREMENT); // Scope depth for any types that have // children ++child_idx; } } uint32_t field_idx = 0; clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++field_idx, ++child_idx) { // Print the starting squiggly bracket (if this is the // first member) or comma (for member 2 and beyond) for // the struct/union/class member. if (child_idx == 0) s->PutChar('{'); else s->PutChar(','); // Indent s->Printf("\n%*s", depth + DEPTH_INCREMENT, ""); clang::QualType field_type = field->getType(); // Print the member type if requested // Figure out the type byte size (field_type_info.first) and // alignment (field_type_info.second) from the AST context. clang::TypeInfo field_type_info = getASTContext()->getTypeInfo(field_type); assert(field_idx < record_layout.getFieldCount()); // Figure out the field offset within the current struct/union/class // type field_bit_offset = record_layout.getFieldOffset(field_idx); field_byte_offset = field_bit_offset / 8; uint32_t field_bitfield_bit_size = 0; uint32_t field_bitfield_bit_offset = 0; if (ClangASTContext::FieldIsBitfield(getASTContext(), *field, field_bitfield_bit_size)) field_bitfield_bit_offset = field_bit_offset % 8; if (show_types) { std::string field_type_name(field_type.getAsString()); if (field_bitfield_bit_size > 0) s->Printf("(%s:%u) ", field_type_name.c_str(), field_bitfield_bit_size); else s->Printf("(%s) ", field_type_name.c_str()); } // Print the member name and equal sign s->Printf("%s = ", field->getNameAsString().c_str()); // Dump the value of the member CompilerType field_clang_type(getASTContext(), field_type); field_clang_type.DumpValue( exe_ctx, s, // Stream to dump to field_clang_type .GetFormat(), // The format with which to display the member data, // Data buffer containing all bytes for this type data_byte_offset + field_byte_offset, // Offset into "data" where to // grab value from field_type_info.Width / 8, // Size of this type in bytes field_bitfield_bit_size, // Bitfield bit size field_bitfield_bit_offset, // Bitfield bit offset show_types, // Boolean indicating if we should show the variable // types show_summary, // Boolean indicating if we should show a summary for // the current type verbose, // Verbose output? depth + DEPTH_INCREMENT); // Scope depth for any types that have // children } // Indent the trailing squiggly bracket if (child_idx > 0) s->Printf("\n%*s}", depth, ""); } return; case clang::Type::Enum: if (GetCompleteType(type)) { const clang::EnumType *enutype = llvm::cast(qual_type.getTypePtr()); const clang::EnumDecl *enum_decl = enutype->getDecl(); assert(enum_decl); clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos; lldb::offset_t offset = data_byte_offset; const int64_t enum_value = data.GetMaxU64Bitfield( &offset, data_byte_size, bitfield_bit_size, bitfield_bit_offset); for (enum_pos = enum_decl->enumerator_begin(), enum_end_pos = enum_decl->enumerator_end(); enum_pos != enum_end_pos; ++enum_pos) { if (enum_pos->getInitVal() == enum_value) { s->Printf("%s", enum_pos->getNameAsString().c_str()); return; } } // If we have gotten here we didn't get find the enumerator in the // enum decl, so just print the integer. s->Printf("%" PRIi64, enum_value); } return; case clang::Type::ConstantArray: { const clang::ConstantArrayType *array = llvm::cast(qual_type.getTypePtr()); bool is_array_of_characters = false; clang::QualType element_qual_type = array->getElementType(); const clang::Type *canonical_type = element_qual_type->getCanonicalTypeInternal().getTypePtr(); if (canonical_type) is_array_of_characters = canonical_type->isCharType(); const uint64_t element_count = array->getSize().getLimitedValue(); clang::TypeInfo field_type_info = getASTContext()->getTypeInfo(element_qual_type); uint32_t element_idx = 0; uint32_t element_offset = 0; uint64_t element_byte_size = field_type_info.Width / 8; uint32_t element_stride = element_byte_size; if (is_array_of_characters) { s->PutChar('"'); data.Dump(s, data_byte_offset, lldb::eFormatChar, element_byte_size, element_count, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); s->PutChar('"'); return; } else { CompilerType element_clang_type(getASTContext(), element_qual_type); lldb::Format element_format = element_clang_type.GetFormat(); for (element_idx = 0; element_idx < element_count; ++element_idx) { // Print the starting squiggly bracket (if this is the // first member) or comman (for member 2 and beyong) for // the struct/union/class member. if (element_idx == 0) s->PutChar('{'); else s->PutChar(','); // Indent and print the index s->Printf("\n%*s[%u] ", depth + DEPTH_INCREMENT, "", element_idx); // Figure out the field offset within the current struct/union/class // type element_offset = element_idx * element_stride; // Dump the value of the member element_clang_type.DumpValue( exe_ctx, s, // Stream to dump to element_format, // The format with which to display the element data, // Data buffer containing all bytes for this type data_byte_offset + element_offset, // Offset into "data" where to grab value from element_byte_size, // Size of this type in bytes 0, // Bitfield bit size 0, // Bitfield bit offset show_types, // Boolean indicating if we should show the variable // types show_summary, // Boolean indicating if we should show a summary for // the current type verbose, // Verbose output? depth + DEPTH_INCREMENT); // Scope depth for any types that have // children } // Indent the trailing squiggly bracket if (element_idx > 0) s->Printf("\n%*s}", depth, ""); } } return; case clang::Type::Typedef: { clang::QualType typedef_qual_type = llvm::cast(qual_type) ->getDecl() ->getUnderlyingType(); CompilerType typedef_clang_type(getASTContext(), typedef_qual_type); lldb::Format typedef_format = typedef_clang_type.GetFormat(); clang::TypeInfo typedef_type_info = getASTContext()->getTypeInfo(typedef_qual_type); uint64_t typedef_byte_size = typedef_type_info.Width / 8; return typedef_clang_type.DumpValue( exe_ctx, s, // Stream to dump to typedef_format, // The format with which to display the element data, // Data buffer containing all bytes for this type data_byte_offset, // Offset into "data" where to grab value from typedef_byte_size, // Size of this type in bytes bitfield_bit_size, // Bitfield bit size bitfield_bit_offset, // Bitfield bit offset show_types, // Boolean indicating if we should show the variable types show_summary, // Boolean indicating if we should show a summary for the // current type verbose, // Verbose output? depth); // Scope depth for any types that have children } break; case clang::Type::Auto: { clang::QualType elaborated_qual_type = llvm::cast(qual_type)->getDeducedType(); CompilerType elaborated_clang_type(getASTContext(), elaborated_qual_type); lldb::Format elaborated_format = elaborated_clang_type.GetFormat(); clang::TypeInfo elaborated_type_info = getASTContext()->getTypeInfo(elaborated_qual_type); uint64_t elaborated_byte_size = elaborated_type_info.Width / 8; return elaborated_clang_type.DumpValue( exe_ctx, s, // Stream to dump to elaborated_format, // The format with which to display the element data, // Data buffer containing all bytes for this type data_byte_offset, // Offset into "data" where to grab value from elaborated_byte_size, // Size of this type in bytes bitfield_bit_size, // Bitfield bit size bitfield_bit_offset, // Bitfield bit offset show_types, // Boolean indicating if we should show the variable types show_summary, // Boolean indicating if we should show a summary for the // current type verbose, // Verbose output? depth); // Scope depth for any types that have children } break; case clang::Type::Elaborated: { clang::QualType elaborated_qual_type = llvm::cast(qual_type)->getNamedType(); CompilerType elaborated_clang_type(getASTContext(), elaborated_qual_type); lldb::Format elaborated_format = elaborated_clang_type.GetFormat(); clang::TypeInfo elaborated_type_info = getASTContext()->getTypeInfo(elaborated_qual_type); uint64_t elaborated_byte_size = elaborated_type_info.Width / 8; return elaborated_clang_type.DumpValue( exe_ctx, s, // Stream to dump to elaborated_format, // The format with which to display the element data, // Data buffer containing all bytes for this type data_byte_offset, // Offset into "data" where to grab value from elaborated_byte_size, // Size of this type in bytes bitfield_bit_size, // Bitfield bit size bitfield_bit_offset, // Bitfield bit offset show_types, // Boolean indicating if we should show the variable types show_summary, // Boolean indicating if we should show a summary for the // current type verbose, // Verbose output? depth); // Scope depth for any types that have children } break; case clang::Type::Paren: { clang::QualType desugar_qual_type = llvm::cast(qual_type)->desugar(); CompilerType desugar_clang_type(getASTContext(), desugar_qual_type); lldb::Format desugar_format = desugar_clang_type.GetFormat(); clang::TypeInfo desugar_type_info = getASTContext()->getTypeInfo(desugar_qual_type); uint64_t desugar_byte_size = desugar_type_info.Width / 8; return desugar_clang_type.DumpValue( exe_ctx, s, // Stream to dump to desugar_format, // The format with which to display the element data, // Data buffer containing all bytes for this type data_byte_offset, // Offset into "data" where to grab value from desugar_byte_size, // Size of this type in bytes bitfield_bit_size, // Bitfield bit size bitfield_bit_offset, // Bitfield bit offset show_types, // Boolean indicating if we should show the variable types show_summary, // Boolean indicating if we should show a summary for the // current type verbose, // Verbose output? depth); // Scope depth for any types that have children } break; default: // We are down to a scalar type that we just need to display. data.Dump(s, data_byte_offset, format, data_byte_size, 1, UINT32_MAX, LLDB_INVALID_ADDRESS, bitfield_bit_size, bitfield_bit_offset); if (show_summary) DumpSummary(type, exe_ctx, s, data, data_byte_offset, data_byte_size); break; } } bool ClangASTContext::DumpTypeValue( lldb::opaque_compiler_type_t type, Stream *s, lldb::Format format, const lldb_private::DataExtractor &data, lldb::offset_t byte_offset, size_t byte_size, uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset, ExecutionContextScope *exe_scope) { if (!type) return false; if (IsAggregateType(type)) { return false; } else { clang::QualType qual_type(GetQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Typedef: { clang::QualType typedef_qual_type = llvm::cast(qual_type) ->getDecl() ->getUnderlyingType(); CompilerType typedef_clang_type(getASTContext(), typedef_qual_type); if (format == eFormatDefault) format = typedef_clang_type.GetFormat(); clang::TypeInfo typedef_type_info = getASTContext()->getTypeInfo(typedef_qual_type); uint64_t typedef_byte_size = typedef_type_info.Width / 8; return typedef_clang_type.DumpTypeValue( s, format, // The format with which to display the element data, // Data buffer containing all bytes for this type byte_offset, // Offset into "data" where to grab value from typedef_byte_size, // Size of this type in bytes bitfield_bit_size, // Size in bits of a bitfield value, if zero don't // treat as a bitfield bitfield_bit_offset, // Offset in bits of a bitfield value if // bitfield_bit_size != 0 exe_scope); } break; case clang::Type::Enum: // If our format is enum or default, show the enumeration value as // its enumeration string value, else just display it as requested. if ((format == eFormatEnum || format == eFormatDefault) && GetCompleteType(type)) { const clang::EnumType *enutype = llvm::cast(qual_type.getTypePtr()); const clang::EnumDecl *enum_decl = enutype->getDecl(); assert(enum_decl); clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos; const bool is_signed = qual_type->isSignedIntegerOrEnumerationType(); lldb::offset_t offset = byte_offset; if (is_signed) { const int64_t enum_svalue = data.GetMaxS64Bitfield( &offset, byte_size, bitfield_bit_size, bitfield_bit_offset); for (enum_pos = enum_decl->enumerator_begin(), enum_end_pos = enum_decl->enumerator_end(); enum_pos != enum_end_pos; ++enum_pos) { if (enum_pos->getInitVal().getSExtValue() == enum_svalue) { s->PutCString(enum_pos->getNameAsString()); return true; } } // If we have gotten here we didn't get find the enumerator in the // enum decl, so just print the integer. s->Printf("%" PRIi64, enum_svalue); } else { const uint64_t enum_uvalue = data.GetMaxU64Bitfield( &offset, byte_size, bitfield_bit_size, bitfield_bit_offset); for (enum_pos = enum_decl->enumerator_begin(), enum_end_pos = enum_decl->enumerator_end(); enum_pos != enum_end_pos; ++enum_pos) { if (enum_pos->getInitVal().getZExtValue() == enum_uvalue) { s->PutCString(enum_pos->getNameAsString()); return true; } } // If we have gotten here we didn't get find the enumerator in the // enum decl, so just print the integer. s->Printf("%" PRIu64, enum_uvalue); } return true; } // format was not enum, just fall through and dump the value as // requested.... LLVM_FALLTHROUGH; default: // We are down to a scalar type that we just need to display. { uint32_t item_count = 1; // A few formats, we might need to modify our size and count for // depending // on how we are trying to display the value... switch (format) { default: case eFormatBoolean: case eFormatBinary: case eFormatComplex: case eFormatCString: // NULL terminated C strings case eFormatDecimal: case eFormatEnum: case eFormatHex: case eFormatHexUppercase: case eFormatFloat: case eFormatOctal: case eFormatOSType: case eFormatUnsigned: case eFormatPointer: case eFormatVectorOfChar: case eFormatVectorOfSInt8: case eFormatVectorOfUInt8: case eFormatVectorOfSInt16: case eFormatVectorOfUInt16: case eFormatVectorOfSInt32: case eFormatVectorOfUInt32: case eFormatVectorOfSInt64: case eFormatVectorOfUInt64: case eFormatVectorOfFloat32: case eFormatVectorOfFloat64: case eFormatVectorOfUInt128: break; case eFormatChar: case eFormatCharPrintable: case eFormatCharArray: case eFormatBytes: case eFormatBytesWithASCII: item_count = byte_size; byte_size = 1; break; case eFormatUnicode16: item_count = byte_size / 2; byte_size = 2; break; case eFormatUnicode32: item_count = byte_size / 4; byte_size = 4; break; } return data.Dump(s, byte_offset, format, byte_size, item_count, UINT32_MAX, LLDB_INVALID_ADDRESS, bitfield_bit_size, bitfield_bit_offset, exe_scope); } break; } } return 0; } void ClangASTContext::DumpSummary(lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, Stream *s, const lldb_private::DataExtractor &data, lldb::offset_t data_byte_offset, size_t data_byte_size) { uint32_t length = 0; if (IsCStringType(type, length)) { if (exe_ctx) { Process *process = exe_ctx->GetProcessPtr(); if (process) { lldb::offset_t offset = data_byte_offset; lldb::addr_t pointer_address = data.GetMaxU64(&offset, data_byte_size); std::vector buf; if (length > 0) buf.resize(length); else buf.resize(256); lldb_private::DataExtractor cstr_data(&buf.front(), buf.size(), process->GetByteOrder(), 4); buf.back() = '\0'; size_t bytes_read; size_t total_cstr_len = 0; Error error; while ((bytes_read = process->ReadMemory(pointer_address, &buf.front(), buf.size(), error)) > 0) { const size_t len = strlen((const char *)&buf.front()); if (len == 0) break; if (total_cstr_len == 0) s->PutCString(" \""); cstr_data.Dump(s, 0, lldb::eFormatChar, 1, len, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); total_cstr_len += len; if (len < buf.size()) break; pointer_address += total_cstr_len; } if (total_cstr_len > 0) s->PutChar('"'); } } } } void ClangASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type) { StreamFile s(stdout, false); DumpTypeDescription(type, &s); ClangASTMetadata *metadata = ClangASTContext::GetMetadata(getASTContext(), type); if (metadata) { metadata->Dump(&s); } } void ClangASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type, Stream *s) { if (type) { clang::QualType qual_type(GetQualType(type)); llvm::SmallVector buf; llvm::raw_svector_ostream llvm_ostrm(buf); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { GetCompleteType(type); const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { clang::PrintingPolicy policy = getASTContext()->getPrintingPolicy(); class_interface_decl->print(llvm_ostrm, policy, s->GetIndentLevel()); } } } break; case clang::Type::Typedef: { const clang::TypedefType *typedef_type = qual_type->getAs(); if (typedef_type) { const clang::TypedefNameDecl *typedef_decl = typedef_type->getDecl(); std::string clang_typedef_name( typedef_decl->getQualifiedNameAsString()); if (!clang_typedef_name.empty()) { s->PutCString("typedef "); s->PutCString(clang_typedef_name); } } } break; case clang::Type::Auto: CompilerType(getASTContext(), llvm::cast(qual_type)->getDeducedType()) .DumpTypeDescription(s); return; case clang::Type::Elaborated: CompilerType(getASTContext(), llvm::cast(qual_type)->getNamedType()) .DumpTypeDescription(s); return; case clang::Type::Paren: CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .DumpTypeDescription(s); return; case clang::Type::Record: { GetCompleteType(type); const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) cxx_record_decl->print(llvm_ostrm, getASTContext()->getPrintingPolicy(), s->GetIndentLevel()); else record_decl->print(llvm_ostrm, getASTContext()->getPrintingPolicy(), s->GetIndentLevel()); } break; default: { const clang::TagType *tag_type = llvm::dyn_cast(qual_type.getTypePtr()); if (tag_type) { clang::TagDecl *tag_decl = tag_type->getDecl(); if (tag_decl) tag_decl->print(llvm_ostrm, 0); } else { std::string clang_type_name(qual_type.getAsString()); if (!clang_type_name.empty()) s->PutCString(clang_type_name); } } } if (buf.size() > 0) { s->Write(buf.data(), buf.size()); } } } void ClangASTContext::DumpTypeName(const CompilerType &type) { if (ClangUtil::IsClangType(type)) { clang::QualType qual_type( ClangUtil::GetCanonicalQualType(ClangUtil::RemoveFastQualifiers(type))); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) printf("class %s", cxx_record_decl->getName().str().c_str()); } break; case clang::Type::Enum: { clang::EnumDecl *enum_decl = llvm::cast(qual_type)->getDecl(); if (enum_decl) { printf("enum %s", enum_decl->getName().str().c_str()); } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); // We currently can't complete objective C types through the newly added // ASTContext // because it only supports TagDecl objects right now... if (class_interface_decl) printf("@class %s", class_interface_decl->getName().str().c_str()); } } break; case clang::Type::Typedef: printf("typedef %s", llvm::cast(qual_type) ->getDecl() ->getName() .str() .c_str()); break; case clang::Type::Auto: printf("auto "); return DumpTypeName(CompilerType(type.GetTypeSystem(), llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr())); case clang::Type::Elaborated: printf("elaborated "); return DumpTypeName(CompilerType( type.GetTypeSystem(), llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr())); case clang::Type::Paren: printf("paren "); return DumpTypeName(CompilerType( type.GetTypeSystem(), llvm::cast(qual_type)->desugar().getAsOpaquePtr())); default: printf("ClangASTContext::DumpTypeName() type_class = %u", type_class); break; } } } clang::ClassTemplateDecl *ClangASTContext::ParseClassTemplateDecl( clang::DeclContext *decl_ctx, lldb::AccessType access_type, const char *parent_name, int tag_decl_kind, const ClangASTContext::TemplateParameterInfos &template_param_infos) { if (template_param_infos.IsValid()) { std::string template_basename(parent_name); template_basename.erase(template_basename.find('<')); return CreateClassTemplateDecl(decl_ctx, access_type, template_basename.c_str(), tag_decl_kind, template_param_infos); } return NULL; } void ClangASTContext::CompleteTagDecl(void *baton, clang::TagDecl *decl) { ClangASTContext *ast = (ClangASTContext *)baton; SymbolFile *sym_file = ast->GetSymbolFile(); if (sym_file) { CompilerType clang_type = GetTypeForDecl(decl); if (clang_type) sym_file->CompleteType(clang_type); } } void ClangASTContext::CompleteObjCInterfaceDecl( void *baton, clang::ObjCInterfaceDecl *decl) { ClangASTContext *ast = (ClangASTContext *)baton; SymbolFile *sym_file = ast->GetSymbolFile(); if (sym_file) { CompilerType clang_type = GetTypeForDecl(decl); if (clang_type) sym_file->CompleteType(clang_type); } } DWARFASTParser *ClangASTContext::GetDWARFParser() { if (!m_dwarf_ast_parser_ap) m_dwarf_ast_parser_ap.reset(new DWARFASTParserClang(*this)); return m_dwarf_ast_parser_ap.get(); } PDBASTParser *ClangASTContext::GetPDBParser() { if (!m_pdb_ast_parser_ap) m_pdb_ast_parser_ap.reset(new PDBASTParser(*this)); return m_pdb_ast_parser_ap.get(); } bool ClangASTContext::LayoutRecordType( void *baton, const clang::RecordDecl *record_decl, uint64_t &bit_size, uint64_t &alignment, llvm::DenseMap &field_offsets, llvm::DenseMap &base_offsets, llvm::DenseMap &vbase_offsets) { ClangASTContext *ast = (ClangASTContext *)baton; DWARFASTParserClang *dwarf_ast_parser = (DWARFASTParserClang *)ast->GetDWARFParser(); return dwarf_ast_parser->GetClangASTImporter().LayoutRecordType( record_decl, bit_size, alignment, field_offsets, base_offsets, vbase_offsets); } //---------------------------------------------------------------------- // CompilerDecl override functions //---------------------------------------------------------------------- ConstString ClangASTContext::DeclGetName(void *opaque_decl) { if (opaque_decl) { clang::NamedDecl *nd = llvm::dyn_cast((clang::Decl *)opaque_decl); if (nd != nullptr) return ConstString(nd->getDeclName().getAsString()); } return ConstString(); } ConstString ClangASTContext::DeclGetMangledName(void *opaque_decl) { if (opaque_decl) { clang::NamedDecl *nd = llvm::dyn_cast((clang::Decl *)opaque_decl); if (nd != nullptr && !llvm::isa(nd)) { clang::MangleContext *mc = getMangleContext(); if (mc && mc->shouldMangleCXXName(nd)) { llvm::SmallVector buf; llvm::raw_svector_ostream llvm_ostrm(buf); if (llvm::isa(nd)) { mc->mangleCXXCtor(llvm::dyn_cast(nd), Ctor_Complete, llvm_ostrm); } else if (llvm::isa(nd)) { mc->mangleCXXDtor(llvm::dyn_cast(nd), Dtor_Complete, llvm_ostrm); } else { mc->mangleName(nd, llvm_ostrm); } if (buf.size() > 0) return ConstString(buf.data(), buf.size()); } } } return ConstString(); } CompilerDeclContext ClangASTContext::DeclGetDeclContext(void *opaque_decl) { if (opaque_decl) return CompilerDeclContext(this, ((clang::Decl *)opaque_decl)->getDeclContext()); else return CompilerDeclContext(); } CompilerType ClangASTContext::DeclGetFunctionReturnType(void *opaque_decl) { if (clang::FunctionDecl *func_decl = llvm::dyn_cast((clang::Decl *)opaque_decl)) return CompilerType(this, func_decl->getReturnType().getAsOpaquePtr()); if (clang::ObjCMethodDecl *objc_method = llvm::dyn_cast((clang::Decl *)opaque_decl)) return CompilerType(this, objc_method->getReturnType().getAsOpaquePtr()); else return CompilerType(); } size_t ClangASTContext::DeclGetFunctionNumArguments(void *opaque_decl) { if (clang::FunctionDecl *func_decl = llvm::dyn_cast((clang::Decl *)opaque_decl)) return func_decl->param_size(); if (clang::ObjCMethodDecl *objc_method = llvm::dyn_cast((clang::Decl *)opaque_decl)) return objc_method->param_size(); else return 0; } CompilerType ClangASTContext::DeclGetFunctionArgumentType(void *opaque_decl, size_t idx) { if (clang::FunctionDecl *func_decl = llvm::dyn_cast((clang::Decl *)opaque_decl)) { if (idx < func_decl->param_size()) { ParmVarDecl *var_decl = func_decl->getParamDecl(idx); if (var_decl) return CompilerType(this, var_decl->getOriginalType().getAsOpaquePtr()); } } else if (clang::ObjCMethodDecl *objc_method = llvm::dyn_cast( (clang::Decl *)opaque_decl)) { if (idx < objc_method->param_size()) return CompilerType( this, objc_method->parameters()[idx]->getOriginalType().getAsOpaquePtr()); } return CompilerType(); } //---------------------------------------------------------------------- // CompilerDeclContext functions //---------------------------------------------------------------------- std::vector ClangASTContext::DeclContextFindDeclByName( void *opaque_decl_ctx, ConstString name, const bool ignore_using_decls) { std::vector found_decls; if (opaque_decl_ctx) { DeclContext *root_decl_ctx = (DeclContext *)opaque_decl_ctx; std::set searched; std::multimap search_queue; SymbolFile *symbol_file = GetSymbolFile(); for (clang::DeclContext *decl_context = root_decl_ctx; decl_context != nullptr && found_decls.empty(); decl_context = decl_context->getParent()) { search_queue.insert(std::make_pair(decl_context, decl_context)); for (auto it = search_queue.find(decl_context); it != search_queue.end(); it++) { if (!searched.insert(it->second).second) continue; symbol_file->ParseDeclsForContext( CompilerDeclContext(this, it->second)); for (clang::Decl *child : it->second->decls()) { if (clang::UsingDirectiveDecl *ud = llvm::dyn_cast(child)) { if (ignore_using_decls) continue; clang::DeclContext *from = ud->getCommonAncestor(); if (searched.find(ud->getNominatedNamespace()) == searched.end()) search_queue.insert( std::make_pair(from, ud->getNominatedNamespace())); } else if (clang::UsingDecl *ud = llvm::dyn_cast(child)) { if (ignore_using_decls) continue; for (clang::UsingShadowDecl *usd : ud->shadows()) { clang::Decl *target = usd->getTargetDecl(); if (clang::NamedDecl *nd = llvm::dyn_cast(target)) { IdentifierInfo *ii = nd->getIdentifier(); if (ii != nullptr && ii->getName().equals(name.AsCString(nullptr))) found_decls.push_back(CompilerDecl(this, nd)); } } } else if (clang::NamedDecl *nd = llvm::dyn_cast(child)) { IdentifierInfo *ii = nd->getIdentifier(); if (ii != nullptr && ii->getName().equals(name.AsCString(nullptr))) found_decls.push_back(CompilerDecl(this, nd)); } } } } } return found_decls; } // Look for child_decl_ctx's lookup scope in frame_decl_ctx and its parents, // and return the number of levels it took to find it, or // LLDB_INVALID_DECL_LEVEL // if not found. If the decl was imported via a using declaration, its name // and/or // type, if set, will be used to check that the decl found in the scope is a // match. // // The optional name is required by languages (like C++) to handle using // declarations // like: // // void poo(); // namespace ns { // void foo(); // void goo(); // } // void bar() { // using ns::foo; // // CountDeclLevels returns 0 for 'foo', 1 for 'poo', and // // LLDB_INVALID_DECL_LEVEL for 'goo'. // } // // The optional type is useful in the case that there's a specific overload // that we're looking for that might otherwise be shadowed, like: // // void foo(int); // namespace ns { // void foo(); // } // void bar() { // using ns::foo; // // CountDeclLevels returns 0 for { 'foo', void() }, // // 1 for { 'foo', void(int) }, and // // LLDB_INVALID_DECL_LEVEL for { 'foo', void(int, int) }. // } // // NOTE: Because file statics are at the TranslationUnit along with globals, a // function at file scope will return the same level as a function at global // scope. // Ideally we'd like to treat the file scope as an additional scope just below // the // global scope. More work needs to be done to recognise that, if the decl // we're // trying to look up is static, we should compare its source file with that of // the // current scope and return a lower number for it. uint32_t ClangASTContext::CountDeclLevels(clang::DeclContext *frame_decl_ctx, clang::DeclContext *child_decl_ctx, ConstString *child_name, CompilerType *child_type) { if (frame_decl_ctx) { std::set searched; std::multimap search_queue; SymbolFile *symbol_file = GetSymbolFile(); // Get the lookup scope for the decl we're trying to find. clang::DeclContext *parent_decl_ctx = child_decl_ctx->getParent(); // Look for it in our scope's decl context and its parents. uint32_t level = 0; for (clang::DeclContext *decl_ctx = frame_decl_ctx; decl_ctx != nullptr; decl_ctx = decl_ctx->getParent()) { if (!decl_ctx->isLookupContext()) continue; if (decl_ctx == parent_decl_ctx) // Found it! return level; search_queue.insert(std::make_pair(decl_ctx, decl_ctx)); for (auto it = search_queue.find(decl_ctx); it != search_queue.end(); it++) { if (searched.find(it->second) != searched.end()) continue; // Currently DWARF has one shared translation unit for all Decls at top // level, so this // would erroneously find using statements anywhere. So don't look at // the top-level // translation unit. // TODO fix this and add a testcase that depends on it. if (llvm::isa(it->second)) continue; searched.insert(it->second); symbol_file->ParseDeclsForContext( CompilerDeclContext(this, it->second)); for (clang::Decl *child : it->second->decls()) { if (clang::UsingDirectiveDecl *ud = llvm::dyn_cast(child)) { clang::DeclContext *ns = ud->getNominatedNamespace(); if (ns == parent_decl_ctx) // Found it! return level; clang::DeclContext *from = ud->getCommonAncestor(); if (searched.find(ns) == searched.end()) search_queue.insert(std::make_pair(from, ns)); } else if (child_name) { if (clang::UsingDecl *ud = llvm::dyn_cast(child)) { for (clang::UsingShadowDecl *usd : ud->shadows()) { clang::Decl *target = usd->getTargetDecl(); clang::NamedDecl *nd = llvm::dyn_cast(target); if (!nd) continue; // Check names. IdentifierInfo *ii = nd->getIdentifier(); if (ii == nullptr || !ii->getName().equals(child_name->AsCString(nullptr))) continue; // Check types, if one was provided. if (child_type) { CompilerType clang_type = ClangASTContext::GetTypeForDecl(nd); if (!AreTypesSame(clang_type, *child_type, /*ignore_qualifiers=*/true)) continue; } // Found it! return level; } } } } } ++level; } } return LLDB_INVALID_DECL_LEVEL; } bool ClangASTContext::DeclContextIsStructUnionOrClass(void *opaque_decl_ctx) { if (opaque_decl_ctx) return ((clang::DeclContext *)opaque_decl_ctx)->isRecord(); else return false; } ConstString ClangASTContext::DeclContextGetName(void *opaque_decl_ctx) { if (opaque_decl_ctx) { clang::NamedDecl *named_decl = llvm::dyn_cast((clang::DeclContext *)opaque_decl_ctx); if (named_decl) return ConstString(named_decl->getName()); } return ConstString(); } ConstString ClangASTContext::DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) { if (opaque_decl_ctx) { clang::NamedDecl *named_decl = llvm::dyn_cast((clang::DeclContext *)opaque_decl_ctx); if (named_decl) return ConstString( llvm::StringRef(named_decl->getQualifiedNameAsString())); } return ConstString(); } bool ClangASTContext::DeclContextIsClassMethod( void *opaque_decl_ctx, lldb::LanguageType *language_ptr, bool *is_instance_method_ptr, ConstString *language_object_name_ptr) { if (opaque_decl_ctx) { clang::DeclContext *decl_ctx = (clang::DeclContext *)opaque_decl_ctx; if (ObjCMethodDecl *objc_method = llvm::dyn_cast(decl_ctx)) { if (is_instance_method_ptr) *is_instance_method_ptr = objc_method->isInstanceMethod(); if (language_ptr) *language_ptr = eLanguageTypeObjC; if (language_object_name_ptr) language_object_name_ptr->SetCString("self"); return true; } else if (CXXMethodDecl *cxx_method = llvm::dyn_cast(decl_ctx)) { if (is_instance_method_ptr) *is_instance_method_ptr = cxx_method->isInstance(); if (language_ptr) *language_ptr = eLanguageTypeC_plus_plus; if (language_object_name_ptr) language_object_name_ptr->SetCString("this"); return true; } else if (clang::FunctionDecl *function_decl = llvm::dyn_cast(decl_ctx)) { ClangASTMetadata *metadata = GetMetadata(&decl_ctx->getParentASTContext(), function_decl); if (metadata && metadata->HasObjectPtr()) { if (is_instance_method_ptr) *is_instance_method_ptr = true; if (language_ptr) *language_ptr = eLanguageTypeObjC; if (language_object_name_ptr) language_object_name_ptr->SetCString(metadata->GetObjectPtrName()); return true; } } } return false; } clang::DeclContext * ClangASTContext::DeclContextGetAsDeclContext(const CompilerDeclContext &dc) { if (dc.IsClang()) return (clang::DeclContext *)dc.GetOpaqueDeclContext(); return nullptr; } ObjCMethodDecl * ClangASTContext::DeclContextGetAsObjCMethodDecl(const CompilerDeclContext &dc) { if (dc.IsClang()) return llvm::dyn_cast( (clang::DeclContext *)dc.GetOpaqueDeclContext()); return nullptr; } CXXMethodDecl * ClangASTContext::DeclContextGetAsCXXMethodDecl(const CompilerDeclContext &dc) { if (dc.IsClang()) return llvm::dyn_cast( (clang::DeclContext *)dc.GetOpaqueDeclContext()); return nullptr; } clang::FunctionDecl * ClangASTContext::DeclContextGetAsFunctionDecl(const CompilerDeclContext &dc) { if (dc.IsClang()) return llvm::dyn_cast( (clang::DeclContext *)dc.GetOpaqueDeclContext()); return nullptr; } clang::NamespaceDecl * ClangASTContext::DeclContextGetAsNamespaceDecl(const CompilerDeclContext &dc) { if (dc.IsClang()) return llvm::dyn_cast( (clang::DeclContext *)dc.GetOpaqueDeclContext()); return nullptr; } ClangASTMetadata * ClangASTContext::DeclContextGetMetaData(const CompilerDeclContext &dc, const void *object) { clang::ASTContext *ast = DeclContextGetClangASTContext(dc); if (ast) return ClangASTContext::GetMetadata(ast, object); return nullptr; } clang::ASTContext * ClangASTContext::DeclContextGetClangASTContext(const CompilerDeclContext &dc) { ClangASTContext *ast = llvm::dyn_cast_or_null(dc.GetTypeSystem()); if (ast) return ast->getASTContext(); return nullptr; } ClangASTContextForExpressions::ClangASTContextForExpressions(Target &target) : ClangASTContext(target.GetArchitecture().GetTriple().getTriple().c_str()), m_target_wp(target.shared_from_this()), m_persistent_variables(new ClangPersistentVariables) {} UserExpression *ClangASTContextForExpressions::GetUserExpression( llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, Expression::ResultType desired_type, const EvaluateExpressionOptions &options) { TargetSP target_sp = m_target_wp.lock(); if (!target_sp) return nullptr; return new ClangUserExpression(*target_sp.get(), expr, prefix, language, desired_type, options); } FunctionCaller *ClangASTContextForExpressions::GetFunctionCaller( const CompilerType &return_type, const Address &function_address, const ValueList &arg_value_list, const char *name) { TargetSP target_sp = m_target_wp.lock(); if (!target_sp) return nullptr; Process *process = target_sp->GetProcessSP().get(); if (!process) return nullptr; return new ClangFunctionCaller(*process, return_type, function_address, arg_value_list, name); } UtilityFunction * ClangASTContextForExpressions::GetUtilityFunction(const char *text, const char *name) { TargetSP target_sp = m_target_wp.lock(); if (!target_sp) return nullptr; return new ClangUtilityFunction(*target_sp.get(), text, name); } PersistentExpressionState * ClangASTContextForExpressions::GetPersistentExpressionState() { return m_persistent_variables.get(); } Index: vendor/lldb/dist/source/Symbol/Type.cpp =================================================================== --- vendor/lldb/dist/source/Symbol/Type.cpp (revision 311541) +++ vendor/lldb/dist/source/Symbol/Type.cpp (revision 311542) @@ -1,1115 +1,1113 @@ //===-- Type.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 // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Module.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/StreamString.h" #include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolContextScope.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "llvm/ADT/StringRef.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" using namespace lldb; using namespace lldb_private; void CompilerContext::Dump() const { switch (type) { case CompilerContextKind::Invalid: printf("Invalid"); break; case CompilerContextKind::TranslationUnit: printf("TranslationUnit"); break; case CompilerContextKind::Module: printf("Module"); break; case CompilerContextKind::Namespace: printf("Namespace"); break; case CompilerContextKind::Class: printf("Class"); break; case CompilerContextKind::Structure: printf("Structure"); break; case CompilerContextKind::Union: printf("Union"); break; case CompilerContextKind::Function: printf("Function"); break; case CompilerContextKind::Variable: printf("Variable"); break; case CompilerContextKind::Enumeration: printf("Enumeration"); break; case CompilerContextKind::Typedef: printf("Typedef"); break; } printf("(\"%s\")\n", name.GetCString()); } class TypeAppendVisitor { public: TypeAppendVisitor(TypeListImpl &type_list) : m_type_list(type_list) {} bool operator()(const lldb::TypeSP &type) { m_type_list.Append(TypeImplSP(new TypeImpl(type))); return true; } private: TypeListImpl &m_type_list; }; void TypeListImpl::Append(const lldb_private::TypeList &type_list) { TypeAppendVisitor cb(*this); type_list.ForEach(cb); } SymbolFileType::SymbolFileType(SymbolFile &symbol_file, const lldb::TypeSP &type_sp) : UserID(type_sp ? type_sp->GetID() : LLDB_INVALID_UID), m_symbol_file(symbol_file), m_type_sp(type_sp) {} Type *SymbolFileType::GetType() { if (!m_type_sp) { Type *resolved_type = m_symbol_file.ResolveTypeUID(GetID()); if (resolved_type) m_type_sp = resolved_type->shared_from_this(); } return m_type_sp.get(); } Type::Type(lldb::user_id_t uid, SymbolFile *symbol_file, const ConstString &name, uint64_t byte_size, SymbolContextScope *context, user_id_t encoding_uid, EncodingDataType encoding_uid_type, const Declaration &decl, const CompilerType &compiler_type, ResolveState compiler_type_resolve_state) : std::enable_shared_from_this(), UserID(uid), m_name(name), m_symbol_file(symbol_file), m_context(context), m_encoding_type(nullptr), m_encoding_uid(encoding_uid), m_encoding_uid_type(encoding_uid_type), m_byte_size(byte_size), m_decl(decl), m_compiler_type(compiler_type) { m_flags.compiler_type_resolve_state = (compiler_type ? compiler_type_resolve_state : eResolveStateUnresolved); m_flags.is_complete_objc_class = false; } Type::Type() : std::enable_shared_from_this(), UserID(0), m_name(""), m_symbol_file(nullptr), m_context(nullptr), m_encoding_type(nullptr), m_encoding_uid(LLDB_INVALID_UID), m_encoding_uid_type(eEncodingInvalid), m_byte_size(0), m_decl(), m_compiler_type() { m_flags.compiler_type_resolve_state = eResolveStateUnresolved; m_flags.is_complete_objc_class = false; } Type::Type(const Type &rhs) : std::enable_shared_from_this(rhs), UserID(rhs), m_name(rhs.m_name), m_symbol_file(rhs.m_symbol_file), m_context(rhs.m_context), m_encoding_type(rhs.m_encoding_type), m_encoding_uid(rhs.m_encoding_uid), m_encoding_uid_type(rhs.m_encoding_uid_type), m_byte_size(rhs.m_byte_size), m_decl(rhs.m_decl), m_compiler_type(rhs.m_compiler_type), m_flags(rhs.m_flags) {} const Type &Type::operator=(const Type &rhs) { if (this != &rhs) { } return *this; } void Type::GetDescription(Stream *s, lldb::DescriptionLevel level, bool show_name) { *s << "id = " << (const UserID &)*this; // Call the name accessor to make sure we resolve the type name if (show_name) { const ConstString &type_name = GetName(); if (type_name) { *s << ", name = \"" << type_name << '"'; ConstString qualified_type_name(GetQualifiedName()); if (qualified_type_name != type_name) { *s << ", qualified = \"" << qualified_type_name << '"'; } } } // Call the get byte size accesor so we resolve our byte size if (GetByteSize()) s->Printf(", byte-size = %" PRIu64, m_byte_size); bool show_fullpaths = (level == lldb::eDescriptionLevelVerbose); m_decl.Dump(s, show_fullpaths); if (m_compiler_type.IsValid()) { *s << ", compiler_type = \""; GetForwardCompilerType().DumpTypeDescription(s); *s << '"'; } else if (m_encoding_uid != LLDB_INVALID_UID) { s->Printf(", type_uid = 0x%8.8" PRIx64, m_encoding_uid); switch (m_encoding_uid_type) { case eEncodingInvalid: break; case eEncodingIsUID: s->PutCString(" (unresolved type)"); break; case eEncodingIsConstUID: s->PutCString(" (unresolved const type)"); break; case eEncodingIsRestrictUID: s->PutCString(" (unresolved restrict type)"); break; case eEncodingIsVolatileUID: s->PutCString(" (unresolved volatile type)"); break; case eEncodingIsTypedefUID: s->PutCString(" (unresolved typedef)"); break; case eEncodingIsPointerUID: s->PutCString(" (unresolved pointer)"); break; case eEncodingIsLValueReferenceUID: s->PutCString(" (unresolved L value reference)"); break; case eEncodingIsRValueReferenceUID: s->PutCString(" (unresolved R value reference)"); break; case eEncodingIsSyntheticUID: s->PutCString(" (synthetic type)"); break; } } } void Type::Dump(Stream *s, bool show_context) { s->Printf("%p: ", static_cast(this)); s->Indent(); *s << "Type" << static_cast(*this) << ' '; if (m_name) *s << ", name = \"" << m_name << "\""; if (m_byte_size != 0) s->Printf(", size = %" PRIu64, m_byte_size); if (show_context && m_context != nullptr) { s->PutCString(", context = ( "); m_context->DumpSymbolContext(s); s->PutCString(" )"); } bool show_fullpaths = false; m_decl.Dump(s, show_fullpaths); if (m_compiler_type.IsValid()) { *s << ", compiler_type = " << m_compiler_type.GetOpaqueQualType() << ' '; GetForwardCompilerType().DumpTypeDescription(s); } else if (m_encoding_uid != LLDB_INVALID_UID) { *s << ", type_data = " << (uint64_t)m_encoding_uid; switch (m_encoding_uid_type) { case eEncodingInvalid: break; case eEncodingIsUID: s->PutCString(" (unresolved type)"); break; case eEncodingIsConstUID: s->PutCString(" (unresolved const type)"); break; case eEncodingIsRestrictUID: s->PutCString(" (unresolved restrict type)"); break; case eEncodingIsVolatileUID: s->PutCString(" (unresolved volatile type)"); break; case eEncodingIsTypedefUID: s->PutCString(" (unresolved typedef)"); break; case eEncodingIsPointerUID: s->PutCString(" (unresolved pointer)"); break; case eEncodingIsLValueReferenceUID: s->PutCString(" (unresolved L value reference)"); break; case eEncodingIsRValueReferenceUID: s->PutCString(" (unresolved R value reference)"); break; case eEncodingIsSyntheticUID: s->PutCString(" (synthetic type)"); break; } } // // if (m_access) // s->Printf(", access = %u", m_access); s->EOL(); } const ConstString &Type::GetName() { if (!m_name) m_name = GetForwardCompilerType().GetConstTypeName(); return m_name; } void Type::DumpTypeName(Stream *s) { GetName().Dump(s, ""); } void Type::DumpValue(ExecutionContext *exe_ctx, Stream *s, const DataExtractor &data, uint32_t data_byte_offset, bool show_types, bool show_summary, bool verbose, lldb::Format format) { if (ResolveClangType(eResolveStateForward)) { if (show_types) { s->PutChar('('); if (verbose) s->Printf("Type{0x%8.8" PRIx64 "} ", GetID()); DumpTypeName(s); s->PutCString(") "); } GetForwardCompilerType().DumpValue( exe_ctx, s, format == lldb::eFormatDefault ? GetFormat() : format, data, data_byte_offset, GetByteSize(), 0, // Bitfield bit size 0, // Bitfield bit offset show_types, show_summary, verbose, 0); } } Type *Type::GetEncodingType() { if (m_encoding_type == nullptr && m_encoding_uid != LLDB_INVALID_UID) m_encoding_type = m_symbol_file->ResolveTypeUID(m_encoding_uid); return m_encoding_type; } uint64_t Type::GetByteSize() { if (m_byte_size == 0) { switch (m_encoding_uid_type) { case eEncodingInvalid: case eEncodingIsSyntheticUID: break; case eEncodingIsUID: case eEncodingIsConstUID: case eEncodingIsRestrictUID: case eEncodingIsVolatileUID: case eEncodingIsTypedefUID: { Type *encoding_type = GetEncodingType(); if (encoding_type) m_byte_size = encoding_type->GetByteSize(); if (m_byte_size == 0) m_byte_size = GetLayoutCompilerType().GetByteSize(nullptr); } break; // If we are a pointer or reference, then this is just a pointer size; case eEncodingIsPointerUID: case eEncodingIsLValueReferenceUID: case eEncodingIsRValueReferenceUID: { ArchSpec arch; if (m_symbol_file->GetObjectFile()->GetArchitecture(arch)) m_byte_size = arch.GetAddressByteSize(); } break; } } return m_byte_size; } uint32_t Type::GetNumChildren(bool omit_empty_base_classes) { return GetForwardCompilerType().GetNumChildren(omit_empty_base_classes); } bool Type::IsAggregateType() { return GetForwardCompilerType().IsAggregateType(); } lldb::TypeSP Type::GetTypedefType() { lldb::TypeSP type_sp; if (IsTypedef()) { Type *typedef_type = m_symbol_file->ResolveTypeUID(m_encoding_uid); if (typedef_type) type_sp = typedef_type->shared_from_this(); } return type_sp; } lldb::Format Type::GetFormat() { return GetForwardCompilerType().GetFormat(); } lldb::Encoding Type::GetEncoding(uint64_t &count) { // Make sure we resolve our type if it already hasn't been. return GetForwardCompilerType().GetEncoding(count); } bool Type::DumpValueInMemory(ExecutionContext *exe_ctx, Stream *s, lldb::addr_t address, AddressType address_type, bool show_types, bool show_summary, bool verbose) { if (address != LLDB_INVALID_ADDRESS) { DataExtractor data; Target *target = nullptr; if (exe_ctx) target = exe_ctx->GetTargetPtr(); if (target) data.SetByteOrder(target->GetArchitecture().GetByteOrder()); if (ReadFromMemory(exe_ctx, address, address_type, data)) { DumpValue(exe_ctx, s, data, 0, show_types, show_summary, verbose); return true; } } return false; } bool Type::ReadFromMemory(ExecutionContext *exe_ctx, lldb::addr_t addr, AddressType address_type, DataExtractor &data) { if (address_type == eAddressTypeFile) { // Can't convert a file address to anything valid without more // context (which Module it came from) return false; } const uint64_t byte_size = GetByteSize(); if (data.GetByteSize() < byte_size) { lldb::DataBufferSP data_sp(new DataBufferHeap(byte_size, '\0')); data.SetData(data_sp); } uint8_t *dst = const_cast(data.PeekData(0, byte_size)); if (dst != nullptr) { if (address_type == eAddressTypeHost) { // The address is an address in this process, so just copy it if (addr == 0) return false; memcpy(dst, (uint8_t *)nullptr + addr, byte_size); return true; } else { if (exe_ctx) { Process *process = exe_ctx->GetProcessPtr(); if (process) { Error error; return exe_ctx->GetProcessPtr()->ReadMemory(addr, dst, byte_size, error) == byte_size; } } } } return false; } bool Type::WriteToMemory(ExecutionContext *exe_ctx, lldb::addr_t addr, AddressType address_type, DataExtractor &data) { return false; } TypeList *Type::GetTypeList() { return GetSymbolFile()->GetTypeList(); } const Declaration &Type::GetDeclaration() const { return m_decl; } bool Type::ResolveClangType(ResolveState compiler_type_resolve_state) { // TODO: This needs to consider the correct type system to use. Type *encoding_type = nullptr; if (!m_compiler_type.IsValid()) { encoding_type = GetEncodingType(); if (encoding_type) { switch (m_encoding_uid_type) { case eEncodingIsUID: { CompilerType encoding_compiler_type = encoding_type->GetForwardCompilerType(); if (encoding_compiler_type.IsValid()) { m_compiler_type = encoding_compiler_type; m_flags.compiler_type_resolve_state = encoding_type->m_flags.compiler_type_resolve_state; } } break; case eEncodingIsConstUID: m_compiler_type = encoding_type->GetForwardCompilerType().AddConstModifier(); break; case eEncodingIsRestrictUID: m_compiler_type = encoding_type->GetForwardCompilerType().AddRestrictModifier(); break; case eEncodingIsVolatileUID: m_compiler_type = encoding_type->GetForwardCompilerType().AddVolatileModifier(); break; case eEncodingIsTypedefUID: m_compiler_type = encoding_type->GetForwardCompilerType().CreateTypedef( m_name.AsCString("__lldb_invalid_typedef_name"), GetSymbolFile()->GetDeclContextContainingUID(GetID())); m_name.Clear(); break; case eEncodingIsPointerUID: m_compiler_type = encoding_type->GetForwardCompilerType().GetPointerType(); break; case eEncodingIsLValueReferenceUID: m_compiler_type = encoding_type->GetForwardCompilerType().GetLValueReferenceType(); break; case eEncodingIsRValueReferenceUID: m_compiler_type = encoding_type->GetForwardCompilerType().GetRValueReferenceType(); break; default: - assert(!"Unhandled encoding_data_type."); - break; + llvm_unreachable("Unhandled encoding_data_type."); } } else { // We have no encoding type, return void? TypeSystem *type_system = m_symbol_file->GetTypeSystemForLanguage(eLanguageTypeC); CompilerType void_compiler_type = type_system->GetBasicTypeFromAST(eBasicTypeVoid); switch (m_encoding_uid_type) { case eEncodingIsUID: m_compiler_type = void_compiler_type; break; case eEncodingIsConstUID: m_compiler_type = void_compiler_type.AddConstModifier(); break; case eEncodingIsRestrictUID: m_compiler_type = void_compiler_type.AddRestrictModifier(); break; case eEncodingIsVolatileUID: m_compiler_type = void_compiler_type.AddVolatileModifier(); break; case eEncodingIsTypedefUID: m_compiler_type = void_compiler_type.CreateTypedef( m_name.AsCString("__lldb_invalid_typedef_name"), GetSymbolFile()->GetDeclContextContainingUID(GetID())); break; case eEncodingIsPointerUID: m_compiler_type = void_compiler_type.GetPointerType(); break; case eEncodingIsLValueReferenceUID: m_compiler_type = void_compiler_type.GetLValueReferenceType(); break; case eEncodingIsRValueReferenceUID: m_compiler_type = void_compiler_type.GetRValueReferenceType(); break; default: - assert(!"Unhandled encoding_data_type."); - break; + llvm_unreachable("Unhandled encoding_data_type."); } } // When we have a EncodingUID, our "m_flags.compiler_type_resolve_state" is // set to eResolveStateUnresolved // so we need to update it to say that we now have a forward declaration // since that is what we created // above. if (m_compiler_type.IsValid()) m_flags.compiler_type_resolve_state = eResolveStateForward; } // Check if we have a forward reference to a class/struct/union/enum? if (compiler_type_resolve_state == eResolveStateLayout || compiler_type_resolve_state == eResolveStateFull) { // Check if we have a forward reference to a class/struct/union/enum? if (m_compiler_type.IsValid() && m_flags.compiler_type_resolve_state < compiler_type_resolve_state) { m_flags.compiler_type_resolve_state = eResolveStateFull; if (!m_compiler_type.IsDefined()) { // We have a forward declaration, we need to resolve it to a complete // definition. m_symbol_file->CompleteType(m_compiler_type); } } } // If we have an encoding type, then we need to make sure it is // resolved appropriately. if (m_encoding_uid != LLDB_INVALID_UID) { if (encoding_type == nullptr) encoding_type = GetEncodingType(); if (encoding_type) { ResolveState encoding_compiler_type_resolve_state = compiler_type_resolve_state; if (compiler_type_resolve_state == eResolveStateLayout) { switch (m_encoding_uid_type) { case eEncodingIsPointerUID: case eEncodingIsLValueReferenceUID: case eEncodingIsRValueReferenceUID: encoding_compiler_type_resolve_state = eResolveStateForward; break; default: break; } } encoding_type->ResolveClangType(encoding_compiler_type_resolve_state); } } return m_compiler_type.IsValid(); } uint32_t Type::GetEncodingMask() { uint32_t encoding_mask = 1u << m_encoding_uid_type; Type *encoding_type = GetEncodingType(); assert(encoding_type != this); if (encoding_type) encoding_mask |= encoding_type->GetEncodingMask(); return encoding_mask; } CompilerType Type::GetFullCompilerType() { ResolveClangType(eResolveStateFull); return m_compiler_type; } CompilerType Type::GetLayoutCompilerType() { ResolveClangType(eResolveStateLayout); return m_compiler_type; } CompilerType Type::GetForwardCompilerType() { ResolveClangType(eResolveStateForward); return m_compiler_type; } int Type::Compare(const Type &a, const Type &b) { // Just compare the UID values for now... lldb::user_id_t a_uid = a.GetID(); lldb::user_id_t b_uid = b.GetID(); if (a_uid < b_uid) return -1; if (a_uid > b_uid) return 1; return 0; } ConstString Type::GetQualifiedName() { return GetForwardCompilerType().GetConstTypeName(); } bool Type::GetTypeScopeAndBasename(const char *&name_cstr, std::string &scope, std::string &basename, TypeClass &type_class) { // Protect against null c string. type_class = eTypeClassAny; if (name_cstr && name_cstr[0]) { llvm::StringRef name_strref(name_cstr); if (name_strref.startswith("struct ")) { name_cstr += 7; type_class = eTypeClassStruct; } else if (name_strref.startswith("class ")) { name_cstr += 6; type_class = eTypeClassClass; } else if (name_strref.startswith("union ")) { name_cstr += 6; type_class = eTypeClassUnion; } else if (name_strref.startswith("enum ")) { name_cstr += 5; type_class = eTypeClassEnumeration; } else if (name_strref.startswith("typedef ")) { name_cstr += 8; type_class = eTypeClassTypedef; } const char *basename_cstr = name_cstr; const char *namespace_separator = ::strstr(basename_cstr, "::"); if (namespace_separator) { const char *template_arg_char = ::strchr(basename_cstr, '<'); while (namespace_separator != nullptr) { if (template_arg_char && namespace_separator > template_arg_char) // but namespace'd template // arguments are still good // to go break; basename_cstr = namespace_separator + 2; namespace_separator = strstr(basename_cstr, "::"); } if (basename_cstr > name_cstr) { scope.assign(name_cstr, basename_cstr - name_cstr); basename.assign(basename_cstr); return true; } } } return false; } ModuleSP Type::GetModule() { if (m_symbol_file) return m_symbol_file->GetObjectFile()->GetModule(); return ModuleSP(); } TypeAndOrName::TypeAndOrName() : m_type_pair(), m_type_name() {} TypeAndOrName::TypeAndOrName(TypeSP &in_type_sp) : m_type_pair(in_type_sp) { if (in_type_sp) m_type_name = in_type_sp->GetName(); } TypeAndOrName::TypeAndOrName(const char *in_type_str) : m_type_name(in_type_str) {} TypeAndOrName::TypeAndOrName(const TypeAndOrName &rhs) : m_type_pair(rhs.m_type_pair), m_type_name(rhs.m_type_name) {} TypeAndOrName::TypeAndOrName(ConstString &in_type_const_string) : m_type_name(in_type_const_string) {} TypeAndOrName &TypeAndOrName::operator=(const TypeAndOrName &rhs) { if (this != &rhs) { m_type_name = rhs.m_type_name; m_type_pair = rhs.m_type_pair; } return *this; } bool TypeAndOrName::operator==(const TypeAndOrName &other) const { if (m_type_pair != other.m_type_pair) return false; if (m_type_name != other.m_type_name) return false; return true; } bool TypeAndOrName::operator!=(const TypeAndOrName &other) const { if (m_type_pair != other.m_type_pair) return true; if (m_type_name != other.m_type_name) return true; return false; } ConstString TypeAndOrName::GetName() const { if (m_type_name) return m_type_name; if (m_type_pair) return m_type_pair.GetName(); return ConstString(""); } void TypeAndOrName::SetName(const ConstString &type_name) { m_type_name = type_name; } void TypeAndOrName::SetName(const char *type_name_cstr) { m_type_name.SetCString(type_name_cstr); } void TypeAndOrName::SetTypeSP(lldb::TypeSP type_sp) { m_type_pair.SetType(type_sp); if (m_type_pair) m_type_name = m_type_pair.GetName(); } void TypeAndOrName::SetCompilerType(CompilerType compiler_type) { m_type_pair.SetType(compiler_type); if (m_type_pair) m_type_name = m_type_pair.GetName(); } bool TypeAndOrName::IsEmpty() const { if ((bool)m_type_name || (bool)m_type_pair) return false; else return true; } void TypeAndOrName::Clear() { m_type_name.Clear(); m_type_pair.Clear(); } bool TypeAndOrName::HasName() const { return (bool)m_type_name; } bool TypeAndOrName::HasTypeSP() const { return m_type_pair.GetTypeSP().get() != nullptr; } bool TypeAndOrName::HasCompilerType() const { return m_type_pair.GetCompilerType().IsValid(); } TypeImpl::TypeImpl() : m_module_wp(), m_static_type(), m_dynamic_type() {} TypeImpl::TypeImpl(const TypeImpl &rhs) : m_module_wp(rhs.m_module_wp), m_static_type(rhs.m_static_type), m_dynamic_type(rhs.m_dynamic_type) {} TypeImpl::TypeImpl(const lldb::TypeSP &type_sp) : m_module_wp(), m_static_type(), m_dynamic_type() { SetType(type_sp); } TypeImpl::TypeImpl(const CompilerType &compiler_type) : m_module_wp(), m_static_type(), m_dynamic_type() { SetType(compiler_type); } TypeImpl::TypeImpl(const lldb::TypeSP &type_sp, const CompilerType &dynamic) : m_module_wp(), m_static_type(type_sp), m_dynamic_type(dynamic) { SetType(type_sp, dynamic); } TypeImpl::TypeImpl(const CompilerType &static_type, const CompilerType &dynamic_type) : m_module_wp(), m_static_type(), m_dynamic_type() { SetType(static_type, dynamic_type); } TypeImpl::TypeImpl(const TypePair &pair, const CompilerType &dynamic) : m_module_wp(), m_static_type(), m_dynamic_type() { SetType(pair, dynamic); } void TypeImpl::SetType(const lldb::TypeSP &type_sp) { m_static_type.SetType(type_sp); if (type_sp) m_module_wp = type_sp->GetModule(); else m_module_wp = lldb::ModuleWP(); } void TypeImpl::SetType(const CompilerType &compiler_type) { m_module_wp = lldb::ModuleWP(); m_static_type.SetType(compiler_type); } void TypeImpl::SetType(const lldb::TypeSP &type_sp, const CompilerType &dynamic) { SetType(type_sp); m_dynamic_type = dynamic; } void TypeImpl::SetType(const CompilerType &compiler_type, const CompilerType &dynamic) { m_module_wp = lldb::ModuleWP(); m_static_type.SetType(compiler_type); m_dynamic_type = dynamic; } void TypeImpl::SetType(const TypePair &pair, const CompilerType &dynamic) { m_module_wp = pair.GetModule(); m_static_type = pair; m_dynamic_type = dynamic; } TypeImpl &TypeImpl::operator=(const TypeImpl &rhs) { if (rhs != *this) { m_module_wp = rhs.m_module_wp; m_static_type = rhs.m_static_type; m_dynamic_type = rhs.m_dynamic_type; } return *this; } bool TypeImpl::CheckModule(lldb::ModuleSP &module_sp) const { // Check if we have a module for this type. If we do and the shared pointer is // can be successfully initialized with m_module_wp, return true. Else return // false // if we didn't have a module, or if we had a module and it has been deleted. // Any // functions doing anything with a TypeSP in this TypeImpl class should call // this // function and only do anything with the ivars if this function returns true. // If // we have a module, the "module_sp" will be filled in with a strong reference // to the // module so that the module will at least stay around long enough for the // type // query to succeed. module_sp = m_module_wp.lock(); if (!module_sp) { lldb::ModuleWP empty_module_wp; // If either call to "std::weak_ptr::owner_before(...) value returns true, // this // indicates that m_module_wp once contained (possibly still does) a // reference // to a valid shared pointer. This helps us know if we had a valid reference // to // a section which is now invalid because the module it was in was deleted if (empty_module_wp.owner_before(m_module_wp) || m_module_wp.owner_before(empty_module_wp)) { // m_module_wp had a valid reference to a module, but all strong // references // have been released and the module has been deleted return false; } } // We either successfully locked the module, or didn't have one to begin with return true; } bool TypeImpl::operator==(const TypeImpl &rhs) const { return m_static_type == rhs.m_static_type && m_dynamic_type == rhs.m_dynamic_type; } bool TypeImpl::operator!=(const TypeImpl &rhs) const { return m_static_type != rhs.m_static_type || m_dynamic_type != rhs.m_dynamic_type; } bool TypeImpl::IsValid() const { // just a name is not valid ModuleSP module_sp; if (CheckModule(module_sp)) return m_static_type.IsValid() || m_dynamic_type.IsValid(); return false; } TypeImpl::operator bool() const { return IsValid(); } void TypeImpl::Clear() { m_module_wp = lldb::ModuleWP(); m_static_type.Clear(); m_dynamic_type.Clear(); } ConstString TypeImpl::GetName() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type) return m_dynamic_type.GetTypeName(); return m_static_type.GetName(); } return ConstString(); } ConstString TypeImpl::GetDisplayTypeName() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type) return m_dynamic_type.GetDisplayTypeName(); return m_static_type.GetDisplayTypeName(); } return ConstString(); } TypeImpl TypeImpl::GetPointerType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetPointerType(), m_dynamic_type.GetPointerType()); } return TypeImpl(m_static_type.GetPointerType()); } return TypeImpl(); } TypeImpl TypeImpl::GetPointeeType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetPointeeType(), m_dynamic_type.GetPointeeType()); } return TypeImpl(m_static_type.GetPointeeType()); } return TypeImpl(); } TypeImpl TypeImpl::GetReferenceType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetReferenceType(), m_dynamic_type.GetLValueReferenceType()); } return TypeImpl(m_static_type.GetReferenceType()); } return TypeImpl(); } TypeImpl TypeImpl::GetTypedefedType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetTypedefedType(), m_dynamic_type.GetTypedefedType()); } return TypeImpl(m_static_type.GetTypedefedType()); } return TypeImpl(); } TypeImpl TypeImpl::GetDereferencedType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetDereferencedType(), m_dynamic_type.GetNonReferenceType()); } return TypeImpl(m_static_type.GetDereferencedType()); } return TypeImpl(); } TypeImpl TypeImpl::GetUnqualifiedType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetUnqualifiedType(), m_dynamic_type.GetFullyUnqualifiedType()); } return TypeImpl(m_static_type.GetUnqualifiedType()); } return TypeImpl(); } TypeImpl TypeImpl::GetCanonicalType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetCanonicalType(), m_dynamic_type.GetCanonicalType()); } return TypeImpl(m_static_type.GetCanonicalType()); } return TypeImpl(); } CompilerType TypeImpl::GetCompilerType(bool prefer_dynamic) { ModuleSP module_sp; if (CheckModule(module_sp)) { if (prefer_dynamic) { if (m_dynamic_type.IsValid()) return m_dynamic_type; } return m_static_type.GetCompilerType(); } return CompilerType(); } TypeSystem *TypeImpl::GetTypeSystem(bool prefer_dynamic) { ModuleSP module_sp; if (CheckModule(module_sp)) { if (prefer_dynamic) { if (m_dynamic_type.IsValid()) return m_dynamic_type.GetTypeSystem(); } return m_static_type.GetCompilerType().GetTypeSystem(); } return NULL; } bool TypeImpl::GetDescription(lldb_private::Stream &strm, lldb::DescriptionLevel description_level) { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { strm.Printf("Dynamic:\n"); m_dynamic_type.DumpTypeDescription(&strm); strm.Printf("\nStatic:\n"); } m_static_type.GetCompilerType().DumpTypeDescription(&strm); } else { strm.PutCString("Invalid TypeImpl module for type has been deleted\n"); } return true; } bool TypeMemberFunctionImpl::IsValid() { return m_type.IsValid() && m_kind != lldb::eMemberFunctionKindUnknown; } ConstString TypeMemberFunctionImpl::GetName() const { return m_name; } ConstString TypeMemberFunctionImpl::GetMangledName() const { return m_decl.GetMangledName(); } CompilerType TypeMemberFunctionImpl::GetType() const { return m_type; } lldb::MemberFunctionKind TypeMemberFunctionImpl::GetKind() const { return m_kind; } bool TypeMemberFunctionImpl::GetDescription(Stream &stream) { switch (m_kind) { case lldb::eMemberFunctionKindUnknown: return false; case lldb::eMemberFunctionKindConstructor: stream.Printf("constructor for %s", m_type.GetTypeName().AsCString("")); break; case lldb::eMemberFunctionKindDestructor: stream.Printf("destructor for %s", m_type.GetTypeName().AsCString("")); break; case lldb::eMemberFunctionKindInstanceMethod: stream.Printf("instance method %s of type %s", m_name.AsCString(), m_decl.GetDeclContext().GetName().AsCString()); break; case lldb::eMemberFunctionKindStaticMethod: stream.Printf("static method %s of type %s", m_name.AsCString(), m_decl.GetDeclContext().GetName().AsCString()); break; } return true; } CompilerType TypeMemberFunctionImpl::GetReturnType() const { if (m_type) return m_type.GetFunctionReturnType(); return m_decl.GetFunctionReturnType(); } size_t TypeMemberFunctionImpl::GetNumArguments() const { if (m_type) return m_type.GetNumberOfFunctionArguments(); else return m_decl.GetNumFunctionArguments(); } CompilerType TypeMemberFunctionImpl::GetArgumentAtIndex(size_t idx) const { if (m_type) return m_type.GetFunctionArgumentAtIndex(idx); else return m_decl.GetFunctionArgumentType(idx); } TypeEnumMemberImpl::TypeEnumMemberImpl(const lldb::TypeImplSP &integer_type_sp, const ConstString &name, const llvm::APSInt &value) : m_integer_type_sp(integer_type_sp), m_name(name), m_value(value), m_valid((bool)name && (bool)integer_type_sp) {} Index: vendor/lldb/dist/source/Target/ABI.cpp =================================================================== --- vendor/lldb/dist/source/Target/ABI.cpp (revision 311541) +++ vendor/lldb/dist/source/Target/ABI.cpp (revision 311542) @@ -1,217 +1,216 @@ //===-- ABI.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/Target/ABI.h" #include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Value.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" using namespace lldb; using namespace lldb_private; ABISP ABI::FindPlugin(const ArchSpec &arch) { ABISP abi_sp; ABICreateInstance create_callback; for (uint32_t idx = 0; (create_callback = PluginManager::GetABICreateCallbackAtIndex(idx)) != nullptr; ++idx) { abi_sp = create_callback(arch); if (abi_sp) return abi_sp; } abi_sp.reset(); return abi_sp; } ABI::ABI() = default; ABI::~ABI() = default; bool ABI::GetRegisterInfoByName(const ConstString &name, RegisterInfo &info) { uint32_t count = 0; const RegisterInfo *register_info_array = GetRegisterInfoArray(count); if (register_info_array) { const char *unique_name_cstr = name.GetCString(); uint32_t i; for (i = 0; i < count; ++i) { if (register_info_array[i].name == unique_name_cstr) { info = register_info_array[i]; return true; } } for (i = 0; i < count; ++i) { if (register_info_array[i].alt_name == unique_name_cstr) { info = register_info_array[i]; return true; } } } return false; } bool ABI::GetRegisterInfoByKind(RegisterKind reg_kind, uint32_t reg_num, RegisterInfo &info) { if (reg_kind < eRegisterKindEHFrame || reg_kind >= kNumRegisterKinds) return false; uint32_t count = 0; const RegisterInfo *register_info_array = GetRegisterInfoArray(count); if (register_info_array) { for (uint32_t i = 0; i < count; ++i) { if (register_info_array[i].kinds[reg_kind] == reg_num) { info = register_info_array[i]; return true; } } } return false; } ValueObjectSP ABI::GetReturnValueObject(Thread &thread, CompilerType &ast_type, bool persistent) const { if (!ast_type.IsValid()) return ValueObjectSP(); ValueObjectSP return_valobj_sp; return_valobj_sp = GetReturnValueObjectImpl(thread, ast_type); if (!return_valobj_sp) return return_valobj_sp; // Now turn this into a persistent variable. // FIXME: This code is duplicated from Target::EvaluateExpression, and it is // used in similar form in a couple // of other places. Figure out the correct Create function to do all this // work. if (persistent) { PersistentExpressionState *persistent_expression_state = thread.CalculateTarget()->GetPersistentExpressionStateForLanguage( ast_type.GetMinimumLanguage()); if (!persistent_expression_state) return ValueObjectSP(); ConstString persistent_variable_name( persistent_expression_state->GetNextPersistentVariableName()); lldb::ValueObjectSP const_valobj_sp; // Check in case our value is already a constant value if (return_valobj_sp->GetIsConstant()) { const_valobj_sp = return_valobj_sp; const_valobj_sp->SetName(persistent_variable_name); } else const_valobj_sp = return_valobj_sp->CreateConstantValue(persistent_variable_name); lldb::ValueObjectSP live_valobj_sp = return_valobj_sp; return_valobj_sp = const_valobj_sp; ExpressionVariableSP clang_expr_variable_sp( persistent_expression_state->CreatePersistentVariable( return_valobj_sp)); assert(clang_expr_variable_sp); // Set flags and live data as appropriate const Value &result_value = live_valobj_sp->GetValue(); switch (result_value.GetValueType()) { case Value::eValueTypeHostAddress: case Value::eValueTypeFileAddress: // we don't do anything with these for now break; case Value::eValueTypeScalar: case Value::eValueTypeVector: clang_expr_variable_sp->m_flags |= ClangExpressionVariable::EVIsFreezeDried; clang_expr_variable_sp->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; clang_expr_variable_sp->m_flags |= ClangExpressionVariable::EVNeedsAllocation; break; case Value::eValueTypeLoadAddress: clang_expr_variable_sp->m_live_sp = live_valobj_sp; clang_expr_variable_sp->m_flags |= ClangExpressionVariable::EVIsProgramReference; break; } return_valobj_sp = clang_expr_variable_sp->GetValueObject(); } return return_valobj_sp; } ValueObjectSP ABI::GetReturnValueObject(Thread &thread, llvm::Type &ast_type, bool persistent) const { ValueObjectSP return_valobj_sp; return_valobj_sp = GetReturnValueObjectImpl(thread, ast_type); return return_valobj_sp; } // specialized to work with llvm IR types // // for now we will specify a default implementation so that we don't need to // modify other ABIs lldb::ValueObjectSP ABI::GetReturnValueObjectImpl(Thread &thread, llvm::Type &ir_type) const { ValueObjectSP return_valobj_sp; /* this is a dummy and will only be called if an ABI does not override this */ return return_valobj_sp; } bool ABI::PrepareTrivialCall(Thread &thread, lldb::addr_t sp, lldb::addr_t functionAddress, lldb::addr_t returnAddress, llvm::Type &returntype, llvm::ArrayRef args) const { // dummy prepare trivial call - assert(!"Should never get here!"); - return false; + llvm_unreachable("Should never get here!"); } bool ABI::GetFallbackRegisterLocation( const RegisterInfo *reg_info, UnwindPlan::Row::RegisterLocation &unwind_regloc) { // Did the UnwindPlan fail to give us the caller's stack pointer? // The stack pointer is defined to be the same as THIS frame's CFA, so return // the CFA value as // the caller's stack pointer. This is true on x86-32/x86-64 at least. if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_SP) { unwind_regloc.SetIsCFAPlusOffset(0); return true; } // If a volatile register is being requested, we don't want to forward the // next frame's register contents // up the stack -- the register is not retrievable at this frame. if (RegisterIsVolatile(reg_info)) { unwind_regloc.SetUndefined(); return true; } return false; } Index: vendor/lldb/dist/source/Target/Platform.cpp =================================================================== --- vendor/lldb/dist/source/Target/Platform.cpp (revision 311541) +++ vendor/lldb/dist/source/Target/Platform.cpp (revision 311542) @@ -1,1889 +1,1888 @@ //===-- Platform.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 #include #include // Other libraries and framework includes #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" // Project includes #include "Utility/ModuleCache.h" #include "lldb/Breakpoint/BreakpointIDList.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/StructuredData.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Property.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/Utils.h" // Define these constants from POSIX mman.h rather than include the file // so that they will be correct even when compiled on Linux. #define MAP_PRIVATE 2 #define MAP_ANON 0x1000 using namespace lldb; using namespace lldb_private; static uint32_t g_initialize_count = 0; // Use a singleton function for g_local_platform_sp to avoid init // constructors since LLDB is often part of a shared library static PlatformSP &GetHostPlatformSP() { static PlatformSP g_platform_sp; return g_platform_sp; } const char *Platform::GetHostPlatformName() { return "host"; } namespace { PropertyDefinition g_properties[] = { {"use-module-cache", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "Use module cache."}, {"module-cache-directory", OptionValue::eTypeFileSpec, true, 0, nullptr, nullptr, "Root directory for cached modules."}, {nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}}; enum { ePropertyUseModuleCache, ePropertyModuleCacheDirectory }; } // namespace ConstString PlatformProperties::GetSettingName() { static ConstString g_setting_name("platform"); return g_setting_name; } PlatformProperties::PlatformProperties() { m_collection_sp.reset(new OptionValueProperties(GetSettingName())); m_collection_sp->Initialize(g_properties); auto module_cache_dir = GetModuleCacheDirectory(); if (module_cache_dir) return; llvm::SmallString<64> user_home_dir; if (!llvm::sys::path::home_directory(user_home_dir)) return; module_cache_dir = FileSpec(user_home_dir.c_str(), false); module_cache_dir.AppendPathComponent(".lldb"); module_cache_dir.AppendPathComponent("module_cache"); SetModuleCacheDirectory(module_cache_dir); } bool PlatformProperties::GetUseModuleCache() const { const auto idx = ePropertyUseModuleCache; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool PlatformProperties::SetUseModuleCache(bool use_module_cache) { return m_collection_sp->SetPropertyAtIndexAsBoolean( nullptr, ePropertyUseModuleCache, use_module_cache); } FileSpec PlatformProperties::GetModuleCacheDirectory() const { return m_collection_sp->GetPropertyAtIndexAsFileSpec( nullptr, ePropertyModuleCacheDirectory); } bool PlatformProperties::SetModuleCacheDirectory(const FileSpec &dir_spec) { return m_collection_sp->SetPropertyAtIndexAsFileSpec( nullptr, ePropertyModuleCacheDirectory, dir_spec); } //------------------------------------------------------------------ /// Get the native host platform plug-in. /// /// There should only be one of these for each host that LLDB runs /// upon that should be statically compiled in and registered using /// preprocessor macros or other similar build mechanisms. /// /// This platform will be used as the default platform when launching /// or attaching to processes unless another platform is specified. //------------------------------------------------------------------ PlatformSP Platform::GetHostPlatform() { return GetHostPlatformSP(); } static std::vector &GetPlatformList() { static std::vector g_platform_list; return g_platform_list; } static std::recursive_mutex &GetPlatformListMutex() { static std::recursive_mutex g_mutex; return g_mutex; } void Platform::Initialize() { g_initialize_count++; } void Platform::Terminate() { if (g_initialize_count > 0) { if (--g_initialize_count == 0) { std::lock_guard guard(GetPlatformListMutex()); GetPlatformList().clear(); } } } const PlatformPropertiesSP &Platform::GetGlobalPlatformProperties() { static const auto g_settings_sp(std::make_shared()); return g_settings_sp; } void Platform::SetHostPlatform(const lldb::PlatformSP &platform_sp) { // The native platform should use its static void Platform::Initialize() // function to register itself as the native platform. GetHostPlatformSP() = platform_sp; if (platform_sp) { std::lock_guard guard(GetPlatformListMutex()); GetPlatformList().push_back(platform_sp); } } Error Platform::GetFileWithUUID(const FileSpec &platform_file, const UUID *uuid_ptr, FileSpec &local_file) { // Default to the local case local_file = platform_file; return Error(); } FileSpecList Platform::LocateExecutableScriptingResources(Target *target, Module &module, Stream *feedback_stream) { return FileSpecList(); } // PlatformSP // Platform::FindPlugin (Process *process, const ConstString &plugin_name) //{ // PlatformCreateInstance create_callback = nullptr; // if (plugin_name) // { // create_callback = // PluginManager::GetPlatformCreateCallbackForPluginName (plugin_name); // if (create_callback) // { // ArchSpec arch; // if (process) // { // arch = process->GetTarget().GetArchitecture(); // } // PlatformSP platform_sp(create_callback(process, &arch)); // if (platform_sp) // return platform_sp; // } // } // else // { // for (uint32_t idx = 0; (create_callback = // PluginManager::GetPlatformCreateCallbackAtIndex(idx)) != nullptr; // ++idx) // { // PlatformSP platform_sp(create_callback(process, nullptr)); // if (platform_sp) // return platform_sp; // } // } // return PlatformSP(); //} Error Platform::GetSharedModule(const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, const FileSpecList *module_search_paths_ptr, ModuleSP *old_module_sp_ptr, bool *did_create_ptr) { if (IsHost()) return ModuleList::GetSharedModule( module_spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr, false); return GetRemoteSharedModule(module_spec, process, module_sp, [&](const ModuleSpec &spec) { Error error = ModuleList::GetSharedModule( spec, module_sp, module_search_paths_ptr, old_module_sp_ptr, did_create_ptr, false); if (error.Success() && module_sp) module_sp->SetPlatformFileSpec( spec.GetFileSpec()); return error; }, did_create_ptr); } bool Platform::GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, ModuleSpec &module_spec) { ModuleSpecList module_specs; if (ObjectFile::GetModuleSpecifications(module_file_spec, 0, 0, module_specs) == 0) return false; ModuleSpec matched_module_spec; return module_specs.FindMatchingModuleSpec(ModuleSpec(module_file_spec, arch), module_spec); } PlatformSP Platform::Find(const ConstString &name) { if (name) { static ConstString g_host_platform_name("host"); if (name == g_host_platform_name) return GetHostPlatform(); std::lock_guard guard(GetPlatformListMutex()); for (const auto &platform_sp : GetPlatformList()) { if (platform_sp->GetName() == name) return platform_sp; } } return PlatformSP(); } PlatformSP Platform::Create(const ConstString &name, Error &error) { PlatformCreateInstance create_callback = nullptr; lldb::PlatformSP platform_sp; if (name) { static ConstString g_host_platform_name("host"); if (name == g_host_platform_name) return GetHostPlatform(); create_callback = PluginManager::GetPlatformCreateCallbackForPluginName(name); if (create_callback) platform_sp = create_callback(true, nullptr); else error.SetErrorStringWithFormat( "unable to find a plug-in for the platform named \"%s\"", name.GetCString()); } else error.SetErrorString("invalid platform name"); if (platform_sp) { std::lock_guard guard(GetPlatformListMutex()); GetPlatformList().push_back(platform_sp); } return platform_sp; } PlatformSP Platform::Create(const ArchSpec &arch, ArchSpec *platform_arch_ptr, Error &error) { lldb::PlatformSP platform_sp; if (arch.IsValid()) { // Scope for locker { // First try exact arch matches across all platforms already created std::lock_guard guard(GetPlatformListMutex()); for (const auto &platform_sp : GetPlatformList()) { if (platform_sp->IsCompatibleArchitecture(arch, true, platform_arch_ptr)) return platform_sp; } // Next try compatible arch matches across all platforms already created for (const auto &platform_sp : GetPlatformList()) { if (platform_sp->IsCompatibleArchitecture(arch, false, platform_arch_ptr)) return platform_sp; } } PlatformCreateInstance create_callback; // First try exact arch matches across all platform plug-ins uint32_t idx; for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)); ++idx) { if (create_callback) { platform_sp = create_callback(false, &arch); if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, true, platform_arch_ptr)) { std::lock_guard guard(GetPlatformListMutex()); GetPlatformList().push_back(platform_sp); return platform_sp; } } } // Next try compatible arch matches across all platform plug-ins for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)); ++idx) { if (create_callback) { platform_sp = create_callback(false, &arch); if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, false, platform_arch_ptr)) { std::lock_guard guard(GetPlatformListMutex()); GetPlatformList().push_back(platform_sp); return platform_sp; } } } } else error.SetErrorString("invalid platform name"); if (platform_arch_ptr) platform_arch_ptr->Clear(); platform_sp.reset(); return platform_sp; } //------------------------------------------------------------------ /// Default Constructor //------------------------------------------------------------------ Platform::Platform(bool is_host) : m_is_host(is_host), m_os_version_set_while_connected(false), m_system_arch_set_while_connected(false), m_sdk_sysroot(), m_sdk_build(), m_working_dir(), m_remote_url(), m_name(), m_major_os_version(UINT32_MAX), m_minor_os_version(UINT32_MAX), m_update_os_version(UINT32_MAX), m_system_arch(), m_mutex(), m_uid_map(), m_gid_map(), m_max_uid_name_len(0), m_max_gid_name_len(0), m_supports_rsync(false), m_rsync_opts(), m_rsync_prefix(), m_supports_ssh(false), m_ssh_opts(), m_ignores_remote_hostname(false), m_trap_handlers(), m_calculated_trap_handlers(false), m_module_cache(llvm::make_unique()) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p Platform::Platform()", static_cast(this)); } //------------------------------------------------------------------ /// Destructor. /// /// The destructor is virtual since this class is designed to be /// inherited from by the plug-in instance. //------------------------------------------------------------------ Platform::~Platform() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p Platform::~Platform()", static_cast(this)); } void Platform::GetStatus(Stream &strm) { uint32_t major = UINT32_MAX; uint32_t minor = UINT32_MAX; uint32_t update = UINT32_MAX; std::string s; strm.Printf(" Platform: %s\n", GetPluginName().GetCString()); ArchSpec arch(GetSystemArchitecture()); if (arch.IsValid()) { if (!arch.GetTriple().str().empty()) { strm.Printf(" Triple: "); arch.DumpTriple(strm); strm.EOL(); } } if (GetOSVersion(major, minor, update)) { strm.Printf("OS Version: %u", major); if (minor != UINT32_MAX) strm.Printf(".%u", minor); if (update != UINT32_MAX) strm.Printf(".%u", update); if (GetOSBuildString(s)) strm.Printf(" (%s)", s.c_str()); strm.EOL(); } if (GetOSKernelDescription(s)) strm.Printf(" Kernel: %s\n", s.c_str()); if (IsHost()) { strm.Printf(" Hostname: %s\n", GetHostname()); } else { const bool is_connected = IsConnected(); if (is_connected) strm.Printf(" Hostname: %s\n", GetHostname()); strm.Printf(" Connected: %s\n", is_connected ? "yes" : "no"); } if (GetWorkingDirectory()) { strm.Printf("WorkingDir: %s\n", GetWorkingDirectory().GetCString()); } if (!IsConnected()) return; std::string specific_info(GetPlatformSpecificConnectionInformation()); if (!specific_info.empty()) strm.Printf("Platform-specific connection: %s\n", specific_info.c_str()); } bool Platform::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update, Process *process) { std::lock_guard guard(m_mutex); bool success = m_major_os_version != UINT32_MAX; if (IsHost()) { if (!success) { // We have a local host platform success = HostInfo::GetOSVersion(m_major_os_version, m_minor_os_version, m_update_os_version); m_os_version_set_while_connected = success; } } else { // We have a remote platform. We can only fetch the remote // OS version if we are connected, and we don't want to do it // more than once. const bool is_connected = IsConnected(); bool fetch = false; if (success) { // We have valid OS version info, check to make sure it wasn't // manually set prior to connecting. If it was manually set prior // to connecting, then lets fetch the actual OS version info // if we are now connected. if (is_connected && !m_os_version_set_while_connected) fetch = true; } else { // We don't have valid OS version info, fetch it if we are connected fetch = is_connected; } if (fetch) { success = GetRemoteOSVersion(); m_os_version_set_while_connected = success; } } if (success) { major = m_major_os_version; minor = m_minor_os_version; update = m_update_os_version; } else if (process) { // Check with the process in case it can answer the question if // a process was provided return process->GetHostOSVersion(major, minor, update); } return success; } bool Platform::GetOSBuildString(std::string &s) { s.clear(); if (IsHost()) #if !defined(__linux__) return HostInfo::GetOSBuildString(s); #else return false; #endif else return GetRemoteOSBuildString(s); } bool Platform::GetOSKernelDescription(std::string &s) { if (IsHost()) #if !defined(__linux__) return HostInfo::GetOSKernelDescription(s); #else return false; #endif else return GetRemoteOSKernelDescription(s); } void Platform::AddClangModuleCompilationOptions( Target *target, std::vector &options) { std::vector default_compilation_options = { "-x", "c++", "-Xclang", "-nostdsysteminc", "-Xclang", "-nostdsysteminc"}; options.insert(options.end(), default_compilation_options.begin(), default_compilation_options.end()); } FileSpec Platform::GetWorkingDirectory() { if (IsHost()) { char cwd[PATH_MAX]; if (getcwd(cwd, sizeof(cwd))) return FileSpec{cwd, true}; else return FileSpec{}; } else { if (!m_working_dir) m_working_dir = GetRemoteWorkingDirectory(); return m_working_dir; } } struct RecurseCopyBaton { const FileSpec &dst; Platform *platform_ptr; Error error; }; static FileSpec::EnumerateDirectoryResult RecurseCopy_Callback(void *baton, FileSpec::FileType file_type, const FileSpec &src) { RecurseCopyBaton *rc_baton = (RecurseCopyBaton *)baton; switch (file_type) { case FileSpec::eFileTypePipe: case FileSpec::eFileTypeSocket: // we have no way to copy pipes and sockets - ignore them and continue return FileSpec::eEnumerateDirectoryResultNext; break; case FileSpec::eFileTypeDirectory: { // make the new directory and get in there FileSpec dst_dir = rc_baton->dst; if (!dst_dir.GetFilename()) dst_dir.GetFilename() = src.GetLastPathComponent(); Error error = rc_baton->platform_ptr->MakeDirectory( dst_dir, lldb::eFilePermissionsDirectoryDefault); if (error.Fail()) { rc_baton->error.SetErrorStringWithFormat( "unable to setup directory %s on remote end", dst_dir.GetCString()); return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out } // now recurse std::string src_dir_path(src.GetPath()); // Make a filespec that only fills in the directory of a FileSpec so // when we enumerate we can quickly fill in the filename for dst copies FileSpec recurse_dst; recurse_dst.GetDirectory().SetCString(dst_dir.GetPath().c_str()); RecurseCopyBaton rc_baton2 = {recurse_dst, rc_baton->platform_ptr, Error()}; FileSpec::EnumerateDirectory(src_dir_path, true, true, true, RecurseCopy_Callback, &rc_baton2); if (rc_baton2.error.Fail()) { rc_baton->error.SetErrorString(rc_baton2.error.AsCString()); return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out } return FileSpec::eEnumerateDirectoryResultNext; } break; case FileSpec::eFileTypeSymbolicLink: { // copy the file and keep going FileSpec dst_file = rc_baton->dst; if (!dst_file.GetFilename()) dst_file.GetFilename() = src.GetFilename(); FileSpec src_resolved; rc_baton->error = FileSystem::Readlink(src, src_resolved); if (rc_baton->error.Fail()) return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out rc_baton->error = rc_baton->platform_ptr->CreateSymlink(dst_file, src_resolved); if (rc_baton->error.Fail()) return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out return FileSpec::eEnumerateDirectoryResultNext; } break; case FileSpec::eFileTypeRegular: { // copy the file and keep going FileSpec dst_file = rc_baton->dst; if (!dst_file.GetFilename()) dst_file.GetFilename() = src.GetFilename(); Error err = rc_baton->platform_ptr->PutFile(src, dst_file); if (err.Fail()) { rc_baton->error.SetErrorString(err.AsCString()); return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out } return FileSpec::eEnumerateDirectoryResultNext; } break; case FileSpec::eFileTypeInvalid: case FileSpec::eFileTypeOther: case FileSpec::eFileTypeUnknown: rc_baton->error.SetErrorStringWithFormat( "invalid file detected during copy: %s", src.GetPath().c_str()); return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out break; } llvm_unreachable("Unhandled FileSpec::FileType!"); } Error Platform::Install(const FileSpec &src, const FileSpec &dst) { Error error; Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf("Platform::Install (src='%s', dst='%s')", src.GetPath().c_str(), dst.GetPath().c_str()); FileSpec fixed_dst(dst); if (!fixed_dst.GetFilename()) fixed_dst.GetFilename() = src.GetFilename(); FileSpec working_dir = GetWorkingDirectory(); if (dst) { if (dst.GetDirectory()) { const char first_dst_dir_char = dst.GetDirectory().GetCString()[0]; if (first_dst_dir_char == '/' || first_dst_dir_char == '\\') { fixed_dst.GetDirectory() = dst.GetDirectory(); } // If the fixed destination file doesn't have a directory yet, // then we must have a relative path. We will resolve this relative // path against the platform's working directory if (!fixed_dst.GetDirectory()) { FileSpec relative_spec; std::string path; if (working_dir) { relative_spec = working_dir; relative_spec.AppendPathComponent(dst.GetPath()); fixed_dst.GetDirectory() = relative_spec.GetDirectory(); } else { error.SetErrorStringWithFormat( "platform working directory must be valid for relative path '%s'", dst.GetPath().c_str()); return error; } } } else { if (working_dir) { fixed_dst.GetDirectory().SetCString(working_dir.GetCString()); } else { error.SetErrorStringWithFormat( "platform working directory must be valid for relative path '%s'", dst.GetPath().c_str()); return error; } } } else { if (working_dir) { fixed_dst.GetDirectory().SetCString(working_dir.GetCString()); } else { error.SetErrorStringWithFormat("platform working directory must be valid " "when destination directory is empty"); return error; } } if (log) log->Printf("Platform::Install (src='%s', dst='%s') fixed_dst='%s'", src.GetPath().c_str(), dst.GetPath().c_str(), fixed_dst.GetPath().c_str()); if (GetSupportsRSync()) { error = PutFile(src, dst); } else { switch (src.GetFileType()) { case FileSpec::eFileTypeDirectory: { if (GetFileExists(fixed_dst)) Unlink(fixed_dst); uint32_t permissions = src.GetPermissions(); if (permissions == 0) permissions = eFilePermissionsDirectoryDefault; error = MakeDirectory(fixed_dst, permissions); if (error.Success()) { // Make a filespec that only fills in the directory of a FileSpec so // when we enumerate we can quickly fill in the filename for dst copies FileSpec recurse_dst; recurse_dst.GetDirectory().SetCString(fixed_dst.GetCString()); std::string src_dir_path(src.GetPath()); RecurseCopyBaton baton = {recurse_dst, this, Error()}; FileSpec::EnumerateDirectory(src_dir_path, true, true, true, RecurseCopy_Callback, &baton); return baton.error; } } break; case FileSpec::eFileTypeRegular: if (GetFileExists(fixed_dst)) Unlink(fixed_dst); error = PutFile(src, fixed_dst); break; case FileSpec::eFileTypeSymbolicLink: { if (GetFileExists(fixed_dst)) Unlink(fixed_dst); FileSpec src_resolved; error = FileSystem::Readlink(src, src_resolved); if (error.Success()) error = CreateSymlink(dst, src_resolved); } break; case FileSpec::eFileTypePipe: error.SetErrorString("platform install doesn't handle pipes"); break; case FileSpec::eFileTypeSocket: error.SetErrorString("platform install doesn't handle sockets"); break; case FileSpec::eFileTypeInvalid: case FileSpec::eFileTypeUnknown: case FileSpec::eFileTypeOther: error.SetErrorString( "platform install doesn't handle non file or directory items"); break; } } return error; } bool Platform::SetWorkingDirectory(const FileSpec &file_spec) { if (IsHost()) { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf("Platform::SetWorkingDirectory('%s')", file_spec.GetCString()); if (file_spec) { if (::chdir(file_spec.GetCString()) == 0) return true; } return false; } else { m_working_dir.Clear(); return SetRemoteWorkingDirectory(file_spec); } } Error Platform::MakeDirectory(const FileSpec &file_spec, uint32_t permissions) { if (IsHost()) return FileSystem::MakeDirectory(file_spec, permissions); else { Error error; error.SetErrorStringWithFormat("remote platform %s doesn't support %s", GetPluginName().GetCString(), LLVM_PRETTY_FUNCTION); return error; } } Error Platform::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) { if (IsHost()) return FileSystem::GetFilePermissions(file_spec, file_permissions); else { Error error; error.SetErrorStringWithFormat("remote platform %s doesn't support %s", GetPluginName().GetCString(), LLVM_PRETTY_FUNCTION); return error; } } Error Platform::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions) { if (IsHost()) return FileSystem::SetFilePermissions(file_spec, file_permissions); else { Error error; error.SetErrorStringWithFormat("remote platform %s doesn't support %s", GetPluginName().GetCString(), LLVM_PRETTY_FUNCTION); return error; } } ConstString Platform::GetName() { return GetPluginName(); } const char *Platform::GetHostname() { if (IsHost()) return "127.0.0.1"; if (m_name.empty()) return nullptr; return m_name.c_str(); } ConstString Platform::GetFullNameForDylib(ConstString basename) { return basename; } bool Platform::SetRemoteWorkingDirectory(const FileSpec &working_dir) { Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); if (log) log->Printf("Platform::SetRemoteWorkingDirectory('%s')", working_dir.GetCString()); m_working_dir = working_dir; return true; } const char *Platform::GetUserName(uint32_t uid) { #if !defined(LLDB_DISABLE_POSIX) const char *user_name = GetCachedUserName(uid); if (user_name) return user_name; if (IsHost()) { std::string name; if (HostInfo::LookupUserName(uid, name)) return SetCachedUserName(uid, name.c_str(), name.size()); } #endif return nullptr; } const char *Platform::GetGroupName(uint32_t gid) { #if !defined(LLDB_DISABLE_POSIX) const char *group_name = GetCachedGroupName(gid); if (group_name) return group_name; if (IsHost()) { std::string name; if (HostInfo::LookupGroupName(gid, name)) return SetCachedGroupName(gid, name.c_str(), name.size()); } #endif return nullptr; } bool Platform::SetOSVersion(uint32_t major, uint32_t minor, uint32_t update) { if (IsHost()) { // We don't need anyone setting the OS version for the host platform, // we should be able to figure it out by calling // HostInfo::GetOSVersion(...). return false; } else { // We have a remote platform, allow setting the target OS version if // we aren't connected, since if we are connected, we should be able to // request the remote OS version from the connected platform. if (IsConnected()) return false; else { // We aren't connected and we might want to set the OS version // ahead of time before we connect so we can peruse files and // use a local SDK or PDK cache of support files to disassemble // or do other things. m_major_os_version = major; m_minor_os_version = minor; m_update_os_version = update; return true; } } return false; } Error Platform::ResolveExecutable(const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, const FileSpecList *module_search_paths_ptr) { Error error; if (module_spec.GetFileSpec().Exists()) { if (module_spec.GetArchitecture().IsValid()) { error = ModuleList::GetSharedModule(module_spec, exe_module_sp, module_search_paths_ptr, nullptr, nullptr); } else { // No valid architecture was specified, ask the platform for // the architectures that we should be using (in the correct order) // and see if we can find a match that way ModuleSpec arch_module_spec(module_spec); for (uint32_t idx = 0; GetSupportedArchitectureAtIndex( idx, arch_module_spec.GetArchitecture()); ++idx) { error = ModuleList::GetSharedModule(arch_module_spec, exe_module_sp, module_search_paths_ptr, nullptr, nullptr); // Did we find an executable using one of the if (error.Success() && exe_module_sp) break; } } } else { error.SetErrorStringWithFormat("'%s' does not exist", module_spec.GetFileSpec().GetPath().c_str()); } return error; } Error Platform::ResolveSymbolFile(Target &target, const ModuleSpec &sym_spec, FileSpec &sym_file) { Error error; if (sym_spec.GetSymbolFileSpec().Exists()) sym_file = sym_spec.GetSymbolFileSpec(); else error.SetErrorString("unable to resolve symbol file"); return error; } bool Platform::ResolveRemotePath(const FileSpec &platform_path, FileSpec &resolved_platform_path) { resolved_platform_path = platform_path; return resolved_platform_path.ResolvePath(); } const ArchSpec &Platform::GetSystemArchitecture() { if (IsHost()) { if (!m_system_arch.IsValid()) { // We have a local host platform m_system_arch = HostInfo::GetArchitecture(); m_system_arch_set_while_connected = m_system_arch.IsValid(); } } else { // We have a remote platform. We can only fetch the remote // system architecture if we are connected, and we don't want to do it // more than once. const bool is_connected = IsConnected(); bool fetch = false; if (m_system_arch.IsValid()) { // We have valid OS version info, check to make sure it wasn't // manually set prior to connecting. If it was manually set prior // to connecting, then lets fetch the actual OS version info // if we are now connected. if (is_connected && !m_system_arch_set_while_connected) fetch = true; } else { // We don't have valid OS version info, fetch it if we are connected fetch = is_connected; } if (fetch) { m_system_arch = GetRemoteSystemArchitecture(); m_system_arch_set_while_connected = m_system_arch.IsValid(); } } return m_system_arch; } Error Platform::ConnectRemote(Args &args) { Error error; if (IsHost()) error.SetErrorStringWithFormat("The currently selected platform (%s) is " "the host platform and is always connected.", GetPluginName().GetCString()); else error.SetErrorStringWithFormat( "Platform::ConnectRemote() is not supported by %s", GetPluginName().GetCString()); return error; } Error Platform::DisconnectRemote() { Error error; if (IsHost()) error.SetErrorStringWithFormat("The currently selected platform (%s) is " "the host platform and is always connected.", GetPluginName().GetCString()); else error.SetErrorStringWithFormat( "Platform::DisconnectRemote() is not supported by %s", GetPluginName().GetCString()); return error; } bool Platform::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { // Take care of the host case so that each subclass can just // call this function to get the host functionality. if (IsHost()) return Host::GetProcessInfo(pid, process_info); return false; } uint32_t Platform::FindProcesses(const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { // Take care of the host case so that each subclass can just // call this function to get the host functionality. uint32_t match_count = 0; if (IsHost()) match_count = Host::FindProcesses(match_info, process_infos); return match_count; } Error Platform::LaunchProcess(ProcessLaunchInfo &launch_info) { Error error; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log) log->Printf("Platform::%s()", __FUNCTION__); // Take care of the host case so that each subclass can just // call this function to get the host functionality. if (IsHost()) { if (::getenv("LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY")) launch_info.GetFlags().Set(eLaunchFlagLaunchInTTY); if (launch_info.GetFlags().Test(eLaunchFlagLaunchInShell)) { const bool is_localhost = true; const bool will_debug = launch_info.GetFlags().Test(eLaunchFlagDebug); const bool first_arg_is_full_shell_command = false; uint32_t num_resumes = GetResumeCountForLaunchInfo(launch_info); if (log) { const FileSpec &shell = launch_info.GetShell(); const char *shell_str = (shell) ? shell.GetPath().c_str() : ""; log->Printf( "Platform::%s GetResumeCountForLaunchInfo() returned %" PRIu32 ", shell is '%s'", __FUNCTION__, num_resumes, shell_str); } if (!launch_info.ConvertArgumentsForLaunchingInShell( error, is_localhost, will_debug, first_arg_is_full_shell_command, num_resumes)) return error; } else if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) { error = ShellExpandArguments(launch_info); if (error.Fail()) { error.SetErrorStringWithFormat("shell expansion failed (reason: %s). " "consider launching with 'process " "launch'.", error.AsCString("unknown")); return error; } } if (log) log->Printf("Platform::%s final launch_info resume count: %" PRIu32, __FUNCTION__, launch_info.GetResumeCount()); error = Host::LaunchProcess(launch_info); } else error.SetErrorString( "base lldb_private::Platform class can't launch remote processes"); return error; } Error Platform::ShellExpandArguments(ProcessLaunchInfo &launch_info) { if (IsHost()) return Host::ShellExpandArguments(launch_info); return Error("base lldb_private::Platform class can't expand arguments"); } Error Platform::KillProcess(const lldb::pid_t pid) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log) log->Printf("Platform::%s, pid %" PRIu64, __FUNCTION__, pid); // Try to find a process plugin to handle this Kill request. If we can't, // fall back to // the default OS implementation. size_t num_debuggers = Debugger::GetNumDebuggers(); for (size_t didx = 0; didx < num_debuggers; ++didx) { DebuggerSP debugger = Debugger::GetDebuggerAtIndex(didx); lldb_private::TargetList &targets = debugger->GetTargetList(); for (int tidx = 0; tidx < targets.GetNumTargets(); ++tidx) { ProcessSP process = targets.GetTargetAtIndex(tidx)->GetProcessSP(); if (process->GetID() == pid) return process->Destroy(true); } } if (!IsHost()) { return Error( "base lldb_private::Platform class can't kill remote processes unless " "they are controlled by a process plugin"); } Host::Kill(pid, SIGTERM); return Error(); } lldb::ProcessSP Platform::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, // Can be nullptr, if nullptr create a // new target, else use existing one Error &error) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log) log->Printf("Platform::%s entered (target %p)", __FUNCTION__, static_cast(target)); ProcessSP process_sp; // Make sure we stop at the entry point launch_info.GetFlags().Set(eLaunchFlagDebug); // We always launch the process we are going to debug in a separate process // group, since then we can handle ^C interrupts ourselves w/o having to worry // about the target getting them as well. launch_info.SetLaunchInSeparateProcessGroup(true); // Allow any StructuredData process-bound plugins to adjust the launch info // if needed size_t i = 0; bool iteration_complete = false; // Note iteration can't simply go until a nullptr callback is returned, as // it is valid for a plugin to not supply a filter. auto get_filter_func = PluginManager::GetStructuredDataFilterCallbackAtIndex; for (auto filter_callback = get_filter_func(i, iteration_complete); !iteration_complete; filter_callback = get_filter_func(++i, iteration_complete)) { if (filter_callback) { // Give this ProcessLaunchInfo filter a chance to adjust the launch // info. error = (*filter_callback)(launch_info, target); if (!error.Success()) { if (log) log->Printf("Platform::%s() StructuredDataPlugin launch " "filter failed.", __FUNCTION__); return process_sp; } } } error = LaunchProcess(launch_info); if (error.Success()) { if (log) log->Printf("Platform::%s LaunchProcess() call succeeded (pid=%" PRIu64 ")", __FUNCTION__, launch_info.GetProcessID()); if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { ProcessAttachInfo attach_info(launch_info); process_sp = Attach(attach_info, debugger, target, error); if (process_sp) { if (log) log->Printf("Platform::%s Attach() succeeded, Process plugin: %s", __FUNCTION__, process_sp->GetPluginName().AsCString()); launch_info.SetHijackListener(attach_info.GetHijackListener()); // Since we attached to the process, it will think it needs to detach // if the process object just goes away without an explicit call to // Process::Kill() or Process::Detach(), so let it know to kill the // process if this happens. process_sp->SetShouldDetach(false); // If we didn't have any file actions, the pseudo terminal might // have been used where the slave side was given as the file to // open for stdin/out/err after we have already opened the master // so we can read/write stdin/out/err. int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd) { process_sp->SetSTDIOFileDescriptor(pty_fd); } } else { if (log) log->Printf("Platform::%s Attach() failed: %s", __FUNCTION__, error.AsCString()); } } else { if (log) log->Printf("Platform::%s LaunchProcess() returned launch_info with " "invalid process id", __FUNCTION__); } } else { if (log) log->Printf("Platform::%s LaunchProcess() failed: %s", __FUNCTION__, error.AsCString()); } return process_sp; } lldb::PlatformSP Platform::GetPlatformForArchitecture(const ArchSpec &arch, ArchSpec *platform_arch_ptr) { lldb::PlatformSP platform_sp; Error error; if (arch.IsValid()) platform_sp = Platform::Create(arch, platform_arch_ptr, error); return platform_sp; } //------------------------------------------------------------------ /// Lets a platform answer if it is compatible with a given /// architecture and the target triple contained within. //------------------------------------------------------------------ bool Platform::IsCompatibleArchitecture(const ArchSpec &arch, bool exact_arch_match, ArchSpec *compatible_arch_ptr) { // If the architecture is invalid, we must answer true... if (arch.IsValid()) { ArchSpec platform_arch; // Try for an exact architecture match first. if (exact_arch_match) { for (uint32_t arch_idx = 0; GetSupportedArchitectureAtIndex(arch_idx, platform_arch); ++arch_idx) { if (arch.IsExactMatch(platform_arch)) { if (compatible_arch_ptr) *compatible_arch_ptr = platform_arch; return true; } } } else { for (uint32_t arch_idx = 0; GetSupportedArchitectureAtIndex(arch_idx, platform_arch); ++arch_idx) { if (arch.IsCompatibleMatch(platform_arch)) { if (compatible_arch_ptr) *compatible_arch_ptr = platform_arch; return true; } } } } if (compatible_arch_ptr) compatible_arch_ptr->Clear(); return false; } Error Platform::PutFile(const FileSpec &source, const FileSpec &destination, uint32_t uid, uint32_t gid) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log) log->Printf("[PutFile] Using block by block transfer....\n"); uint32_t source_open_options = File::eOpenOptionRead | File::eOpenOptionCloseOnExec; if (source.GetFileType() == FileSpec::eFileTypeSymbolicLink) source_open_options |= File::eOpenOptionDontFollowSymlinks; File source_file(source, source_open_options, lldb::eFilePermissionsUserRW); Error error; uint32_t permissions = source_file.GetPermissions(error); if (permissions == 0) permissions = lldb::eFilePermissionsFileDefault; if (!source_file.IsValid()) return Error("PutFile: unable to open source file"); lldb::user_id_t dest_file = OpenFile( destination, File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate | File::eOpenOptionCloseOnExec, permissions, error); if (log) log->Printf("dest_file = %" PRIu64 "\n", dest_file); if (error.Fail()) return error; if (dest_file == UINT64_MAX) return Error("unable to open target file"); lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); uint64_t offset = 0; for (;;) { size_t bytes_read = buffer_sp->GetByteSize(); error = source_file.Read(buffer_sp->GetBytes(), bytes_read); if (error.Fail() || bytes_read == 0) break; const uint64_t bytes_written = WriteFile(dest_file, offset, buffer_sp->GetBytes(), bytes_read, error); if (error.Fail()) break; offset += bytes_written; if (bytes_written != bytes_read) { // We didn't write the correct number of bytes, so adjust // the file position in the source file we are reading from... source_file.SeekFromStart(offset); } } CloseFile(dest_file, error); if (uid == UINT32_MAX && gid == UINT32_MAX) return error; // TODO: ChownFile? return error; } Error Platform::GetFile(const FileSpec &source, const FileSpec &destination) { Error error("unimplemented"); return error; } Error Platform::CreateSymlink( const FileSpec &src, // The name of the link is in src const FileSpec &dst) // The symlink points to dst { Error error("unimplemented"); return error; } bool Platform::GetFileExists(const lldb_private::FileSpec &file_spec) { return false; } Error Platform::Unlink(const FileSpec &path) { Error error("unimplemented"); return error; } uint64_t Platform::ConvertMmapFlagsToPlatform(const ArchSpec &arch, unsigned flags) { uint64_t flags_platform = 0; if (flags & eMmapFlagsPrivate) flags_platform |= MAP_PRIVATE; if (flags & eMmapFlagsAnon) flags_platform |= MAP_ANON; return flags_platform; } lldb_private::Error Platform::RunShellCommand( const char *command, // Shouldn't be nullptr const FileSpec & working_dir, // Pass empty FileSpec to use the current working directory int *status_ptr, // Pass nullptr if you don't want the process exit status int *signo_ptr, // Pass nullptr if you don't want the signal that caused the // process to exit std::string *command_output, // Pass nullptr if you don't want the command output uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish { if (IsHost()) return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec); else return Error("unimplemented"); } bool Platform::CalculateMD5(const FileSpec &file_spec, uint64_t &low, uint64_t &high) { if (IsHost()) return FileSystem::CalculateMD5(file_spec, low, high); else return false; } void Platform::SetLocalCacheDirectory(const char *local) { m_local_cache_directory.assign(local); } const char *Platform::GetLocalCacheDirectory() { return m_local_cache_directory.c_str(); } static OptionDefinition g_rsync_option_table[] = { {LLDB_OPT_SET_ALL, false, "rsync", 'r', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Enable rsync."}, {LLDB_OPT_SET_ALL, false, "rsync-opts", 'R', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCommandName, "Platform-specific options required for rsync to work."}, {LLDB_OPT_SET_ALL, false, "rsync-prefix", 'P', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCommandName, "Platform-specific rsync prefix put before the remote path."}, {LLDB_OPT_SET_ALL, false, "ignore-remote-hostname", 'i', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Do not automatically fill in the remote hostname when composing the " "rsync command."}, }; static OptionDefinition g_ssh_option_table[] = { {LLDB_OPT_SET_ALL, false, "ssh", 's', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Enable SSH."}, {LLDB_OPT_SET_ALL, false, "ssh-opts", 'S', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCommandName, "Platform-specific options required for SSH to work."}, }; static OptionDefinition g_caching_option_table[] = { {LLDB_OPT_SET_ALL, false, "local-cache-dir", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePath, "Path in which to store local copies of files."}, }; llvm::ArrayRef OptionGroupPlatformRSync::GetDefinitions() { return llvm::makeArrayRef(g_rsync_option_table); } void OptionGroupPlatformRSync::OptionParsingStarting( ExecutionContext *execution_context) { m_rsync = false; m_rsync_opts.clear(); m_rsync_prefix.clear(); m_ignores_remote_hostname = false; } lldb_private::Error OptionGroupPlatformRSync::SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) { Error error; char short_option = (char)GetDefinitions()[option_idx].short_option; switch (short_option) { case 'r': m_rsync = true; break; case 'R': m_rsync_opts.assign(option_arg); break; case 'P': m_rsync_prefix.assign(option_arg); break; case 'i': m_ignores_remote_hostname = true; break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } lldb::BreakpointSP Platform::SetThreadCreationBreakpoint(lldb_private::Target &target) { return lldb::BreakpointSP(); } llvm::ArrayRef OptionGroupPlatformSSH::GetDefinitions() { return llvm::makeArrayRef(g_ssh_option_table); } void OptionGroupPlatformSSH::OptionParsingStarting( ExecutionContext *execution_context) { m_ssh = false; m_ssh_opts.clear(); } lldb_private::Error OptionGroupPlatformSSH::SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) { Error error; char short_option = (char)GetDefinitions()[option_idx].short_option; switch (short_option) { case 's': m_ssh = true; break; case 'S': m_ssh_opts.assign(option_arg); break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } llvm::ArrayRef OptionGroupPlatformCaching::GetDefinitions() { return llvm::makeArrayRef(g_caching_option_table); } void OptionGroupPlatformCaching::OptionParsingStarting( ExecutionContext *execution_context) { m_cache_dir.clear(); } lldb_private::Error OptionGroupPlatformCaching::SetOptionValue( uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) { Error error; char short_option = (char)GetDefinitions()[option_idx].short_option; switch (short_option) { case 'c': m_cache_dir.assign(option_arg); break; default: error.SetErrorStringWithFormat("unrecognized option '%c'", short_option); break; } return error; } size_t Platform::GetEnvironment(StringList &environment) { environment.Clear(); return false; } const std::vector &Platform::GetTrapHandlerSymbolNames() { if (!m_calculated_trap_handlers) { std::lock_guard guard(m_mutex); if (!m_calculated_trap_handlers) { CalculateTrapHandlerSymbolNames(); m_calculated_trap_handlers = true; } } return m_trap_handlers; } Error Platform::GetCachedExecutable(ModuleSpec &module_spec, lldb::ModuleSP &module_sp, const FileSpecList *module_search_paths_ptr, Platform &remote_platform) { const auto platform_spec = module_spec.GetFileSpec(); const auto error = LoadCachedExecutable( module_spec, module_sp, module_search_paths_ptr, remote_platform); if (error.Success()) { module_spec.GetFileSpec() = module_sp->GetFileSpec(); module_spec.GetPlatformFileSpec() = platform_spec; } return error; } Error Platform::LoadCachedExecutable( const ModuleSpec &module_spec, lldb::ModuleSP &module_sp, const FileSpecList *module_search_paths_ptr, Platform &remote_platform) { return GetRemoteSharedModule(module_spec, nullptr, module_sp, [&](const ModuleSpec &spec) { return remote_platform.ResolveExecutable( spec, module_sp, module_search_paths_ptr); }, nullptr); } Error Platform::GetRemoteSharedModule(const ModuleSpec &module_spec, Process *process, lldb::ModuleSP &module_sp, const ModuleResolver &module_resolver, bool *did_create_ptr) { // Get module information from a target. ModuleSpec resolved_module_spec; bool got_module_spec = false; if (process) { // Try to get module information from the process if (process->GetModuleSpec(module_spec.GetFileSpec(), module_spec.GetArchitecture(), resolved_module_spec)) { if (module_spec.GetUUID().IsValid() == false || module_spec.GetUUID() == resolved_module_spec.GetUUID()) { got_module_spec = true; } } } if (module_spec.GetArchitecture().IsValid() == false) { Error error; // No valid architecture was specified, ask the platform for // the architectures that we should be using (in the correct order) // and see if we can find a match that way ModuleSpec arch_module_spec(module_spec); for (uint32_t idx = 0; GetSupportedArchitectureAtIndex( idx, arch_module_spec.GetArchitecture()); ++idx) { error = ModuleList::GetSharedModule(arch_module_spec, module_sp, nullptr, nullptr, nullptr); // Did we find an executable using one of the if (error.Success() && module_sp) break; } if (module_sp) got_module_spec = true; } if (!got_module_spec) { // Get module information from a target. if (!GetModuleSpec(module_spec.GetFileSpec(), module_spec.GetArchitecture(), resolved_module_spec)) { if (module_spec.GetUUID().IsValid() == false || module_spec.GetUUID() == resolved_module_spec.GetUUID()) { return module_resolver(module_spec); } } } // If we are looking for a specific UUID, make sure resolved_module_spec has // the same one before we search. if (module_spec.GetUUID().IsValid()) { resolved_module_spec.GetUUID() = module_spec.GetUUID(); } // Trying to find a module by UUID on local file system. const auto error = module_resolver(resolved_module_spec); if (error.Fail()) { if (GetCachedSharedModule(resolved_module_spec, module_sp, did_create_ptr)) return Error(); } return error; } bool Platform::GetCachedSharedModule(const ModuleSpec &module_spec, lldb::ModuleSP &module_sp, bool *did_create_ptr) { if (IsHost() || !GetGlobalPlatformProperties()->GetUseModuleCache() || !GetGlobalPlatformProperties()->GetModuleCacheDirectory()) return false; Log *log = GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM); // Check local cache for a module. auto error = m_module_cache->GetAndPut( GetModuleCacheRoot(), GetCacheHostname(), module_spec, [this](const ModuleSpec &module_spec, const FileSpec &tmp_download_file_spec) { return DownloadModuleSlice( module_spec.GetFileSpec(), module_spec.GetObjectOffset(), module_spec.GetObjectSize(), tmp_download_file_spec); }, [this](const ModuleSP &module_sp, const FileSpec &tmp_download_file_spec) { return DownloadSymbolFile(module_sp, tmp_download_file_spec); }, module_sp, did_create_ptr); if (error.Success()) return true; if (log) log->Printf("Platform::%s - module %s not found in local cache: %s", __FUNCTION__, module_spec.GetUUID().GetAsString().c_str(), error.AsCString()); return false; } Error Platform::DownloadModuleSlice(const FileSpec &src_file_spec, const uint64_t src_offset, const uint64_t src_size, const FileSpec &dst_file_spec) { Error error; std::ofstream dst(dst_file_spec.GetPath(), std::ios::out | std::ios::binary); if (!dst.is_open()) { error.SetErrorStringWithFormat("unable to open destination file: %s", dst_file_spec.GetPath().c_str()); return error; } auto src_fd = OpenFile(src_file_spec, File::eOpenOptionRead, lldb::eFilePermissionsFileDefault, error); if (error.Fail()) { error.SetErrorStringWithFormat("unable to open source file: %s", error.AsCString()); return error; } std::vector buffer(1024); auto offset = src_offset; uint64_t total_bytes_read = 0; while (total_bytes_read < src_size) { const auto to_read = std::min(static_cast(buffer.size()), src_size - total_bytes_read); const uint64_t n_read = ReadFile(src_fd, offset, &buffer[0], to_read, error); if (error.Fail()) break; if (n_read == 0) { error.SetErrorString("read 0 bytes"); break; } offset += n_read; total_bytes_read += n_read; dst.write(&buffer[0], n_read); } Error close_error; CloseFile(src_fd, close_error); // Ignoring close error. return error; } Error Platform::DownloadSymbolFile(const lldb::ModuleSP &module_sp, const FileSpec &dst_file_spec) { return Error( "Symbol file downloading not supported by the default platform."); } FileSpec Platform::GetModuleCacheRoot() { auto dir_spec = GetGlobalPlatformProperties()->GetModuleCacheDirectory(); dir_spec.AppendPathComponent(GetName().AsCString()); return dir_spec; } const char *Platform::GetCacheHostname() { return GetHostname(); } const UnixSignalsSP &Platform::GetRemoteUnixSignals() { static const auto s_default_unix_signals_sp = std::make_shared(); return s_default_unix_signals_sp; } const UnixSignalsSP &Platform::GetUnixSignals() { if (IsHost()) return Host::GetUnixSignals(); return GetRemoteUnixSignals(); } uint32_t Platform::LoadImage(lldb_private::Process *process, const lldb_private::FileSpec &local_file, const lldb_private::FileSpec &remote_file, lldb_private::Error &error) { if (local_file && remote_file) { // Both local and remote file was specified. Install the local file to the // given location. if (IsRemote() || local_file != remote_file) { error = Install(local_file, remote_file); if (error.Fail()) return LLDB_INVALID_IMAGE_TOKEN; } return DoLoadImage(process, remote_file, error); } if (local_file) { // Only local file was specified. Install it to the current working // directory. FileSpec target_file = GetWorkingDirectory(); target_file.AppendPathComponent(local_file.GetFilename().AsCString()); if (IsRemote() || local_file != target_file) { error = Install(local_file, target_file); if (error.Fail()) return LLDB_INVALID_IMAGE_TOKEN; } return DoLoadImage(process, target_file, error); } if (remote_file) { // Only remote file was specified so we don't have to do any copying return DoLoadImage(process, remote_file, error); } error.SetErrorString("Neither local nor remote file was specified"); return LLDB_INVALID_IMAGE_TOKEN; } uint32_t Platform::DoLoadImage(lldb_private::Process *process, const lldb_private::FileSpec &remote_file, lldb_private::Error &error) { error.SetErrorString("LoadImage is not supported on the current platform"); return LLDB_INVALID_IMAGE_TOKEN; } Error Platform::UnloadImage(lldb_private::Process *process, uint32_t image_token) { return Error("UnloadImage is not supported on the current platform"); } lldb::ProcessSP Platform::ConnectProcess(llvm::StringRef connect_url, llvm::StringRef plugin_name, lldb_private::Debugger &debugger, lldb_private::Target *target, lldb_private::Error &error) { error.Clear(); if (!target) { TargetSP new_target_sp; error = debugger.GetTargetList().CreateTarget(debugger, "", "", false, nullptr, new_target_sp); target = new_target_sp.get(); } if (!target || error.Fail()) return nullptr; debugger.GetTargetList().SetSelectedTarget(target); lldb::ProcessSP process_sp = target->CreateProcess(debugger.GetListener(), plugin_name, nullptr); if (!process_sp) return nullptr; error = process_sp->ConnectRemote(debugger.GetOutputFile().get(), connect_url); if (error.Fail()) return nullptr; return process_sp; } size_t Platform::ConnectToWaitingProcesses(lldb_private::Debugger &debugger, lldb_private::Error &error) { error.Clear(); return 0; } size_t Platform::GetSoftwareBreakpointTrapOpcode(Target &target, BreakpointSite *bp_site) { ArchSpec arch = target.GetArchitecture(); const uint8_t *trap_opcode = nullptr; size_t trap_opcode_size = 0; switch (arch.GetMachine()) { case llvm::Triple::aarch64: { static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x20, 0xd4}; trap_opcode = g_aarch64_opcode; trap_opcode_size = sizeof(g_aarch64_opcode); } break; // TODO: support big-endian arm and thumb trap codes. case llvm::Triple::arm: { // The ARM reference recommends the use of 0xe7fddefe and 0xdefe // but the linux kernel does otherwise. static const uint8_t g_arm_breakpoint_opcode[] = {0xf0, 0x01, 0xf0, 0xe7}; static const uint8_t g_thumb_breakpoint_opcode[] = {0x01, 0xde}; lldb::BreakpointLocationSP bp_loc_sp(bp_site->GetOwnerAtIndex(0)); AddressClass addr_class = eAddressClassUnknown; if (bp_loc_sp) { addr_class = bp_loc_sp->GetAddress().GetAddressClass(); if (addr_class == eAddressClassUnknown && (bp_loc_sp->GetAddress().GetFileAddress() & 1)) addr_class = eAddressClassCodeAlternateISA; } if (addr_class == eAddressClassCodeAlternateISA) { trap_opcode = g_thumb_breakpoint_opcode; trap_opcode_size = sizeof(g_thumb_breakpoint_opcode); } else { trap_opcode = g_arm_breakpoint_opcode; trap_opcode_size = sizeof(g_arm_breakpoint_opcode); } } break; case llvm::Triple::mips: case llvm::Triple::mips64: { static const uint8_t g_hex_opcode[] = {0x00, 0x00, 0x00, 0x0d}; trap_opcode = g_hex_opcode; trap_opcode_size = sizeof(g_hex_opcode); } break; case llvm::Triple::mipsel: case llvm::Triple::mips64el: { static const uint8_t g_hex_opcode[] = {0x0d, 0x00, 0x00, 0x00}; trap_opcode = g_hex_opcode; trap_opcode_size = sizeof(g_hex_opcode); } break; case llvm::Triple::systemz: { static const uint8_t g_hex_opcode[] = {0x00, 0x01}; trap_opcode = g_hex_opcode; trap_opcode_size = sizeof(g_hex_opcode); } break; case llvm::Triple::hexagon: { static const uint8_t g_hex_opcode[] = {0x0c, 0xdb, 0x00, 0x54}; trap_opcode = g_hex_opcode; trap_opcode_size = sizeof(g_hex_opcode); } break; case llvm::Triple::ppc: case llvm::Triple::ppc64: { static const uint8_t g_ppc_opcode[] = {0x7f, 0xe0, 0x00, 0x08}; trap_opcode = g_ppc_opcode; trap_opcode_size = sizeof(g_ppc_opcode); } break; case llvm::Triple::x86: case llvm::Triple::x86_64: { static const uint8_t g_i386_opcode[] = {0xCC}; trap_opcode = g_i386_opcode; trap_opcode_size = sizeof(g_i386_opcode); } break; default: - assert( - !"Unhandled architecture in Platform::GetSoftwareBreakpointTrapOpcode"); - break; + llvm_unreachable( + "Unhandled architecture in Platform::GetSoftwareBreakpointTrapOpcode"); } assert(bp_site); if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) return trap_opcode_size; return 0; } Index: vendor/lldb/dist/source/Target/StackFrameList.cpp =================================================================== --- vendor/lldb/dist/source/Target/StackFrameList.cpp (revision 311541) +++ vendor/lldb/dist/source/Target/StackFrameList.cpp (revision 311542) @@ -1,853 +1,852 @@ //===-- StackFrameList.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/Target/StackFrameList.h" #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Log.h" #include "lldb/Core/SourceManager.h" #include "lldb/Core/StreamFile.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.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" #include "lldb/Target/Unwind.h" //#define DEBUG_STACK_FRAMES 1 using namespace lldb; using namespace lldb_private; //---------------------------------------------------------------------- // StackFrameList constructor //---------------------------------------------------------------------- StackFrameList::StackFrameList(Thread &thread, const lldb::StackFrameListSP &prev_frames_sp, bool show_inline_frames) : m_thread(thread), m_prev_frames_sp(prev_frames_sp), m_mutex(), m_frames(), m_selected_frame_idx(0), m_concrete_frames_fetched(0), m_current_inlined_depth(UINT32_MAX), m_current_inlined_pc(LLDB_INVALID_ADDRESS), m_show_inlined_frames(show_inline_frames) { if (prev_frames_sp) { m_current_inlined_depth = prev_frames_sp->m_current_inlined_depth; m_current_inlined_pc = prev_frames_sp->m_current_inlined_pc; } } StackFrameList::~StackFrameList() { // Call clear since this takes a lock and clears the stack frame list // in case another thread is currently using this stack frame list Clear(); } void StackFrameList::CalculateCurrentInlinedDepth() { uint32_t cur_inlined_depth = GetCurrentInlinedDepth(); if (cur_inlined_depth == UINT32_MAX) { ResetCurrentInlinedDepth(); } } uint32_t StackFrameList::GetCurrentInlinedDepth() { if (m_show_inlined_frames && m_current_inlined_pc != LLDB_INVALID_ADDRESS) { lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC(); if (cur_pc != m_current_inlined_pc) { m_current_inlined_pc = LLDB_INVALID_ADDRESS; m_current_inlined_depth = UINT32_MAX; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf( "GetCurrentInlinedDepth: invalidating current inlined depth.\n"); } return m_current_inlined_depth; } else { return UINT32_MAX; } } void StackFrameList::ResetCurrentInlinedDepth() { std::lock_guard guard(m_mutex); if (m_show_inlined_frames) { GetFramesUpTo(0); if (m_frames.empty()) return; if (!m_frames[0]->IsInlined()) { m_current_inlined_depth = UINT32_MAX; m_current_inlined_pc = LLDB_INVALID_ADDRESS; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf( "ResetCurrentInlinedDepth: Invalidating current inlined depth.\n"); } else { // We only need to do something special about inlined blocks when we // are at the beginning of an inlined function: // FIXME: We probably also have to do something special if the PC is at // the END // of an inlined function, which coincides with the end of either its // containing // function or another inlined function. lldb::addr_t curr_pc = m_thread.GetRegisterContext()->GetPC(); Block *block_ptr = m_frames[0]->GetFrameBlock(); if (block_ptr) { Address pc_as_address; pc_as_address.SetLoadAddress(curr_pc, &(m_thread.GetProcess()->GetTarget())); AddressRange containing_range; if (block_ptr->GetRangeContainingAddress(pc_as_address, containing_range)) { if (pc_as_address == containing_range.GetBaseAddress()) { // If we got here because of a breakpoint hit, then set the inlined // depth depending on where // the breakpoint was set. // If we got here because of a crash, then set the inlined depth to // the deepest most block. // Otherwise, we stopped here naturally as the result of a step, so // set ourselves in the // containing frame of the whole set of nested inlines, so the user // can then "virtually" // step into the frames one by one, or next over the whole mess. // Note: We don't have to handle being somewhere in the middle of // the stack here, since // ResetCurrentInlinedDepth doesn't get called if there is a valid // inlined depth set. StopInfoSP stop_info_sp = m_thread.GetStopInfo(); if (stop_info_sp) { switch (stop_info_sp->GetStopReason()) { case eStopReasonWatchpoint: case eStopReasonException: case eStopReasonExec: case eStopReasonSignal: // In all these cases we want to stop in the deepest most frame. m_current_inlined_pc = curr_pc; m_current_inlined_depth = 0; break; case eStopReasonBreakpoint: { // FIXME: Figure out what this break point is doing, and set the // inline depth // appropriately. Be careful to take into account breakpoints // that implement // step over prologue, since that should do the default // calculation. // For now, if the breakpoints corresponding to this hit are all // internal, // I set the stop location to the top of the inlined stack, // since that will make // things like stepping over prologues work right. But if there // are any non-internal // breakpoints I do to the bottom of the stack, since that was // the old behavior. uint32_t bp_site_id = stop_info_sp->GetValue(); BreakpointSiteSP bp_site_sp( m_thread.GetProcess()->GetBreakpointSiteList().FindByID( bp_site_id)); bool all_internal = true; if (bp_site_sp) { uint32_t num_owners = bp_site_sp->GetNumberOfOwners(); for (uint32_t i = 0; i < num_owners; i++) { Breakpoint &bp_ref = bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint(); if (!bp_ref.IsInternal()) { all_internal = false; } } } if (!all_internal) { m_current_inlined_pc = curr_pc; m_current_inlined_depth = 0; break; } } LLVM_FALLTHROUGH; default: { // Otherwise, we should set ourselves at the container of the // inlining, so that the // user can descend into them. // So first we check whether we have more than one inlined block // sharing this PC: int num_inlined_functions = 0; for (Block *container_ptr = block_ptr->GetInlinedParent(); container_ptr != nullptr; container_ptr = container_ptr->GetInlinedParent()) { if (!container_ptr->GetRangeContainingAddress( pc_as_address, containing_range)) break; if (pc_as_address != containing_range.GetBaseAddress()) break; num_inlined_functions++; } m_current_inlined_pc = curr_pc; m_current_inlined_depth = num_inlined_functions + 1; Log *log( lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf("ResetCurrentInlinedDepth: setting inlined " "depth: %d 0x%" PRIx64 ".\n", m_current_inlined_depth, curr_pc); } break; } } } } } } } } bool StackFrameList::DecrementCurrentInlinedDepth() { if (m_show_inlined_frames) { uint32_t current_inlined_depth = GetCurrentInlinedDepth(); if (current_inlined_depth != UINT32_MAX) { if (current_inlined_depth > 0) { m_current_inlined_depth--; return true; } } } return false; } void StackFrameList::SetCurrentInlinedDepth(uint32_t new_depth) { m_current_inlined_depth = new_depth; if (new_depth == UINT32_MAX) m_current_inlined_pc = LLDB_INVALID_ADDRESS; else m_current_inlined_pc = m_thread.GetRegisterContext()->GetPC(); } void StackFrameList::GetFramesUpTo(uint32_t end_idx) { // this makes sure we do not fetch frames for an invalid thread if (!m_thread.IsValid()) return; // We've already gotten more frames than asked for, or we've already finished // unwinding, return. if (m_frames.size() > end_idx || GetAllFramesFetched()) return; Unwind *unwinder = m_thread.GetUnwinder(); if (m_show_inlined_frames) { #if defined(DEBUG_STACK_FRAMES) StreamFile s(stdout, false); #endif // If we are hiding some frames from the outside world, we need to add those // onto the total count of // frames to fetch. However, we don't need to do that if end_idx is 0 since // in that case we always // get the first concrete frame and all the inlined frames below it... And // of course, if end_idx is // UINT32_MAX that means get all, so just do that... uint32_t inlined_depth = 0; if (end_idx > 0 && end_idx != UINT32_MAX) { inlined_depth = GetCurrentInlinedDepth(); if (inlined_depth != UINT32_MAX) { if (end_idx > 0) end_idx += inlined_depth; } } StackFrameSP unwind_frame_sp; do { uint32_t idx = m_concrete_frames_fetched++; lldb::addr_t pc = LLDB_INVALID_ADDRESS; lldb::addr_t cfa = LLDB_INVALID_ADDRESS; if (idx == 0) { // We might have already created frame zero, only create it // if we need to if (m_frames.empty()) { RegisterContextSP reg_ctx_sp(m_thread.GetRegisterContext()); if (reg_ctx_sp) { const bool success = unwinder && unwinder->GetFrameInfoAtIndex(idx, cfa, pc); // There shouldn't be any way not to get the frame info for frame 0. // But if the unwinder can't make one, lets make one by hand with // the // SP as the CFA and see if that gets any further. if (!success) { cfa = reg_ctx_sp->GetSP(); pc = reg_ctx_sp->GetPC(); } unwind_frame_sp.reset(new StackFrame(m_thread.shared_from_this(), m_frames.size(), idx, reg_ctx_sp, cfa, pc, nullptr)); m_frames.push_back(unwind_frame_sp); } } else { unwind_frame_sp = m_frames.front(); cfa = unwind_frame_sp->m_id.GetCallFrameAddress(); } } else { const bool success = unwinder && unwinder->GetFrameInfoAtIndex(idx, cfa, pc); if (!success) { // We've gotten to the end of the stack. SetAllFramesFetched(); break; } const bool cfa_is_valid = true; const bool stop_id_is_valid = false; const bool is_history_frame = false; unwind_frame_sp.reset(new StackFrame( m_thread.shared_from_this(), m_frames.size(), idx, cfa, cfa_is_valid, pc, 0, stop_id_is_valid, is_history_frame, nullptr)); m_frames.push_back(unwind_frame_sp); } assert(unwind_frame_sp); SymbolContext unwind_sc = unwind_frame_sp->GetSymbolContext( eSymbolContextBlock | eSymbolContextFunction); Block *unwind_block = unwind_sc.block; if (unwind_block) { Address curr_frame_address(unwind_frame_sp->GetFrameCodeAddress()); TargetSP target_sp = m_thread.CalculateTarget(); // Be sure to adjust the frame address to match the address // that was used to lookup the symbol context above. If we are // in the first concrete frame, then we lookup using the current // address, else we decrement the address by one to get the correct // location. if (idx > 0) { if (curr_frame_address.GetOffset() == 0) { // If curr_frame_address points to the first address in a section // then after // adjustment it will point to an other section. In that case // resolve the // address again to the correct section plus offset form. addr_t load_addr = curr_frame_address.GetOpcodeLoadAddress( target_sp.get(), eAddressClassCode); curr_frame_address.SetOpcodeLoadAddress( load_addr - 1, target_sp.get(), eAddressClassCode); } else { curr_frame_address.Slide(-1); } } SymbolContext next_frame_sc; Address next_frame_address; while (unwind_sc.GetParentOfInlinedScope( curr_frame_address, next_frame_sc, next_frame_address)) { next_frame_sc.line_entry.ApplyFileMappings(target_sp); StackFrameSP frame_sp( new StackFrame(m_thread.shared_from_this(), m_frames.size(), idx, unwind_frame_sp->GetRegisterContextSP(), cfa, next_frame_address, &next_frame_sc)); m_frames.push_back(frame_sp); unwind_sc = next_frame_sc; curr_frame_address = next_frame_address; } } } while (m_frames.size() - 1 < end_idx); // Don't try to merge till you've calculated all the frames in this stack. if (GetAllFramesFetched() && m_prev_frames_sp) { StackFrameList *prev_frames = m_prev_frames_sp.get(); StackFrameList *curr_frames = this; // curr_frames->m_current_inlined_depth = prev_frames->m_current_inlined_depth; // curr_frames->m_current_inlined_pc = prev_frames->m_current_inlined_pc; // printf ("GetFramesUpTo: Copying current inlined depth: %d 0x%" PRIx64 ".\n", // curr_frames->m_current_inlined_depth, curr_frames->m_current_inlined_pc); #if defined(DEBUG_STACK_FRAMES) s.PutCString("\nprev_frames:\n"); prev_frames->Dump(&s); s.PutCString("\ncurr_frames:\n"); curr_frames->Dump(&s); s.EOL(); #endif size_t curr_frame_num, prev_frame_num; for (curr_frame_num = curr_frames->m_frames.size(), prev_frame_num = prev_frames->m_frames.size(); curr_frame_num > 0 && prev_frame_num > 0; --curr_frame_num, --prev_frame_num) { const size_t curr_frame_idx = curr_frame_num - 1; const size_t prev_frame_idx = prev_frame_num - 1; StackFrameSP curr_frame_sp(curr_frames->m_frames[curr_frame_idx]); StackFrameSP prev_frame_sp(prev_frames->m_frames[prev_frame_idx]); #if defined(DEBUG_STACK_FRAMES) s.Printf("\n\nCurr frame #%u ", curr_frame_idx); if (curr_frame_sp) curr_frame_sp->Dump(&s, true, false); else s.PutCString("NULL"); s.Printf("\nPrev frame #%u ", prev_frame_idx); if (prev_frame_sp) prev_frame_sp->Dump(&s, true, false); else s.PutCString("NULL"); #endif StackFrame *curr_frame = curr_frame_sp.get(); StackFrame *prev_frame = prev_frame_sp.get(); if (curr_frame == nullptr || prev_frame == nullptr) break; // Check the stack ID to make sure they are equal if (curr_frame->GetStackID() != prev_frame->GetStackID()) break; prev_frame->UpdatePreviousFrameFromCurrentFrame(*curr_frame); // Now copy the fixed up previous frame into the current frames // so the pointer doesn't change m_frames[curr_frame_idx] = prev_frame_sp; // curr_frame->UpdateCurrentFrameFromPreviousFrame (*prev_frame); #if defined(DEBUG_STACK_FRAMES) s.Printf("\n Copying previous frame to current frame"); #endif } // We are done with the old stack frame list, we can release it now m_prev_frames_sp.reset(); } #if defined(DEBUG_STACK_FRAMES) s.PutCString("\n\nNew frames:\n"); Dump(&s); s.EOL(); #endif } else { if (end_idx < m_concrete_frames_fetched) return; if (unwinder) { uint32_t num_frames = unwinder->GetFramesUpTo(end_idx); if (num_frames <= end_idx + 1) { // Done unwinding. m_concrete_frames_fetched = UINT32_MAX; } m_frames.resize(num_frames); } } } uint32_t StackFrameList::GetNumFrames(bool can_create) { std::lock_guard guard(m_mutex); if (can_create) GetFramesUpTo(UINT32_MAX); uint32_t inlined_depth = GetCurrentInlinedDepth(); if (inlined_depth == UINT32_MAX) return m_frames.size(); else return m_frames.size() - inlined_depth; } void StackFrameList::Dump(Stream *s) { if (s == nullptr) return; std::lock_guard guard(m_mutex); const_iterator pos, begin = m_frames.begin(), end = m_frames.end(); for (pos = begin; pos != end; ++pos) { StackFrame *frame = (*pos).get(); s->Printf("%p: ", static_cast(frame)); if (frame) { frame->GetStackID().Dump(s); frame->DumpUsingSettingsFormat(s); } else s->Printf("frame #%u", (uint32_t)std::distance(begin, pos)); s->EOL(); } s->EOL(); } StackFrameSP StackFrameList::GetFrameAtIndex(uint32_t idx) { StackFrameSP frame_sp; std::lock_guard guard(m_mutex); uint32_t original_idx = idx; uint32_t inlined_depth = GetCurrentInlinedDepth(); if (inlined_depth != UINT32_MAX) idx += inlined_depth; if (idx < m_frames.size()) frame_sp = m_frames[idx]; if (frame_sp) return frame_sp; // GetFramesUpTo will fill m_frames with as many frames as you asked for, // if there are that many. If there weren't then you asked for too many // frames. GetFramesUpTo(idx); if (idx < m_frames.size()) { if (m_show_inlined_frames) { // When inline frames are enabled we actually create all the frames in // GetFramesUpTo. frame_sp = m_frames[idx]; } else { Unwind *unwinder = m_thread.GetUnwinder(); if (unwinder) { addr_t pc, cfa; if (unwinder->GetFrameInfoAtIndex(idx, cfa, pc)) { const bool cfa_is_valid = true; const bool stop_id_is_valid = false; const bool is_history_frame = false; frame_sp.reset(new StackFrame( m_thread.shared_from_this(), idx, idx, cfa, cfa_is_valid, pc, 0, stop_id_is_valid, is_history_frame, nullptr)); Function *function = frame_sp->GetSymbolContext(eSymbolContextFunction).function; if (function) { // When we aren't showing inline functions we always use // the top most function block as the scope. frame_sp->SetSymbolContextScope(&function->GetBlock(false)); } else { // Set the symbol scope from the symbol regardless if it is nullptr // or valid. frame_sp->SetSymbolContextScope( frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol); } SetFrameAtIndex(idx, frame_sp); } } } } else if (original_idx == 0) { // There should ALWAYS be a frame at index 0. If something went wrong with // the CurrentInlinedDepth such that // there weren't as many frames as we thought taking that into account, then // reset the current inlined depth // and return the real zeroth frame. if (m_frames.empty()) { // Why do we have a thread with zero frames, that should not ever // happen... - if (m_thread.IsValid()) - assert("A valid thread has no frames."); + assert(!m_thread.IsValid() && "A valid thread has no frames."); } else { ResetCurrentInlinedDepth(); frame_sp = m_frames[original_idx]; } } return frame_sp; } StackFrameSP StackFrameList::GetFrameWithConcreteFrameIndex(uint32_t unwind_idx) { // First try assuming the unwind index is the same as the frame index. The // unwind index is always greater than or equal to the frame index, so it // is a good place to start. If we have inlined frames we might have 5 // concrete frames (frame unwind indexes go from 0-4), but we might have 15 // frames after we make all the inlined frames. Most of the time the unwind // frame index (or the concrete frame index) is the same as the frame index. uint32_t frame_idx = unwind_idx; StackFrameSP frame_sp(GetFrameAtIndex(frame_idx)); while (frame_sp) { if (frame_sp->GetFrameIndex() == unwind_idx) break; frame_sp = GetFrameAtIndex(++frame_idx); } return frame_sp; } static bool CompareStackID(const StackFrameSP &stack_sp, const StackID &stack_id) { return stack_sp->GetStackID() < stack_id; } StackFrameSP StackFrameList::GetFrameWithStackID(const StackID &stack_id) { StackFrameSP frame_sp; if (stack_id.IsValid()) { std::lock_guard guard(m_mutex); uint32_t frame_idx = 0; // Do a binary search in case the stack frame is already in our cache collection::const_iterator begin = m_frames.begin(); collection::const_iterator end = m_frames.end(); if (begin != end) { collection::const_iterator pos = std::lower_bound(begin, end, stack_id, CompareStackID); if (pos != end) { if ((*pos)->GetStackID() == stack_id) return *pos; } // if (m_frames.back()->GetStackID() < stack_id) // frame_idx = m_frames.size(); } do { frame_sp = GetFrameAtIndex(frame_idx); if (frame_sp && frame_sp->GetStackID() == stack_id) break; frame_idx++; } while (frame_sp); } return frame_sp; } bool StackFrameList::SetFrameAtIndex(uint32_t idx, StackFrameSP &frame_sp) { if (idx >= m_frames.size()) m_frames.resize(idx + 1); // Make sure allocation succeeded by checking bounds again if (idx < m_frames.size()) { m_frames[idx] = frame_sp; return true; } return false; // resize failed, out of memory? } uint32_t StackFrameList::GetSelectedFrameIndex() const { std::lock_guard guard(m_mutex); return m_selected_frame_idx; } uint32_t StackFrameList::SetSelectedFrame(lldb_private::StackFrame *frame) { std::lock_guard guard(m_mutex); const_iterator pos; const_iterator begin = m_frames.begin(); const_iterator end = m_frames.end(); m_selected_frame_idx = 0; for (pos = begin; pos != end; ++pos) { if (pos->get() == frame) { m_selected_frame_idx = std::distance(begin, pos); uint32_t inlined_depth = GetCurrentInlinedDepth(); if (inlined_depth != UINT32_MAX) m_selected_frame_idx -= inlined_depth; break; } } SetDefaultFileAndLineToSelectedFrame(); return m_selected_frame_idx; } // Mark a stack frame as the current frame using the frame index bool StackFrameList::SetSelectedFrameByIndex(uint32_t idx) { std::lock_guard guard(m_mutex); StackFrameSP frame_sp(GetFrameAtIndex(idx)); if (frame_sp) { SetSelectedFrame(frame_sp.get()); return true; } else return false; } void StackFrameList::SetDefaultFileAndLineToSelectedFrame() { if (m_thread.GetID() == m_thread.GetProcess()->GetThreadList().GetSelectedThread()->GetID()) { StackFrameSP frame_sp(GetFrameAtIndex(GetSelectedFrameIndex())); if (frame_sp) { SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextLineEntry); if (sc.line_entry.file) m_thread.CalculateTarget()->GetSourceManager().SetDefaultFileAndLine( sc.line_entry.file, sc.line_entry.line); } } } // The thread has been run, reset the number stack frames to zero so we can // determine how many frames we have lazily. void StackFrameList::Clear() { std::lock_guard guard(m_mutex); m_frames.clear(); m_concrete_frames_fetched = 0; } void StackFrameList::InvalidateFrames(uint32_t start_idx) { std::lock_guard guard(m_mutex); if (m_show_inlined_frames) { Clear(); } else { const size_t num_frames = m_frames.size(); while (start_idx < num_frames) { m_frames[start_idx].reset(); ++start_idx; } } } void StackFrameList::Merge(std::unique_ptr &curr_ap, lldb::StackFrameListSP &prev_sp) { std::unique_lock current_lock, previous_lock; if (curr_ap) current_lock = std::unique_lock(curr_ap->m_mutex); if (prev_sp) previous_lock = std::unique_lock(prev_sp->m_mutex); #if defined(DEBUG_STACK_FRAMES) StreamFile s(stdout, false); s.PutCString("\n\nStackFrameList::Merge():\nPrev:\n"); if (prev_sp) prev_sp->Dump(&s); else s.PutCString("NULL"); s.PutCString("\nCurr:\n"); if (curr_ap) curr_ap->Dump(&s); else s.PutCString("NULL"); s.EOL(); #endif if (!curr_ap || curr_ap->GetNumFrames(false) == 0) { #if defined(DEBUG_STACK_FRAMES) s.PutCString("No current frames, leave previous frames alone...\n"); #endif curr_ap.release(); return; } if (!prev_sp || prev_sp->GetNumFrames(false) == 0) { #if defined(DEBUG_STACK_FRAMES) s.PutCString("No previous frames, so use current frames...\n"); #endif // We either don't have any previous frames, or since we have more than // one current frames it means we have all the frames and can safely // replace our previous frames. prev_sp.reset(curr_ap.release()); return; } const uint32_t num_curr_frames = curr_ap->GetNumFrames(false); if (num_curr_frames > 1) { #if defined(DEBUG_STACK_FRAMES) s.PutCString( "We have more than one current frame, so use current frames...\n"); #endif // We have more than one current frames it means we have all the frames // and can safely replace our previous frames. prev_sp.reset(curr_ap.release()); #if defined(DEBUG_STACK_FRAMES) s.PutCString("\nMerged:\n"); prev_sp->Dump(&s); #endif return; } StackFrameSP prev_frame_zero_sp(prev_sp->GetFrameAtIndex(0)); StackFrameSP curr_frame_zero_sp(curr_ap->GetFrameAtIndex(0)); StackID curr_stack_id(curr_frame_zero_sp->GetStackID()); StackID prev_stack_id(prev_frame_zero_sp->GetStackID()); #if defined(DEBUG_STACK_FRAMES) const uint32_t num_prev_frames = prev_sp->GetNumFrames(false); s.Printf("\n%u previous frames with one current frame\n", num_prev_frames); #endif // We have only a single current frame // Our previous stack frames only had a single frame as well... if (curr_stack_id == prev_stack_id) { #if defined(DEBUG_STACK_FRAMES) s.Printf("\nPrevious frame #0 is same as current frame #0, merge the " "cached data\n"); #endif curr_frame_zero_sp->UpdateCurrentFrameFromPreviousFrame( *prev_frame_zero_sp); // prev_frame_zero_sp->UpdatePreviousFrameFromCurrentFrame // (*curr_frame_zero_sp); // prev_sp->SetFrameAtIndex (0, prev_frame_zero_sp); } else if (curr_stack_id < prev_stack_id) { #if defined(DEBUG_STACK_FRAMES) s.Printf("\nCurrent frame #0 has a stack ID that is less than the previous " "frame #0, insert current frame zero in front of previous\n"); #endif prev_sp->m_frames.insert(prev_sp->m_frames.begin(), curr_frame_zero_sp); } curr_ap.release(); #if defined(DEBUG_STACK_FRAMES) s.PutCString("\nMerged:\n"); prev_sp->Dump(&s); #endif } lldb::StackFrameSP StackFrameList::GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr) { const_iterator pos; const_iterator begin = m_frames.begin(); const_iterator end = m_frames.end(); lldb::StackFrameSP ret_sp; for (pos = begin; pos != end; ++pos) { if (pos->get() == stack_frame_ptr) { ret_sp = (*pos); break; } } return ret_sp; } size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame, uint32_t num_frames, bool show_frame_info, uint32_t num_frames_with_source, const char *selected_frame_marker) { size_t num_frames_displayed = 0; if (num_frames == 0) return 0; StackFrameSP frame_sp; uint32_t frame_idx = 0; uint32_t last_frame; // Don't let the last frame wrap around... if (num_frames == UINT32_MAX) last_frame = UINT32_MAX; else last_frame = first_frame + num_frames; StackFrameSP selected_frame_sp = m_thread.GetSelectedFrame(); const char *unselected_marker = nullptr; std::string buffer; if (selected_frame_marker) { size_t len = strlen(selected_frame_marker); buffer.insert(buffer.begin(), len, ' '); unselected_marker = buffer.c_str(); } const char *marker = nullptr; for (frame_idx = first_frame; frame_idx < last_frame; ++frame_idx) { frame_sp = GetFrameAtIndex(frame_idx); if (!frame_sp) break; if (selected_frame_marker != nullptr) { if (frame_sp == selected_frame_sp) marker = selected_frame_marker; else marker = unselected_marker; } if (!frame_sp->GetStatus(strm, show_frame_info, num_frames_with_source > (first_frame - frame_idx), marker)) break; ++num_frames_displayed; } strm.IndentLess(); return num_frames_displayed; } Index: vendor/lldb/dist/tools/driver/Platform.cpp =================================================================== --- vendor/lldb/dist/tools/driver/Platform.cpp (revision 311541) +++ vendor/lldb/dist/tools/driver/Platform.cpp (revision 311542) @@ -1,62 +1,60 @@ //===-- Platform.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // this file is only relevant for Visual C++ #if defined(_WIN32) #include #include #include #include "Platform.h" +#include "llvm/Support/ErrorHandling.h" int ioctl(int d, int request, ...) { switch (request) { // request the console windows size case (TIOCGWINSZ): { va_list vl; va_start(vl, request); // locate the window size structure on stack winsize *ws = va_arg(vl, winsize *); // get screen buffer information CONSOLE_SCREEN_BUFFER_INFO info; if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info) == TRUE) // fill in the columns ws->ws_col = info.dwMaximumWindowSize.X; va_end(vl); return 0; } break; default: - assert(!"Not implemented!"); + llvm_unreachable("Not implemented!"); } - return -1; } int kill(pid_t pid, int sig) { // is the app trying to kill itself if (pid == getpid()) exit(sig); // - assert(!"Not implemented!"); - return -1; + llvm_unreachable("Not implemented!"); } int tcsetattr(int fd, int optional_actions, const struct termios *termios_p) { - assert(!"Not implemented!"); - return -1; + llvm_unreachable("Not implemented!"); } int tcgetattr(int fildes, struct termios *termios_p) { // assert( !"Not implemented!" ); // error return value (0=success) return -1; } #endif Index: vendor/lldb/dist/tools/lldb-mi/MICmdCmdEnviro.cpp =================================================================== --- vendor/lldb/dist/tools/lldb-mi/MICmdCmdEnviro.cpp (revision 311541) +++ vendor/lldb/dist/tools/lldb-mi/MICmdCmdEnviro.cpp (revision 311542) @@ -1,145 +1,152 @@ //===-- MICmdCmdEnviro.cpp --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Overview: CMICmdCmdEnvironmentCd implementation. // In-house headers: #include "MICmdCmdEnviro.h" #include "MICmdArgValFile.h" #include "MICmnLLDBDebugSessionInfo.h" #include "MICmnLLDBDebugger.h" #include "MICmnMIResultRecord.h" #include "MICmnMIValueConst.h" //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdEnvironmentCd constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdEnvironmentCd::CMICmdCmdEnvironmentCd() : m_constStrArgNamePathDir("pathdir") { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "environment-cd"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdEnvironmentCd::CreateSelf; } //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdEnvironmentCd destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdEnvironmentCd::~CMICmdCmdEnvironmentCd() {} //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line // options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdEnvironmentCd::ParseArgs() { m_setCmdArgs.Add(new CMICmdArgValFile(m_constStrArgNamePathDir, true, true)); CMICmdArgContext argCntxt(m_cmdData.strMiCmdOption); return ParseValidateCmdOptions(); } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this // function. // The command is likely to communicate with the LLDB SBDebugger in // here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdEnvironmentCd::Execute() { CMICMDBASE_GETOPTION(pArgPathDir, File, m_constStrArgNamePathDir); const CMIUtilString &strWkDir(pArgPathDir->GetValue()); CMICmnLLDBDebugger &rDbg(CMICmnLLDBDebugger::Instance()); lldb::SBDebugger &rLldbDbg = rDbg.GetTheDebugger(); bool bOk = rLldbDbg.SetCurrentPlatformSDKRoot(strWkDir.c_str()); if (bOk) { const CMIUtilString &rStrKeyWkDir( m_rLLDBDebugSessionInfo.m_constStrSharedDataKeyWkDir); if (!m_rLLDBDebugSessionInfo.SharedDataAdd(rStrKeyWkDir, strWkDir)) { SetError(CMIUtilString::Format(MIRSRC(IDS_DBGSESSION_ERR_SHARED_DATA_ADD), m_cmdData.strMiCmd.c_str(), rStrKeyWkDir.c_str())); bOk = MIstatus::failure; } } else SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_FNFAILED), m_cmdData.strMiCmd.c_str(), "SetCurrentPlatformSDKRoot()")); + lldb::SBTarget sbTarget = m_rLLDBDebugSessionInfo.GetTarget(); + if (sbTarget.IsValid()) { + lldb::SBLaunchInfo sbLaunchInfo = sbTarget.GetLaunchInfo(); + sbLaunchInfo.SetWorkingDirectory(strWkDir.c_str()); + sbTarget.SetLaunchInfo(sbLaunchInfo); + } + return bOk; } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record // Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdEnvironmentCd::Acknowledge() { const CMIUtilString &rStrKeyWkDir( m_rLLDBDebugSessionInfo.m_constStrSharedDataKeyWkDir); CMIUtilString strWkDir; const bool bOk = m_rLLDBDebugSessionInfo.SharedDataRetrieve( rStrKeyWkDir, strWkDir); if (bOk) { const CMICmnMIValueConst miValueConst(strWkDir); const CMICmnMIValueResult miValueResult("path", miValueConst); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_SHARED_DATA_NOT_FOUND), m_cmdData.strMiCmd.c_str(), rStrKeyWkDir.c_str())); return MIstatus::failure; } //++ //------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The // factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase *CMICmdCmdEnvironmentCd::CreateSelf() { return new CMICmdCmdEnvironmentCd(); } Index: vendor/lldb/dist/tools/lldb-mi/MICmdCmdGdbSet.cpp =================================================================== --- vendor/lldb/dist/tools/lldb-mi/MICmdCmdGdbSet.cpp (revision 311541) +++ vendor/lldb/dist/tools/lldb-mi/MICmdCmdGdbSet.cpp (revision 311542) @@ -1,421 +1,455 @@ //===-- MICmdCmdGdbSet.cpp --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Overview: CMICmdCmdGdbSet implementation. // In-house headers: #include "MICmdCmdGdbSet.h" #include "MICmdArgValListOfN.h" #include "MICmdArgValOptionLong.h" #include "MICmdArgValString.h" #include "MICmnLLDBDebugSessionInfo.h" #include "MICmnMIResultRecord.h" #include "MICmnMIValueConst.h" // Instantiations: const CMICmdCmdGdbSet::MapGdbOptionNameToFnGdbOptionPtr_t CMICmdCmdGdbSet::ms_mapGdbOptionNameToFnGdbOptionPtr = { {"target-async", &CMICmdCmdGdbSet::OptionFnTargetAsync}, {"print", &CMICmdCmdGdbSet::OptionFnPrint}, // { "auto-solib-add", &CMICmdCmdGdbSet::OptionFnAutoSolibAdd }, // // Example code if need to implement GDB set other options {"output-radix", &CMICmdCmdGdbSet::OptionFnOutputRadix}, {"solib-search-path", &CMICmdCmdGdbSet::OptionFnSolibSearchPath}, + {"disassembly-flavor", &CMICmdCmdGdbSet::OptionFnDisassemblyFlavor}, {"fallback", &CMICmdCmdGdbSet::OptionFnFallback}}; //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdGdbSet constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdGdbSet::CMICmdCmdGdbSet() : m_constStrArgNamedGdbOption("option"), m_bGdbOptionRecognised(true), m_bGdbOptionFnSuccessful(false), m_bGbbOptionFnHasError(false), m_strGdbOptionFnError(MIRSRC(IDS_WORD_ERR_MSG_NOT_IMPLEMENTED_BRKTS)) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "gdb-set"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdGdbSet::CreateSelf; } //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdGdbSet destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdGdbSet::~CMICmdCmdGdbSet() {} //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line // options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdGdbSet::ParseArgs() { m_setCmdArgs.Add(new CMICmdArgValListOfN( m_constStrArgNamedGdbOption, true, true, CMICmdArgValListBase::eArgValType_StringAnything)); return ParseValidateCmdOptions(); } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command is executed in this // function. // The command is likely to communicate with the LLDB SBDebugger in // here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdGdbSet::Execute() { CMICMDBASE_GETOPTION(pArgGdbOption, ListOfN, m_constStrArgNamedGdbOption); const CMICmdArgValListBase::VecArgObjPtr_t &rVecWords( pArgGdbOption->GetExpectedOptions()); // Get the gdb-set option to carry out. This option will be used as an action // which should be done. Further arguments will be used as parameters for it. CMICmdArgValListBase::VecArgObjPtr_t::const_iterator it = rVecWords.begin(); const CMICmdArgValString *pOption = static_cast(*it); const CMIUtilString strOption(pOption->GetValue()); ++it; // Retrieve the parameter(s) for the option CMIUtilString::VecString_t vecWords; while (it != rVecWords.end()) { const CMICmdArgValString *pWord = static_cast(*it); vecWords.push_back(pWord->GetValue()); // Next ++it; } FnGdbOptionPtr pPrintRequestFn = nullptr; if (!GetOptionFn(strOption, pPrintRequestFn)) { // For unimplemented option handlers, fallback on a generic handler // ToDo: Remove this when ALL options have been implemented if (!GetOptionFn("fallback", pPrintRequestFn)) { m_bGdbOptionRecognised = false; m_strGdbOptionName = "fallback"; // This would be the strOption name return MIstatus::success; } } m_bGdbOptionFnSuccessful = (this->*(pPrintRequestFn))(vecWords); if (!m_bGdbOptionFnSuccessful && !m_bGbbOptionFnHasError) return MIstatus::failure; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record // Result // for the work carried out in the Execute() method. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdGdbSet::Acknowledge() { // Print error if option isn't recognized: // ^error,msg="The request '%s' was not recognized, not implemented" if (!m_bGdbOptionRecognised) { const CMICmnMIValueConst miValueConst( CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INFO_PRINTFN_NOT_FOUND), m_strGdbOptionName.c_str())); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } // ^done,value="%s" if (m_bGdbOptionFnSuccessful) { const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); m_miResultRecord = miRecordResult; return MIstatus::success; } // Print error if request failed: // ^error,msg="The request '%s' failed. const CMICmnMIValueConst miValueConst(CMIUtilString::Format( MIRSRC(IDS_CMD_ERR_INFO_PRINTFN_FAILED), m_strGdbOptionFnError.c_str())); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The // factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase *CMICmdCmdGdbSet::CreateSelf() { return new CMICmdCmdGdbSet(); } //++ //------------------------------------------------------------------------------------ // Details: Retrieve the print function's pointer for the matching print // request. // Type: Method. // Args: vrPrintFnName - (R) The info requested. // vrwpFn - (W) The print function's pointer of the function // to carry out // Return: bool - True = Print request is implemented, false = not found. // Throws: None. //-- bool CMICmdCmdGdbSet::GetOptionFn(const CMIUtilString &vrPrintFnName, FnGdbOptionPtr &vrwpFn) const { vrwpFn = nullptr; const MapGdbOptionNameToFnGdbOptionPtr_t::const_iterator it = ms_mapGdbOptionNameToFnGdbOptionPtr.find(vrPrintFnName); if (it != ms_mapGdbOptionNameToFnGdbOptionPtr.end()) { vrwpFn = (*it).second; return true; } return false; } //++ //------------------------------------------------------------------------------------ // Details: Carry out work to complete the GDB set option 'target-async' to // prepare // and send back information asked for. // Type: Method. // Args: vrWords - (R) List of additional parameters used by this option. // Return: MIstatus::success - Function succeeded. // MIstatus::failure - Function failed. // Throws: None. //-- bool CMICmdCmdGdbSet::OptionFnTargetAsync( const CMIUtilString::VecString_t &vrWords) { bool bAsyncMode = false; bool bOk = true; if (vrWords.size() > 1) // Too many arguments. bOk = false; else if (vrWords.size() == 0) // If no arguments, default is "on". bAsyncMode = true; else if (CMIUtilString::Compare(vrWords[0], "on")) bAsyncMode = true; else if (CMIUtilString::Compare(vrWords[0], "off")) bAsyncMode = false; else // Unrecognized argument. bOk = false; if (!bOk) { // Report error. m_bGbbOptionFnHasError = true; m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSET_OPT_TARGETASYNC); return MIstatus::failure; } // Turn async mode on/off. CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); rSessionInfo.GetDebugger().SetAsync(bAsyncMode); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Carry out work to complete the GDB set option // 'print-char-array-as-string' to // prepare and send back information asked for. // Type: Method. // Args: vrWords - (R) List of additional parameters used by this option. // Return: MIstatus::success - Function succeeded. // MIstatus::failure - Function failed. // Throws: None. //-- bool CMICmdCmdGdbSet::OptionFnPrint(const CMIUtilString::VecString_t &vrWords) { const bool bAllArgs(vrWords.size() == 2); const bool bArgOn(bAllArgs && (CMIUtilString::Compare(vrWords[1], "on") || CMIUtilString::Compare(vrWords[1], "1"))); const bool bArgOff(bAllArgs && (CMIUtilString::Compare(vrWords[1], "off") || CMIUtilString::Compare(vrWords[1], "0"))); if (!bAllArgs || (!bArgOn && !bArgOff)) { m_bGbbOptionFnHasError = true; m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSET_OPT_PRINT_BAD_ARGS); return MIstatus::failure; } const CMIUtilString strOption(vrWords[0]); CMIUtilString strOptionKey; if (CMIUtilString::Compare(strOption, "char-array-as-string")) strOptionKey = m_rLLDBDebugSessionInfo.m_constStrPrintCharArrayAsString; else if (CMIUtilString::Compare(strOption, "expand-aggregates")) strOptionKey = m_rLLDBDebugSessionInfo.m_constStrPrintExpandAggregates; else if (CMIUtilString::Compare(strOption, "aggregate-field-names")) strOptionKey = m_rLLDBDebugSessionInfo.m_constStrPrintAggregateFieldNames; else { m_bGbbOptionFnHasError = true; m_strGdbOptionFnError = CMIUtilString::Format( MIRSRC(IDS_CMD_ERR_GDBSET_OPT_PRINT_UNKNOWN_OPTION), strOption.c_str()); return MIstatus::failure; } const bool bOptionValue(bArgOn); if (!m_rLLDBDebugSessionInfo.SharedDataAdd(strOptionKey, bOptionValue)) { m_bGbbOptionFnHasError = false; SetError(CMIUtilString::Format(MIRSRC(IDS_DBGSESSION_ERR_SHARED_DATA_ADD), m_cmdData.strMiCmd.c_str(), strOptionKey.c_str())); return MIstatus::failure; } return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Carry out work to complete the GDB set option 'solib-search-path' to // prepare // and send back information asked for. // Type: Method. // Args: vrWords - (R) List of additional parameters used by this option. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdGdbSet::OptionFnSolibSearchPath( const CMIUtilString::VecString_t &vrWords) { // Check we have at least one argument if (vrWords.size() < 1) { m_bGbbOptionFnHasError = true; m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSET_OPT_SOLIBSEARCHPATH); return MIstatus::failure; } const CMIUtilString &rStrValSolibPath(vrWords[0]); // Add 'solib-search-path' to the shared data list const CMIUtilString &rStrKeySolibPath( m_rLLDBDebugSessionInfo.m_constStrSharedDataSolibPath); if (!m_rLLDBDebugSessionInfo.SharedDataAdd(rStrKeySolibPath, rStrValSolibPath)) { m_bGbbOptionFnHasError = false; SetError(CMIUtilString::Format(MIRSRC(IDS_DBGSESSION_ERR_SHARED_DATA_ADD), m_cmdData.strMiCmd.c_str(), rStrKeySolibPath.c_str())); return MIstatus::failure; } return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Carry out work to complete the GDB set option 'output-radix' to // prepare // and send back information asked for. // Type: Method. // Args: vrWords - (R) List of additional parameters used by this option. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdGdbSet::OptionFnOutputRadix( const CMIUtilString::VecString_t &vrWords) { // Check we have at least one argument if (vrWords.size() < 1) { m_bGbbOptionFnHasError = true; m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSET_OPT_SOLIBSEARCHPATH); return MIstatus::failure; } const CMIUtilString &rStrValOutputRadix(vrWords[0]); CMICmnLLDBDebugSessionInfoVarObj::varFormat_e format = CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Invalid; MIint64 radix; if (rStrValOutputRadix.ExtractNumber(radix)) { switch (radix) { case 8: format = CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Octal; break; case 10: format = CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Natural; break; case 16: format = CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Hex; break; default: format = CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Invalid; break; } } if (format == CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Invalid) { m_bGbbOptionFnHasError = false; SetError(CMIUtilString::Format(MIRSRC(IDS_DBGSESSION_ERR_SHARED_DATA_ADD), m_cmdData.strMiCmd.c_str(), "Output Radix")); return MIstatus::failure; } CMICmnLLDBDebugSessionInfoVarObj::VarObjSetFormat(format); + + return MIstatus::success; +} + +//++ +//------------------------------------------------------------------------------------ +// Details: Carry out work to complete the GDB set option 'disassembly-flavor' +// to prepare +// and send back information asked for. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbSet::OptionFnDisassemblyFlavor( + const CMIUtilString::VecString_t &vrWords) { + // Check we have at least one argument + if (vrWords.size() < 1) { + m_bGbbOptionFnHasError = true; + // m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSET_OPT_SOLIBSEARCHPATH); + return MIstatus::failure; + } + const CMIUtilString &rStrValDisasmFlavor(vrWords[0]); + + lldb::SBDebugger &rDbgr = m_rLLDBDebugSessionInfo.GetDebugger(); + lldb::SBError error = lldb::SBDebugger::SetInternalVariable( + "target.x86-disassembly-flavor", rStrValDisasmFlavor.c_str(), + rDbgr.GetInstanceName()); + if (error.Fail()) { + m_strGdbOptionFnError = error.GetCString(); + return MIstatus::failure; + } return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Carry out work to complete the GDB set option to prepare and send // back the // requested information. // Type: Method. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdGdbSet::OptionFnFallback( const CMIUtilString::VecString_t &vrWords) { MIunused(vrWords); // Do nothing - intentional. This is a fallback function to do nothing. // This allows the search for gdb-set options to always succeed when the // option is not // found (implemented). return MIstatus::success; } Index: vendor/lldb/dist/tools/lldb-mi/MICmdCmdGdbSet.h =================================================================== --- vendor/lldb/dist/tools/lldb-mi/MICmdCmdGdbSet.h (revision 311541) +++ vendor/lldb/dist/tools/lldb-mi/MICmdCmdGdbSet.h (revision 311542) @@ -1,100 +1,101 @@ //===-- MICmdCmdGdbSet.h ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Overview: CMICmdCmdGdbSet interface. // // To implement new MI commands, derive a new command class from // the command base // class. To enable the new command for interpretation add the new // command class // to the command factory. The files of relevance are: // MICmdCommands.cpp // MICmdBase.h / .cpp // MICmdCmd.h / .cpp // For an introduction to adding a new command see // CMICmdCmdSupportInfoMiCmdQuery // command class as an example. #pragma once // In-house headers: #include "MICmdBase.h" //++ //============================================================================ // Details: MI command class. MI commands derived from the command base class. // *this class implements MI command "gdb-set". // This command does not follow the MI documentation exactly. While // *this // command is implemented it does not do anything with the gdb-set // variable past in. // The design of matching the info request to a request action (or // command) is very simple. The request function which carries out // the task of information gathering and printing to stdout is part of // *this class. Should the request function become more complicated // then // that request should really reside in a command type class. Then this // class instantiates a request info command for a matching request. // The // design/code of *this class then does not then become bloated. Use a // lightweight version of the current MI command system. //-- class CMICmdCmdGdbSet : public CMICmdBase { // Statics: public: // Required by the CMICmdFactory when registering *this command static CMICmdBase *CreateSelf(); // Methods: public: /* ctor */ CMICmdCmdGdbSet(); // Overridden: public: // From CMICmdInvoker::ICmd bool Execute() override; bool Acknowledge() override; bool ParseArgs() override; // From CMICmnBase /* dtor */ ~CMICmdCmdGdbSet() override; // Typedefs: private: typedef bool (CMICmdCmdGdbSet::*FnGdbOptionPtr)( const CMIUtilString::VecString_t &vrWords); typedef std::map MapGdbOptionNameToFnGdbOptionPtr_t; // Methods: private: bool GetOptionFn(const CMIUtilString &vrGdbOptionName, FnGdbOptionPtr &vrwpFn) const; bool OptionFnTargetAsync(const CMIUtilString::VecString_t &vrWords); bool OptionFnPrint(const CMIUtilString::VecString_t &vrWords); bool OptionFnSolibSearchPath(const CMIUtilString::VecString_t &vrWords); bool OptionFnOutputRadix(const CMIUtilString::VecString_t &vrWords); + bool OptionFnDisassemblyFlavor(const CMIUtilString::VecString_t &vrWords); bool OptionFnFallback(const CMIUtilString::VecString_t &vrWords); // Attributes: private: const static MapGdbOptionNameToFnGdbOptionPtr_t ms_mapGdbOptionNameToFnGdbOptionPtr; // const CMIUtilString m_constStrArgNamedGdbOption; bool m_bGdbOptionRecognised; // True = This command has a function with a name // that matches the Print argument, false = not // found bool m_bGdbOptionFnSuccessful; // True = The print function completed its task // ok, false = function failed for some reason bool m_bGbbOptionFnHasError; // True = The option function has an error // condition (not the command!), false = option // function ok. CMIUtilString m_strGdbOptionName; CMIUtilString m_strGdbOptionFnError; }; Index: vendor/lldb/dist/tools/lldb-mi/MICmdCmdGdbShow.cpp =================================================================== --- vendor/lldb/dist/tools/lldb-mi/MICmdCmdGdbShow.cpp (revision 311541) +++ vendor/lldb/dist/tools/lldb-mi/MICmdCmdGdbShow.cpp (revision 311542) @@ -1,349 +1,371 @@ //===-- MICmdCmdGdbShow.cpp -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Overview: CMICmdCmdGdbShow implementation. // Third party headers: #include "lldb/API/SBCompileUnit.h" #include "lldb/API/SBFrame.h" #include "lldb/API/SBLanguageRuntime.h" +#include "lldb/API/SBStringList.h" #include "lldb/API/SBThread.h" // In-house headers: #include "MICmdArgValListOfN.h" #include "MICmdArgValOptionLong.h" #include "MICmdArgValString.h" #include "MICmdCmdGdbShow.h" #include "MICmnLLDBDebugSessionInfo.h" #include "MICmnMIResultRecord.h" #include "MICmnMIValueConst.h" // Instantiations: const CMICmdCmdGdbShow::MapGdbOptionNameToFnGdbOptionPtr_t CMICmdCmdGdbShow::ms_mapGdbOptionNameToFnGdbOptionPtr = { {"target-async", &CMICmdCmdGdbShow::OptionFnTargetAsync}, {"print", &CMICmdCmdGdbShow::OptionFnPrint}, {"language", &CMICmdCmdGdbShow::OptionFnLanguage}, + {"disassembly-flavor", &CMICmdCmdGdbShow::OptionFnDisassemblyFlavor}, {"fallback", &CMICmdCmdGdbShow::OptionFnFallback}}; //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdGdbShow constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdGdbShow::CMICmdCmdGdbShow() : m_constStrArgNamedGdbOption("option"), m_bGdbOptionRecognised(true), m_bGdbOptionFnSuccessful(false), m_bGbbOptionFnHasError(false), m_strGdbOptionFnError(MIRSRC(IDS_WORD_ERR_MSG_NOT_IMPLEMENTED_BRKTS)) { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "gdb-show"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdGdbShow::CreateSelf; } //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdGdbShow destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdGdbShow::~CMICmdCmdGdbShow() {} //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line // options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Function succeeded. // MIstatus::failure - Function failed. // Throws: None. //-- bool CMICmdCmdGdbShow::ParseArgs() { m_setCmdArgs.Add(new CMICmdArgValListOfN( m_constStrArgNamedGdbOption, true, true, CMICmdArgValListBase::eArgValType_StringAnything)); return ParseValidateCmdOptions(); } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command is executed in this // function. // Type: Overridden. // Args: None. // Return: MIstatus::success - Function succeeded. // MIstatus::failure - Function failed. // Throws: None. //-- bool CMICmdCmdGdbShow::Execute() { CMICMDBASE_GETOPTION(pArgGdbOption, ListOfN, m_constStrArgNamedGdbOption); const CMICmdArgValListBase::VecArgObjPtr_t &rVecWords( pArgGdbOption->GetExpectedOptions()); // Get the gdb-show option to carry out. This option will be used as an action // which should be done. Further arguments will be used as parameters for it. CMICmdArgValListBase::VecArgObjPtr_t::const_iterator it = rVecWords.begin(); const CMICmdArgValString *pOption = static_cast(*it); const CMIUtilString strOption(pOption->GetValue()); ++it; // Retrieve the parameter(s) for the option CMIUtilString::VecString_t vecWords; while (it != rVecWords.end()) { const CMICmdArgValString *pWord = static_cast(*it); vecWords.push_back(pWord->GetValue()); // Next ++it; } FnGdbOptionPtr pPrintRequestFn = nullptr; if (!GetOptionFn(strOption, pPrintRequestFn)) { // For unimplemented option handlers, fallback to a generic handler // ToDo: Remove this when ALL options have been implemented if (!GetOptionFn("fallback", pPrintRequestFn)) { m_bGdbOptionRecognised = false; m_strGdbOptionName = "fallback"; // This would be the strOption name return MIstatus::success; } } m_bGdbOptionFnSuccessful = (this->*(pPrintRequestFn))(vecWords); if (!m_bGdbOptionFnSuccessful && !m_bGbbOptionFnHasError) return MIstatus::failure; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record // Result // for the work carried out in the Execute() method. // Type: Overridden. // Args: None. // Return: MIstatus::success - Function succeeded. // MIstatus::failure - Function failed. // Throws: None. //-- bool CMICmdCmdGdbShow::Acknowledge() { // Print error if option isn't recognized: // ^error,msg="The request '%s' was not recognized, not implemented" if (!m_bGdbOptionRecognised) { const CMICmnMIValueConst miValueConst( CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INFO_PRINTFN_NOT_FOUND), m_strGdbOptionName.c_str())); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } // ^done,value="%s" if (m_bGdbOptionFnSuccessful && !m_strValue.empty()) { const CMICmnMIValueConst miValueConst(m_strValue); const CMICmnMIValueResult miValueResult("value", miValueConst); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } else if (m_bGdbOptionFnSuccessful) { // Ignore empty value (for fallback) const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); m_miResultRecord = miRecordResult; return MIstatus::success; } // Print error if request failed: // ^error,msg="The request '%s' failed. const CMICmnMIValueConst miValueConst(CMIUtilString::Format( MIRSRC(IDS_CMD_ERR_INFO_PRINTFN_FAILED), m_strGdbOptionFnError.c_str())); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The // factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase *CMICmdCmdGdbShow::CreateSelf() { return new CMICmdCmdGdbShow(); } //++ //------------------------------------------------------------------------------------ // Details: Retrieve the print function's pointer for the matching print // request. // Type: Method. // Args: vrPrintFnName - (R) The info requested. // vrwpFn - (W) The print function's pointer of the function // to carry out // Return: bool - True = Print request is implemented, false = not found. // Throws: None. //-- bool CMICmdCmdGdbShow::GetOptionFn(const CMIUtilString &vrPrintFnName, FnGdbOptionPtr &vrwpFn) const { vrwpFn = nullptr; const MapGdbOptionNameToFnGdbOptionPtr_t::const_iterator it = ms_mapGdbOptionNameToFnGdbOptionPtr.find(vrPrintFnName); if (it != ms_mapGdbOptionNameToFnGdbOptionPtr.end()) { vrwpFn = (*it).second; return true; } return false; } //++ //------------------------------------------------------------------------------------ // Details: Carry out work to complete the GDB show option 'target-async' to // prepare // and send back the requested information. // Type: Method. // Args: vrWords - (R) List of additional parameters used by this option. // Return: MIstatus::success - Function succeeded. // MIstatus::failure - Function failed. // Throws: None. //-- bool CMICmdCmdGdbShow::OptionFnTargetAsync( const CMIUtilString::VecString_t &vrWords) { MIunused(vrWords); // Get async mode CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); const bool bAsyncMode = rSessionInfo.GetDebugger().GetAsync(); m_strValue = bAsyncMode ? "on" : "off"; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Carry out work to complete the GDB show option 'print' to prepare // and send // back the requested information. // Type: Method. // Args: vrWords - (R) List of additional parameters used by this option. // Return: MIstatus::success - Function succeeded. // MIstatus::failure - Function failed. // Throws: None. //-- bool CMICmdCmdGdbShow::OptionFnPrint( const CMIUtilString::VecString_t &vrWords) { const bool bAllArgs(vrWords.size() == 1); if (!bAllArgs) { m_bGbbOptionFnHasError = true; m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSHOW_OPT_PRINT_BAD_ARGS); return MIstatus::failure; } const CMIUtilString strOption(vrWords[0]); CMIUtilString strOptionKey; bool bOptionValueDefault = false; if (CMIUtilString::Compare(strOption, "char-array-as-string")) strOptionKey = m_rLLDBDebugSessionInfo.m_constStrPrintCharArrayAsString; else if (CMIUtilString::Compare(strOption, "expand-aggregates")) strOptionKey = m_rLLDBDebugSessionInfo.m_constStrPrintExpandAggregates; else if (CMIUtilString::Compare(strOption, "aggregate-field-names")) { strOptionKey = m_rLLDBDebugSessionInfo.m_constStrPrintAggregateFieldNames; bOptionValueDefault = true; } else { m_bGbbOptionFnHasError = true; m_strGdbOptionFnError = CMIUtilString::Format( MIRSRC(IDS_CMD_ERR_GDBSHOW_OPT_PRINT_UNKNOWN_OPTION), strOption.c_str()); return MIstatus::failure; } bool bOptionValue = false; bOptionValue = bOptionValueDefault ? !m_rLLDBDebugSessionInfo.SharedDataRetrieve( strOptionKey, bOptionValue) || bOptionValue : m_rLLDBDebugSessionInfo.SharedDataRetrieve( strOptionKey, bOptionValue) && bOptionValue; m_strValue = bOptionValue ? "on" : "off"; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Carry out work to complete the GDB show option 'language' to prepare // and send back the requested information. // Type: Method. // Args: vrWords - (R) List of additional parameters used by this option. // Return: MIstatus::success - Function succeeded. // MIstatus::failure - Function failed. // Throws: None. //-- bool CMICmdCmdGdbShow::OptionFnLanguage( const CMIUtilString::VecString_t &vrWords) { MIunused(vrWords); // Get current language CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); lldb::SBThread sbThread = rSessionInfo.GetProcess().GetSelectedThread(); const lldb::SBFrame sbFrame = sbThread.GetSelectedFrame(); lldb::SBCompileUnit sbCompileUnit = sbFrame.GetCompileUnit(); const lldb::LanguageType eLanguageType = sbCompileUnit.GetLanguage(); m_strValue = lldb::SBLanguageRuntime::GetNameForLanguageType(eLanguageType); + return MIstatus::success; +} + +//++ +//------------------------------------------------------------------------------------ +// Details: Carry out work to complete the GDB show option 'disassembly-flavor' to prepare +// and send back the requested information. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbShow::OptionFnDisassemblyFlavor(const CMIUtilString::VecString_t &vrWords) { + MIunused(vrWords); + + // Get current disassembly flavor + lldb::SBDebugger &rDbgr = m_rLLDBDebugSessionInfo.GetDebugger(); + m_strValue = lldb::SBDebugger::GetInternalVariableValue("target.x86-disassembly-flavor", + rDbgr.GetInstanceName()).GetStringAtIndex(0); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Carry out work to complete the GDB show option to prepare and send // back the // requested information. // Type: Method. // Args: None. // Return: MIstatus::success - Function succeeded. // MIstatus::failure - Function failed. // Throws: None. //-- bool CMICmdCmdGdbShow::OptionFnFallback( const CMIUtilString::VecString_t &vrWords) { MIunused(vrWords); // Do nothing - intentional. This is a fallback function to do nothing. // This allows the search for gdb-show options to always succeed when the // option is not // found (implemented). return MIstatus::success; } Index: vendor/lldb/dist/tools/lldb-mi/MICmdCmdGdbShow.h =================================================================== --- vendor/lldb/dist/tools/lldb-mi/MICmdCmdGdbShow.h (revision 311541) +++ vendor/lldb/dist/tools/lldb-mi/MICmdCmdGdbShow.h (revision 311542) @@ -1,100 +1,101 @@ //===-- MICmdCmdGdbShow.h ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Overview: CMICmdCmdGdbShow interface. // // To implement new MI commands, derive a new command class from // the command base // class. To enable the new command for interpretation add the new // command class // to the command factory. The files of relevance are: // MICmdCommands.cpp // MICmdBase.h / .cpp // MICmdCmd.h / .cpp // For an introduction to adding a new command see // CMICmdCmdSupportInfoMiCmdQuery // command class as an example. #pragma once // In-house headers: #include "MICmdBase.h" //++ //============================================================================ // Details: MI command class. MI commands derived from the command base class. // *this class implements MI command "gdb-show". // This command does not follow the MI documentation exactly. While // *this // command is implemented it does not do anything with the gdb-set // variable past in. // The design of matching the info request to a request action (or // command) is very simple. The request function which carries out // the task of information gathering and printing to stdout is part of // *this class. Should the request function become more complicated // then // that request should really reside in a command type class. Then this // class instantiates a request info command for a matching request. // The // design/code of *this class then does not then become bloated. Use a // lightweight version of the current MI command system. //-- class CMICmdCmdGdbShow : public CMICmdBase { // Statics: public: // Required by the CMICmdFactory when registering *this command static CMICmdBase *CreateSelf(); // Methods: public: /* ctor */ CMICmdCmdGdbShow(); // Overridden: public: // From CMICmdInvoker::ICmd bool Execute() override; bool Acknowledge() override; bool ParseArgs() override; // From CMICmnBase /* dtor */ ~CMICmdCmdGdbShow() override; // Typedefs: private: typedef bool (CMICmdCmdGdbShow::*FnGdbOptionPtr)( const CMIUtilString::VecString_t &vrWords); typedef std::map MapGdbOptionNameToFnGdbOptionPtr_t; // Methods: private: bool GetOptionFn(const CMIUtilString &vrGdbOptionName, FnGdbOptionPtr &vrwpFn) const; bool OptionFnTargetAsync(const CMIUtilString::VecString_t &vrWords); bool OptionFnPrint(const CMIUtilString::VecString_t &vrWords); bool OptionFnLanguage(const CMIUtilString::VecString_t &vrWords); + bool OptionFnDisassemblyFlavor(const CMIUtilString::VecString_t &vrWords); bool OptionFnFallback(const CMIUtilString::VecString_t &vrWords); // Attributes: private: const static MapGdbOptionNameToFnGdbOptionPtr_t ms_mapGdbOptionNameToFnGdbOptionPtr; const CMIUtilString m_constStrArgNamedGdbOption; bool m_bGdbOptionRecognised; // True = This command has a function with a name // that matches the Print argument, false = not // found bool m_bGdbOptionFnSuccessful; // True = The print function completed its task // ok, false = function failed for some reason bool m_bGbbOptionFnHasError; // True = The option function has an error // condition (not the command!), false = option // function ok. CMIUtilString m_strGdbOptionName; CMIUtilString m_strGdbOptionFnError; CMIUtilString m_strValue; }; Index: vendor/lldb/dist/tools/lldb-mi/MICmdCmdMiscellanous.cpp =================================================================== --- vendor/lldb/dist/tools/lldb-mi/MICmdCmdMiscellanous.cpp (revision 311541) +++ vendor/lldb/dist/tools/lldb-mi/MICmdCmdMiscellanous.cpp (revision 311542) @@ -1,609 +1,617 @@ //===-- MICmdCmdMiscellanous.cpp --------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Overview: CMICmdCmdGdbExit implementation. // CMICmdCmdListThreadGroups implementation. // CMICmdCmdInterpreterExec implementation. // CMICmdCmdInferiorTtySet implementation. // Third Party Headers: #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBThread.h" // In-house headers: #include "MICmdArgValFile.h" #include "MICmdArgValListOfN.h" #include "MICmdArgValNumber.h" #include "MICmdArgValOptionLong.h" #include "MICmdArgValOptionShort.h" #include "MICmdArgValString.h" #include "MICmdArgValThreadGrp.h" #include "MICmdCmdMiscellanous.h" #include "MICmnLLDBDebugSessionInfo.h" #include "MICmnLLDBDebugger.h" #include "MICmnMIOutOfBandRecord.h" #include "MICmnMIResultRecord.h" #include "MICmnMIValueConst.h" #include "MICmnStreamStderr.h" #include "MICmnStreamStdout.h" #include "MIDriverBase.h" //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdGdbExit constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdGdbExit::CMICmdCmdGdbExit() { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "gdb-exit"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdGdbExit::CreateSelf; } //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdGdbExit destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdGdbExit::~CMICmdCmdGdbExit() {} //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this // function. // The command is likely to communicate with the LLDB SBDebugger in // here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdGdbExit::Execute() { CMICmnLLDBDebugger::Instance().GetDriver().SetExitApplicationFlag(true); const lldb::SBError sbErr = m_rLLDBDebugSessionInfo.GetProcess().Destroy(); // Do not check for sbErr.Fail() here, m_lldbProcess is likely !IsValid() return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record // Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdGdbExit::Acknowledge() { const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Exit); m_miResultRecord = miRecordResult; // Prod the client i.e. Eclipse with out-of-band results to help it 'continue' // because it is using LLDB debugger // Give the client '=thread-group-exited,id="i1"' m_bHasResultRecordExtra = true; const CMICmnMIValueConst miValueConst2("i1"); const CMICmnMIValueResult miValueResult2("id", miValueConst2); const CMICmnMIOutOfBandRecord miOutOfBand( CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupExited, miValueResult2); m_miResultRecordExtra = miOutOfBand.GetString(); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The // factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase *CMICmdCmdGdbExit::CreateSelf() { return new CMICmdCmdGdbExit(); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdListThreadGroups constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdListThreadGroups::CMICmdCmdListThreadGroups() : m_bIsI1(false), m_bHaveArgOption(false), m_bHaveArgRecurse(false), m_constStrArgNamedAvailable("available"), m_constStrArgNamedRecurse("recurse"), m_constStrArgNamedGroup("group"), m_constStrArgNamedThreadGroup("i1") { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "list-thread-groups"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdListThreadGroups::CreateSelf; } //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdListThreadGroups destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdListThreadGroups::~CMICmdCmdListThreadGroups() { m_vecMIValueTuple.clear(); } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line // options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdListThreadGroups::ParseArgs() { m_setCmdArgs.Add( new CMICmdArgValOptionLong(m_constStrArgNamedAvailable, false, true)); m_setCmdArgs.Add( new CMICmdArgValOptionLong(m_constStrArgNamedRecurse, false, true, CMICmdArgValListBase::eArgValType_Number, 1)); m_setCmdArgs.Add( new CMICmdArgValListOfN(m_constStrArgNamedGroup, false, true, CMICmdArgValListBase::eArgValType_Number)); m_setCmdArgs.Add( new CMICmdArgValThreadGrp(m_constStrArgNamedThreadGroup, false, true)); return ParseValidateCmdOptions(); } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this // function. // The command is likely to communicate with the LLDB SBDebugger in // here. // Synopsis: -list-thread-groups [ --available ] [ --recurse 1 ] [ // group ... ] // This command does not follow the MI documentation exactly. Has an // extra // argument "i1" to handle. // Ref: // http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Miscellaneous-Commands.html#GDB_002fMI-Miscellaneous-Commands // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdListThreadGroups::Execute() { if (m_setCmdArgs.IsArgContextEmpty()) // No options so "top level thread groups" return MIstatus::success; CMICMDBASE_GETOPTION(pArgAvailable, OptionLong, m_constStrArgNamedAvailable); CMICMDBASE_GETOPTION(pArgRecurse, OptionLong, m_constStrArgNamedRecurse); CMICMDBASE_GETOPTION(pArgThreadGroup, ThreadGrp, m_constStrArgNamedThreadGroup); // Got some options so "threads" if (pArgAvailable->GetFound()) { if (pArgRecurse->GetFound()) { m_bHaveArgRecurse = true; return MIstatus::success; } m_bHaveArgOption = true; return MIstatus::success; } // "i1" as first argument (pos 0 of possible arg) if (!pArgThreadGroup->GetFound()) return MIstatus::success; m_bIsI1 = true; CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); // Note do not check for sbProcess is IsValid(), continue m_vecMIValueTuple.clear(); const MIuint nThreads = sbProcess.GetNumThreads(); for (MIuint i = 0; i < nThreads; i++) { // GetThreadAtIndex() uses a base 0 index // GetThreadByIndexID() uses a base 1 index lldb::SBThread thread = sbProcess.GetThreadAtIndex(i); if (thread.IsValid()) { CMICmnMIValueTuple miTuple; if (!rSessionInfo.MIResponseFormThreadInfo( m_cmdData, thread, CMICmnLLDBDebugSessionInfo::eThreadInfoFormat_NoFrames, miTuple)) return MIstatus::failure; m_vecMIValueTuple.push_back(miTuple); } } return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record // Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdListThreadGroups::Acknowledge() { if (m_bHaveArgOption) { if (m_bHaveArgRecurse) { const CMICmnMIValueConst miValueConst( MIRSRC(IDS_WORD_NOT_IMPLEMENTED_BRKTS)); const CMICmnMIValueResult miValueResult("msg", miValueConst); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } const CMICmnMIValueConst miValueConst1("i1"); const CMICmnMIValueResult miValueResult1("id", miValueConst1); CMICmnMIValueTuple miTuple(miValueResult1); const CMICmnMIValueConst miValueConst2("process"); const CMICmnMIValueResult miValueResult2("type", miValueConst2); miTuple.Add(miValueResult2); CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); if (rSessionInfo.GetProcess().IsValid()) { const lldb::pid_t pid = rSessionInfo.GetProcess().GetProcessID(); const CMIUtilString strPid(CMIUtilString::Format("%lld", pid)); const CMICmnMIValueConst miValueConst3(strPid); const CMICmnMIValueResult miValueResult3("pid", miValueConst3); miTuple.Add(miValueResult3); } const CMICmnMIValueConst miValueConst4( MIRSRC(IDS_WORD_NOT_IMPLEMENTED_BRKTS)); const CMICmnMIValueResult miValueResult4("num_children", miValueConst4); miTuple.Add(miValueResult4); const CMICmnMIValueConst miValueConst5( MIRSRC(IDS_WORD_NOT_IMPLEMENTED_BRKTS)); const CMICmnMIValueResult miValueResult5("cores", miValueConst5); miTuple.Add(miValueResult5); const CMICmnMIValueList miValueList(miTuple); const CMICmnMIValueResult miValueResult6("groups", miValueList); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult6); m_miResultRecord = miRecordResult; return MIstatus::success; } if (!m_bIsI1) { const CMICmnMIValueConst miValueConst1("i1"); const CMICmnMIValueResult miValueResult1("id", miValueConst1); CMICmnMIValueTuple miTuple(miValueResult1); const CMICmnMIValueConst miValueConst2("process"); const CMICmnMIValueResult miValueResult2("type", miValueConst2); miTuple.Add(miValueResult2); CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); if (rSessionInfo.GetProcess().IsValid()) { const lldb::pid_t pid = rSessionInfo.GetProcess().GetProcessID(); const CMIUtilString strPid(CMIUtilString::Format("%lld", pid)); const CMICmnMIValueConst miValueConst3(strPid); const CMICmnMIValueResult miValueResult3("pid", miValueConst3); miTuple.Add(miValueResult3); } if (rSessionInfo.GetTarget().IsValid()) { lldb::SBTarget sbTrgt = rSessionInfo.GetTarget(); const char *pDir = sbTrgt.GetExecutable().GetDirectory(); const char *pFileName = sbTrgt.GetExecutable().GetFilename(); const CMIUtilString strFile( CMIUtilString::Format("%s/%s", pDir, pFileName)); const CMICmnMIValueConst miValueConst4(strFile); const CMICmnMIValueResult miValueResult4("executable", miValueConst4); miTuple.Add(miValueResult4); } const CMICmnMIValueList miValueList(miTuple); const CMICmnMIValueResult miValueResult5("groups", miValueList); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult5); m_miResultRecord = miRecordResult; return MIstatus::success; } // Build up a list of thread information from tuples VecMIValueTuple_t::const_iterator it = m_vecMIValueTuple.begin(); if (it == m_vecMIValueTuple.end()) { const CMICmnMIValueConst miValueConst("[]"); const CMICmnMIValueResult miValueResult("threads", miValueConst); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } CMICmnMIValueList miValueList(*it); ++it; while (it != m_vecMIValueTuple.end()) { const CMICmnMIValueTuple &rTuple(*it); miValueList.Add(rTuple); // Next ++it; } const CMICmnMIValueResult miValueResult("threads", miValueList); const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, miValueResult); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The // factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase *CMICmdCmdListThreadGroups::CreateSelf() { return new CMICmdCmdListThreadGroups(); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdInterpreterExec constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdInterpreterExec::CMICmdCmdInterpreterExec() : m_constStrArgNamedInterpreter("interpreter"), m_constStrArgNamedCommand("command") { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "interpreter-exec"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdInterpreterExec::CreateSelf; } //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdInterpreterExec destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdInterpreterExec::~CMICmdCmdInterpreterExec() {} //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line // options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdInterpreterExec::ParseArgs() { m_setCmdArgs.Add( new CMICmdArgValString(m_constStrArgNamedInterpreter, true, true)); m_setCmdArgs.Add( new CMICmdArgValString(m_constStrArgNamedCommand, true, true, true)); return ParseValidateCmdOptions(); } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this // function. // The command is likely to communicate with the LLDB SBDebugger in // here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdInterpreterExec::Execute() { CMICMDBASE_GETOPTION(pArgInterpreter, String, m_constStrArgNamedInterpreter); CMICMDBASE_GETOPTION(pArgCommand, String, m_constStrArgNamedCommand); // Handle the interpreter parameter by do nothing on purpose (set to 'handled' // in // the arg definition above) const CMIUtilString &rStrInterpreter(pArgInterpreter->GetValue()); MIunused(rStrInterpreter); const CMIUtilString &rStrCommand(pArgCommand->GetValue()); CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); const lldb::ReturnStatus rtn = rSessionInfo.GetDebugger().GetCommandInterpreter().HandleCommand( rStrCommand.c_str(), m_lldbResult, true); MIunused(rtn); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record // Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdInterpreterExec::Acknowledge() { if (m_lldbResult.GetOutputSize() > 0) { - CMIUtilString strMsg(m_lldbResult.GetOutput()); - strMsg = strMsg.StripCREndOfLine(); - CMICmnStreamStdout::TextToStdout(strMsg); + const CMIUtilString line(m_lldbResult.GetOutput()); + const bool bEscapeQuotes(true); + CMICmnMIValueConst miValueConst(line.Escape(bEscapeQuotes)); + CMICmnMIOutOfBandRecord miOutOfBandRecord(CMICmnMIOutOfBandRecord::eOutOfBand_ConsoleStreamOutput, miValueConst); + const bool bOk = CMICmnStreamStdout::TextToStdout(miOutOfBandRecord.GetString()); + if (!bOk) + return MIstatus::failure; } if (m_lldbResult.GetErrorSize() > 0) { - CMIUtilString strMsg(m_lldbResult.GetError()); - strMsg = strMsg.StripCREndOfLine(); - CMICmnStreamStderr::LLDBMsgToConsole(strMsg); + const CMIUtilString line(m_lldbResult.GetError()); + const bool bEscapeQuotes(true); + CMICmnMIValueConst miValueConst(line.Escape(bEscapeQuotes)); + CMICmnMIOutOfBandRecord miOutOfBandRecord(CMICmnMIOutOfBandRecord::eOutOfBand_LogStreamOutput, miValueConst); + const bool bOk = CMICmnStreamStdout::TextToStdout(miOutOfBandRecord.GetString()); + if (!bOk) + return MIstatus::failure; } const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The // factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase *CMICmdCmdInterpreterExec::CreateSelf() { return new CMICmdCmdInterpreterExec(); } //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------- //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdInferiorTtySet constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdInferiorTtySet::CMICmdCmdInferiorTtySet() { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "inferior-tty-set"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdInferiorTtySet::CreateSelf; } //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdInferiorTtySet destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdInferiorTtySet::~CMICmdCmdInferiorTtySet() {} //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this // function. // The command is likely to communicate with the LLDB SBDebugger in // here. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdInferiorTtySet::Execute() { // Do nothing return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record // Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdInferiorTtySet::Acknowledge() { const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The // factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase *CMICmdCmdInferiorTtySet::CreateSelf() { return new CMICmdCmdInferiorTtySet(); } Index: vendor/lldb/dist/tools/lldb-mi/MICmdCmdTarget.cpp =================================================================== --- vendor/lldb/dist/tools/lldb-mi/MICmdCmdTarget.cpp (revision 311541) +++ vendor/lldb/dist/tools/lldb-mi/MICmdCmdTarget.cpp (revision 311542) @@ -1,488 +1,489 @@ //===-- MICmdCmdTarget.cpp --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Overview: CMICmdCmdTargetSelect implementation. // Third Party Headers: #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBStream.h" // In-house headers: #include "MICmdArgValNumber.h" #include "MICmdArgValOptionLong.h" #include "MICmdArgValOptionShort.h" #include "MICmdArgValString.h" #include "MICmdCmdTarget.h" #include "MICmnLLDBDebugSessionInfo.h" #include "MICmnLLDBDebugger.h" #include "MICmnMIOutOfBandRecord.h" #include "MICmnMIResultRecord.h" #include "MICmnMIValueConst.h" //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdTargetSelect constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdTargetSelect::CMICmdCmdTargetSelect() : m_constStrArgNamedType("type"), m_constStrArgNamedParameters("parameters") { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "target-select"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdTargetSelect::CreateSelf; } //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdTargetSelect destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdTargetSelect::~CMICmdCmdTargetSelect() {} //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line // options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdTargetSelect::ParseArgs() { m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgNamedType, true, true)); m_setCmdArgs.Add( new CMICmdArgValString(m_constStrArgNamedParameters, true, true)); return ParseValidateCmdOptions(); } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this // function. // The command is likely to communicate with the LLDB SBDebugger in // here. // Synopsis: -target-select type parameters ... // Ref: // http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Target-Manipulation.html#GDB_002fMI-Target-Manipulation // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdTargetSelect::Execute() { CMICMDBASE_GETOPTION(pArgType, String, m_constStrArgNamedType); CMICMDBASE_GETOPTION(pArgParameters, String, m_constStrArgNamedParameters); CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); // Check we have a valid target // Note: target created via 'file-exec-and-symbols' command if (!rSessionInfo.GetTarget().IsValid()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_CURRENT), m_cmdData.strMiCmd.c_str())); return MIstatus::failure; } // Verify that we are executing remotely const CMIUtilString &rRemoteType(pArgType->GetValue()); if (rRemoteType != "remote") { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_TYPE), m_cmdData.strMiCmd.c_str(), rRemoteType.c_str())); return MIstatus::failure; } // Create a URL pointing to the remote gdb stub const CMIUtilString strUrl = CMIUtilString::Format("connect://%s", pArgParameters->GetValue().c_str()); // Ask LLDB to collect to the target port const char *pPlugin("gdb-remote"); lldb::SBError error; lldb::SBProcess process = rSessionInfo.GetTarget().ConnectRemote( rSessionInfo.GetListener(), strUrl.c_str(), pPlugin, error); // Verify that we have managed to connect successfully lldb::SBStream errMsg; + error.GetDescription(errMsg); if (!process.IsValid()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_PLUGIN), m_cmdData.strMiCmd.c_str(), errMsg.GetData())); return MIstatus::failure; } if (error.Fail()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_CONNECT_TO_TARGET), m_cmdData.strMiCmd.c_str(), errMsg.GetData())); return MIstatus::failure; } // Set the environment path if we were given one CMIUtilString strWkDir; if (rSessionInfo.SharedDataRetrieve( rSessionInfo.m_constStrSharedDataKeyWkDir, strWkDir)) { lldb::SBDebugger &rDbgr = rSessionInfo.GetDebugger(); if (!rDbgr.SetCurrentPlatformSDKRoot(strWkDir.c_str())) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_FNFAILED), m_cmdData.strMiCmd.c_str(), "target-select")); return MIstatus::failure; } } // Set the shared object path if we were given one CMIUtilString strSolibPath; if (rSessionInfo.SharedDataRetrieve( rSessionInfo.m_constStrSharedDataSolibPath, strSolibPath)) { lldb::SBDebugger &rDbgr = rSessionInfo.GetDebugger(); lldb::SBCommandInterpreter cmdIterpreter = rDbgr.GetCommandInterpreter(); CMIUtilString strCmdString = CMIUtilString::Format( "target modules search-paths add . %s", strSolibPath.c_str()); lldb::SBCommandReturnObject retObj; cmdIterpreter.HandleCommand(strCmdString.c_str(), retObj, false); if (!retObj.Succeeded()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_FNFAILED), m_cmdData.strMiCmd.c_str(), "target-select")); return MIstatus::failure; } } return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record // Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdTargetSelect::Acknowledge() { const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Connected); m_miResultRecord = miRecordResult; CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); lldb::pid_t pid = rSessionInfo.GetProcess().GetProcessID(); // Prod the client i.e. Eclipse with out-of-band results to help it 'continue' // because it is using LLDB debugger // Give the client '=thread-group-started,id="i1"' m_bHasResultRecordExtra = true; const CMICmnMIValueConst miValueConst2("i1"); const CMICmnMIValueResult miValueResult2("id", miValueConst2); const CMIUtilString strPid(CMIUtilString::Format("%lld", pid)); const CMICmnMIValueConst miValueConst(strPid); const CMICmnMIValueResult miValueResult("pid", miValueConst); CMICmnMIOutOfBandRecord miOutOfBand( CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupStarted, miValueResult2); miOutOfBand.Add(miValueResult); m_miResultRecordExtra = miOutOfBand.GetString(); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The // factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase *CMICmdCmdTargetSelect::CreateSelf() { return new CMICmdCmdTargetSelect(); } //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdTargetAttach constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdTargetAttach::CMICmdCmdTargetAttach() : m_constStrArgPid("pid"), m_constStrArgNamedFile("n"), m_constStrArgWaitFor("waitfor") { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "target-attach"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdTargetAttach::CreateSelf; } //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdTargetAttach destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdTargetAttach::~CMICmdCmdTargetAttach() {} //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line // options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdTargetAttach::ParseArgs() { m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgPid, false, true)); m_setCmdArgs.Add( new CMICmdArgValOptionShort(m_constStrArgNamedFile, false, true, CMICmdArgValListBase::eArgValType_String, 1)); m_setCmdArgs.Add( new CMICmdArgValOptionLong(m_constStrArgWaitFor, false, true)); return ParseValidateCmdOptions(); } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this // function. // The command is likely to communicate with the LLDB SBDebugger in // here. // Synopsis: -target-attach file // Ref: // http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Target-Manipulation.html#GDB_002fMI-Target-Manipulation // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdTargetAttach::Execute() { CMICMDBASE_GETOPTION(pArgPid, Number, m_constStrArgPid); CMICMDBASE_GETOPTION(pArgFile, OptionShort, m_constStrArgNamedFile); CMICMDBASE_GETOPTION(pArgWaitFor, OptionLong, m_constStrArgWaitFor); CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); // If the current target is invalid, create one lldb::SBTarget target = rSessionInfo.GetTarget(); if (!target.IsValid()) { target = rSessionInfo.GetDebugger().CreateTarget(NULL); if (!target.IsValid()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_CURRENT), m_cmdData.strMiCmd.c_str())); return MIstatus::failure; } } lldb::SBError error; lldb::SBListener listener; if (pArgPid->GetFound() && pArgPid->GetValid()) { lldb::pid_t pid; pid = pArgPid->GetValue(); target.AttachToProcessWithID(listener, pid, error); } else if (pArgFile->GetFound() && pArgFile->GetValid()) { bool bWaitFor = (pArgWaitFor->GetFound()); CMIUtilString file; pArgFile->GetExpectedOption(file); target.AttachToProcessWithName(listener, file.c_str(), bWaitFor, error); } else { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_ATTACH_BAD_ARGS), m_cmdData.strMiCmd.c_str())); return MIstatus::failure; } lldb::SBStream errMsg; if (error.Fail()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_ATTACH_FAILED), m_cmdData.strMiCmd.c_str(), errMsg.GetData())); return MIstatus::failure; } return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record // Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdTargetAttach::Acknowledge() { const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); m_miResultRecord = miRecordResult; CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); lldb::pid_t pid = rSessionInfo.GetProcess().GetProcessID(); // Prod the client i.e. Eclipse with out-of-band results to help it 'continue' // because it is using LLDB debugger // Give the client '=thread-group-started,id="i1"' m_bHasResultRecordExtra = true; const CMICmnMIValueConst miValueConst2("i1"); const CMICmnMIValueResult miValueResult2("id", miValueConst2); const CMIUtilString strPid(CMIUtilString::Format("%lld", pid)); const CMICmnMIValueConst miValueConst(strPid); const CMICmnMIValueResult miValueResult("pid", miValueConst); CMICmnMIOutOfBandRecord miOutOfBand( CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupStarted, miValueResult2); miOutOfBand.Add(miValueResult); m_miResultRecordExtra = miOutOfBand.GetString(); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The // factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase *CMICmdCmdTargetAttach::CreateSelf() { return new CMICmdCmdTargetAttach(); } //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdTargetDetach constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdTargetDetach::CMICmdCmdTargetDetach() { // Command factory matches this name with that received from the stdin stream m_strMiCmd = "target-detach"; // Required by the CMICmdFactory when registering *this command m_pSelfCreatorFn = &CMICmdCmdTargetDetach::CreateSelf; } //++ //------------------------------------------------------------------------------------ // Details: CMICmdCmdTargetDetach destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmdCmdTargetDetach::~CMICmdCmdTargetDetach() {} //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The parses the command line // options // arguments to extract values for each of those arguments. // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdTargetDetach::ParseArgs() { return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command does work in this // function. // The command is likely to communicate with the LLDB SBDebugger in // here. // Synopsis: -target-attach file // Ref: // http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Target-Manipulation.html#GDB_002fMI-Target-Manipulation // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdTargetDetach::Execute() { CMICmnLLDBDebugSessionInfo &rSessionInfo( CMICmnLLDBDebugSessionInfo::Instance()); lldb::SBProcess process = rSessionInfo.GetProcess(); if (!process.IsValid()) { SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS), m_cmdData.strMiCmd.c_str())); return MIstatus::failure; } process.Detach(); return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: The invoker requires this function. The command prepares a MI Record // Result // for the work carried out in the Execute(). // Type: Overridden. // Args: None. // Return: MIstatus::success - Functional succeeded. // MIstatus::failure - Functional failed. // Throws: None. //-- bool CMICmdCmdTargetDetach::Acknowledge() { const CMICmnMIResultRecord miRecordResult( m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); m_miResultRecord = miRecordResult; return MIstatus::success; } //++ //------------------------------------------------------------------------------------ // Details: Required by the CMICmdFactory when registering *this command. The // factory // calls this function to create an instance of *this command. // Type: Static method. // Args: None. // Return: CMICmdBase * - Pointer to a new command. // Throws: None. //-- CMICmdBase *CMICmdCmdTargetDetach::CreateSelf() { return new CMICmdCmdTargetDetach(); } Index: vendor/lldb/dist/tools/lldb-mi/MICmnMIOutOfBandRecord.cpp =================================================================== --- vendor/lldb/dist/tools/lldb-mi/MICmnMIOutOfBandRecord.cpp (revision 311541) +++ vendor/lldb/dist/tools/lldb-mi/MICmnMIOutOfBandRecord.cpp (revision 311542) @@ -1,201 +1,209 @@ //===-- MICmnMIOutOfBandRecord.cpp ------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Third Party Headers: #include // In-house headers: #include "MICmnMIOutOfBandRecord.h" #include "MICmnResources.h" // Instantiations: static const char * MapOutOfBandToText(CMICmnMIOutOfBandRecord::OutOfBand_e veType) { switch (veType) { case CMICmnMIOutOfBandRecord::eOutOfBand_Running: return "running"; case CMICmnMIOutOfBandRecord::eOutOfBand_Stopped: return "stopped"; case CMICmnMIOutOfBandRecord::eOutOfBand_BreakPointCreated: return "breakpoint-created"; case CMICmnMIOutOfBandRecord::eOutOfBand_BreakPointModified: return "breakpoint-modified"; case CMICmnMIOutOfBandRecord::eOutOfBand_Thread: return ""; // "" Meant to be empty case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupAdded: return "thread-group-added"; case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupExited: return "thread-group-exited"; case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupRemoved: return "thread-group-removed"; case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupStarted: return "thread-group-started"; case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadCreated: return "thread-created"; case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadExited: return "thread-exited"; case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadSelected: return "thread-selected"; case CMICmnMIOutOfBandRecord::eOutOfBand_TargetModuleLoaded: return "library-loaded"; case CMICmnMIOutOfBandRecord::eOutOfBand_TargetModuleUnloaded: return "library-unloaded"; case CMICmnMIOutOfBandRecord::eOutOfBand_TargetStreamOutput: return ""; + case CMICmnMIOutOfBandRecord::eOutOfBand_ConsoleStreamOutput: + return ""; + case CMICmnMIOutOfBandRecord::eOutOfBand_LogStreamOutput: + return ""; } assert(false && "unknown CMICmnMIOutofBandRecord::OutOfBand_e"); return NULL; } static const char * MapOutOfBandToToken(CMICmnMIOutOfBandRecord::OutOfBand_e veType) { switch (veType) { case CMICmnMIOutOfBandRecord::eOutOfBand_Running: return "*"; case CMICmnMIOutOfBandRecord::eOutOfBand_Stopped: return "*"; case CMICmnMIOutOfBandRecord::eOutOfBand_BreakPointCreated: return "="; case CMICmnMIOutOfBandRecord::eOutOfBand_BreakPointModified: return "="; case CMICmnMIOutOfBandRecord::eOutOfBand_Thread: return "@"; case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupAdded: return "="; case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupExited: return "="; case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupRemoved: return "="; case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupStarted: return "="; case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadCreated: return "="; case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadExited: return "="; case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadSelected: return "="; case CMICmnMIOutOfBandRecord::eOutOfBand_TargetModuleLoaded: return "="; case CMICmnMIOutOfBandRecord::eOutOfBand_TargetModuleUnloaded: return "="; case CMICmnMIOutOfBandRecord::eOutOfBand_TargetStreamOutput: return "@"; + case CMICmnMIOutOfBandRecord::eOutOfBand_ConsoleStreamOutput: + return "~"; + case CMICmnMIOutOfBandRecord::eOutOfBand_LogStreamOutput: + return "&"; } assert(false && "unknown CMICmnMIOutofBandRecord::OutOfBand_e"); return NULL; } //++ //------------------------------------------------------------------------------------ // Details: Build the Out-of-band record's mandatory data part. The part up to // the first // (additional) result i.e. async-record ==> "*" type. // Args: veType - (R) A MI Out-of-Band enumeration. // Return: CMIUtilString - The async record text. // Throws: None. //-- static CMIUtilString BuildAsyncRecord(CMICmnMIOutOfBandRecord::OutOfBand_e veType) { return CMIUtilString::Format("%s%s", MapOutOfBandToToken(veType), MapOutOfBandToText(veType)); } //++ //------------------------------------------------------------------------------------ // Details: CMICmnMIOutOfBandRecord constructor. // Type: Method. // Args: None. // Return: None. // Throws: None. //-- CMICmnMIOutOfBandRecord::CMICmnMIOutOfBandRecord() : m_strAsyncRecord(MIRSRC(IDS_CMD_ERR_EVENT_HANDLED_BUT_NO_ACTION)) {} //++ //------------------------------------------------------------------------------------ // Details: CMICmnMIOutOfBandRecord constructor. // Type: Method. // Args: veType - (R) A MI Out-of-Bound enumeration. // Return: None. // Throws: None. //-- CMICmnMIOutOfBandRecord::CMICmnMIOutOfBandRecord(OutOfBand_e veType) : m_strAsyncRecord(BuildAsyncRecord(veType)) {} //++ //------------------------------------------------------------------------------------ // Details: CMICmnMIOutOfBandRecord constructor. // Type: Method. // Args: veType - (R) A MI Out-of-Bound enumeration. // vConst - (R) A MI const object. // Return: None. // Throws: None. //-- CMICmnMIOutOfBandRecord::CMICmnMIOutOfBandRecord( OutOfBand_e veType, const CMICmnMIValueConst &vConst) : m_strAsyncRecord(BuildAsyncRecord(veType)) { m_strAsyncRecord += vConst.GetString(); } //++ //------------------------------------------------------------------------------------ // Details: CMICmnMIOutOfBandRecord constructor. // Type: Method. // Args: veType - (R) A MI Out-of-Bound enumeration. // vResult - (R) A MI result object. // Return: None. // Throws: None. //-- CMICmnMIOutOfBandRecord::CMICmnMIOutOfBandRecord( OutOfBand_e veType, const CMICmnMIValueResult &vResult) : m_strAsyncRecord(BuildAsyncRecord(veType)) { Add(vResult); } //++ //------------------------------------------------------------------------------------ // Details: CMICmnMIOutOfBandRecord destructor. // Type: Overrideable. // Args: None. // Return: None. // Throws: None. //-- CMICmnMIOutOfBandRecord::~CMICmnMIOutOfBandRecord() {} //++ //------------------------------------------------------------------------------------ // Details: Return the MI Out-of-band record as a string. The string is a direct // result of // work done on *this Out-of-band record so if not enough data is added // then it is // possible to return a malformed Out-of-band record. If nothing has // been set or // added to *this MI Out-of-band record object then text "" // will be returned. // Type: Method. // Args: None. // Return: CMIUtilString & - MI output text. // Throws: None. //-- const CMIUtilString &CMICmnMIOutOfBandRecord::GetString() const { return m_strAsyncRecord; } //++ //------------------------------------------------------------------------------------ // Details: Add to *this Out-of-band record additional information. // Type: Method. // Args: vResult - (R) A MI result object. // Return: None. // Throws: None. //-- void CMICmnMIOutOfBandRecord::Add(const CMICmnMIValueResult &vResult) { m_strAsyncRecord += ","; m_strAsyncRecord += vResult.GetString(); } Index: vendor/lldb/dist/tools/lldb-mi/MICmnMIOutOfBandRecord.h =================================================================== --- vendor/lldb/dist/tools/lldb-mi/MICmnMIOutOfBandRecord.h (revision 311541) +++ vendor/lldb/dist/tools/lldb-mi/MICmnMIOutOfBandRecord.h (revision 311542) @@ -1,92 +1,94 @@ //===-- MICmnMIOutOfBandRecord.h --------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #pragma once // In-house headers: #include "MICmnBase.h" #include "MICmnMIValueConst.h" #include "MICmnMIValueResult.h" #include "MIUtilString.h" //++ //============================================================================ // Details: MI common code MI Out-of-band (Async) Record class. A class that // encapsulates // MI result record data and the forming/format of data added to it. // Out-of-band records are used to notify the GDB/MI client of // additional // changes that have occurred. Those changes can either be a // consequence // of GDB/MI (e.g., a breakpoint modified) or a result of target // activity // (e.g., target stopped). // The syntax is as follows: // "*" type ( "," result )* // type ==> running | stopped // // The Out-of-band record can be retrieve at any time *this object is // instantiated so unless work is done on *this Out-of-band record then // it is // possible to return a malformed Out-of-band record. If nothing has // been set // or added to *this MI Out-of-band record object then text "" // will // be returned. // // More information see: // http://ftp.gnu.org/old-gnu/Manuals/gdb-5.1.1/html_chapter/gdb_22.html// //-- class CMICmnMIOutOfBandRecord : public CMICmnBase { // Enumerations: public: //++ // Details: Enumeration of the type of Out-of-band for *this Out-of-band // record //-- enum OutOfBand_e { eOutOfBand_Running = 0, eOutOfBand_Stopped, eOutOfBand_BreakPointCreated, eOutOfBand_BreakPointModified, eOutOfBand_Thread, eOutOfBand_ThreadGroupAdded, eOutOfBand_ThreadGroupExited, eOutOfBand_ThreadGroupRemoved, eOutOfBand_ThreadGroupStarted, eOutOfBand_ThreadCreated, eOutOfBand_ThreadExited, eOutOfBand_ThreadSelected, eOutOfBand_TargetModuleLoaded, eOutOfBand_TargetModuleUnloaded, - eOutOfBand_TargetStreamOutput + eOutOfBand_TargetStreamOutput, + eOutOfBand_ConsoleStreamOutput, + eOutOfBand_LogStreamOutput }; // Methods: public: /* ctor */ CMICmnMIOutOfBandRecord(); /* ctor */ CMICmnMIOutOfBandRecord(OutOfBand_e veType); /* ctor */ CMICmnMIOutOfBandRecord(OutOfBand_e veType, const CMICmnMIValueConst &vConst); /* ctor */ CMICmnMIOutOfBandRecord(OutOfBand_e veType, const CMICmnMIValueResult &vResult); // const CMIUtilString &GetString() const; void Add(const CMICmnMIValueResult &vResult); // Overridden: public: // From CMICmnBase /* dtor */ ~CMICmnMIOutOfBandRecord() override; // Attributes: private: CMIUtilString m_strAsyncRecord; // Holds the text version of the result record to date }; Index: vendor/lldb/dist/tools/lldb-perf/lib/Results.cpp =================================================================== --- vendor/lldb/dist/tools/lldb-perf/lib/Results.cpp (revision 311541) +++ vendor/lldb/dist/tools/lldb-perf/lib/Results.cpp (revision 311542) @@ -1,239 +1,237 @@ //===-- Results.cpp ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "Results.h" #include #ifdef __APPLE__ #include "CFCMutableArray.h" #include "CFCMutableDictionary.h" #include "CFCReleaser.h" #include "CFCString.h" #endif using namespace lldb_perf; static void AddResultToArray(CFCMutableArray &array, Results::Result *result); static void AddResultToDictionary(CFCMutableDictionary &parent_dict, const char *key, Results::Result *result); static void AddResultToArray(CFCMutableArray &parent_array, Results::Result *result) { switch (result->GetType()) { case Results::Result::Type::Invalid: break; case Results::Result::Type::Array: { Results::Array *value = result->GetAsArray(); CFCMutableArray array; value->ForEach([&array](const Results::ResultSP &value_sp) -> bool { AddResultToArray(array, value_sp.get()); return true; }); parent_array.AppendValue(array.get(), true); } break; case Results::Result::Type::Dictionary: { Results::Dictionary *value = result->GetAsDictionary(); CFCMutableDictionary dict; value->ForEach([&dict](const std::string &key, const Results::ResultSP &value_sp) -> bool { AddResultToDictionary(dict, key.c_str(), value_sp.get()); return true; }); if (result->GetDescription()) { dict.AddValueCString(CFSTR("description"), result->GetDescription()); } parent_array.AppendValue(dict.get(), true); } break; case Results::Result::Type::Double: { double d = result->GetAsDouble()->GetValue(); CFCReleaser cf_number( ::CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &d)); if (cf_number.get()) parent_array.AppendValue(cf_number.get(), true); } break; case Results::Result::Type::String: { CFCString cfstr(result->GetAsString()->GetValue()); if (cfstr.get()) parent_array.AppendValue(cfstr.get(), true); } break; case Results::Result::Type::Unsigned: { uint64_t uval64 = result->GetAsUnsigned()->GetValue(); CFCReleaser cf_number( ::CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &uval64)); if (cf_number.get()) parent_array.AppendValue(cf_number.get(), true); } break; default: - assert(!"unhandled result"); - break; + llvm_unreachable("unhandled result"); } } static void AddResultToDictionary(CFCMutableDictionary &parent_dict, const char *key, Results::Result *result) { assert(key && key[0]); CFCString cf_key(key); switch (result->GetType()) { case Results::Result::Type::Invalid: break; case Results::Result::Type::Array: { Results::Array *value = result->GetAsArray(); CFCMutableArray array; value->ForEach([&array](const Results::ResultSP &value_sp) -> bool { AddResultToArray(array, value_sp.get()); return true; }); parent_dict.AddValue(cf_key.get(), array.get(), true); } break; case Results::Result::Type::Dictionary: { Results::Dictionary *value = result->GetAsDictionary(); CFCMutableDictionary dict; value->ForEach([&dict](const std::string &key, const Results::ResultSP &value_sp) -> bool { AddResultToDictionary(dict, key.c_str(), value_sp.get()); return true; }); if (result->GetDescription()) { dict.AddValueCString(CFSTR("description"), result->GetDescription()); } parent_dict.AddValue(cf_key.get(), dict.get(), true); } break; case Results::Result::Type::Double: { parent_dict.SetValueDouble(cf_key.get(), result->GetAsDouble()->GetValue(), true); } break; case Results::Result::Type::String: { parent_dict.SetValueCString(cf_key.get(), result->GetAsString()->GetValue(), true); } break; case Results::Result::Type::Unsigned: { parent_dict.SetValueUInt64(cf_key.get(), result->GetAsUnsigned()->GetValue(), true); } break; default: - assert(!"unhandled result"); - break; + llvm_unreachable("unhandled result"); } } void Results::Write(const char *out_path) { #ifdef __APPLE__ CFCMutableDictionary dict; m_results.ForEach( [&dict](const std::string &key, const ResultSP &value_sp) -> bool { AddResultToDictionary(dict, key.c_str(), value_sp.get()); return true; }); CFDataRef xmlData = CFPropertyListCreateData( kCFAllocatorDefault, dict.get(), kCFPropertyListXMLFormat_v1_0, 0, NULL); if (out_path == NULL) out_path = "/dev/stdout"; CFURLRef file = CFURLCreateFromFileSystemRepresentation( NULL, (const UInt8 *)out_path, strlen(out_path), FALSE); CFURLWriteDataAndPropertiesToResource(file, xmlData, NULL, NULL); #endif } Results::ResultSP Results::Dictionary::AddUnsigned(const char *name, const char *description, uint64_t value) { assert(name && name[0]); if (description && description[0]) { std::unique_ptr value_dict_ap( new Results::Dictionary()); value_dict_ap->AddString("description", NULL, description); value_dict_ap->AddUnsigned("value", NULL, value); m_dictionary[std::string(name)] = ResultSP(value_dict_ap.release()); } else m_dictionary[std::string(name)] = ResultSP(new Unsigned(name, description, value)); return m_dictionary[std::string(name)]; } Results::ResultSP Results::Dictionary::AddDouble(const char *name, const char *description, double value) { assert(name && name[0]); if (description && description[0]) { std::unique_ptr value_dict_ap( new Results::Dictionary()); value_dict_ap->AddString("description", NULL, description); value_dict_ap->AddDouble("value", NULL, value); m_dictionary[std::string(name)] = ResultSP(value_dict_ap.release()); } else m_dictionary[std::string(name)] = ResultSP(new Double(name, description, value)); return m_dictionary[std::string(name)]; } Results::ResultSP Results::Dictionary::AddString(const char *name, const char *description, const char *value) { assert(name && name[0]); if (description && description[0]) { std::unique_ptr value_dict_ap( new Results::Dictionary()); value_dict_ap->AddString("description", NULL, description); value_dict_ap->AddString("value", NULL, value); m_dictionary[std::string(name)] = ResultSP(value_dict_ap.release()); } else m_dictionary[std::string(name)] = ResultSP(new String(name, description, value)); return m_dictionary[std::string(name)]; } Results::ResultSP Results::Dictionary::Add(const char *name, const char *description, const ResultSP &result_sp) { assert(name && name[0]); if (description && description[0]) { std::unique_ptr value_dict_ap( new Results::Dictionary()); value_dict_ap->AddString("description", NULL, description); value_dict_ap->Add("value", NULL, result_sp); m_dictionary[std::string(name)] = ResultSP(value_dict_ap.release()); } else m_dictionary[std::string(name)] = result_sp; return m_dictionary[std::string(name)]; } void Results::Dictionary::ForEach( const std::function &callback) { collection::const_iterator pos, end = m_dictionary.end(); for (pos = m_dictionary.begin(); pos != end; ++pos) { if (callback(pos->first.c_str(), pos->second) == false) return; } } Results::ResultSP Results::Array::Append(const ResultSP &result_sp) { m_array.push_back(result_sp); return result_sp; } void Results::Array::ForEach( const std::function &callback) { collection::const_iterator pos, end = m_array.end(); for (pos = m_array.begin(); pos != end; ++pos) { if (callback(*pos) == false) return; } } Index: vendor/lldb/dist/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp =================================================================== --- vendor/lldb/dist/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp (revision 311541) +++ vendor/lldb/dist/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp (revision 311542) @@ -1,307 +1,315 @@ //===-- GDBRemoteCommunicationClientTest.cpp --------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) // Workaround for MSVC standard library bug, which fails to include // when // exceptions are disabled. #include #endif #include #include "GDBRemoteTestUtils.h" #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h" #include "lldb/Core/DataBuffer.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/StructuredData.h" #include "llvm/ADT/ArrayRef.h" using namespace lldb_private::process_gdb_remote; using namespace lldb_private; using namespace lldb; using namespace llvm; namespace { typedef GDBRemoteCommunication::PacketResult PacketResult; struct TestClient : public GDBRemoteCommunicationClient { TestClient() { m_send_acks = false; } }; void Handle_QThreadSuffixSupported(MockServer &server, bool supported) { StringExtractorGDBRemote request; ASSERT_EQ(PacketResult::Success, server.GetPacket(request)); ASSERT_EQ("QThreadSuffixSupported", request.GetStringRef()); if (supported) ASSERT_EQ(PacketResult::Success, server.SendOKResponse()); else ASSERT_EQ(PacketResult::Success, server.SendUnimplementedResponse(nullptr)); } void HandlePacket(MockServer &server, StringRef expected, StringRef response) { StringExtractorGDBRemote request; ASSERT_EQ(PacketResult::Success, server.GetPacket(request)); ASSERT_EQ(expected, request.GetStringRef()); ASSERT_EQ(PacketResult::Success, server.SendPacket(response)); } uint8_t all_registers[] = {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'}; std::string all_registers_hex = "404142434445464748494a4b4c4d4e4f"; uint8_t one_register[] = {'A', 'B', 'C', 'D'}; std::string one_register_hex = "41424344"; } // end anonymous namespace class GDBRemoteCommunicationClientTest : public GDBRemoteTest {}; TEST_F(GDBRemoteCommunicationClientTest, WriteRegister) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; const uint32_t reg_num = 4; std::future write_result = std::async(std::launch::async, [&] { return client.WriteRegister(tid, reg_num, one_register); }); Handle_QThreadSuffixSupported(server, true); HandlePacket(server, "P4=" + one_register_hex + ";thread:0047;", "OK"); ASSERT_TRUE(write_result.get()); write_result = std::async(std::launch::async, [&] { return client.WriteAllRegisters(tid, all_registers); }); HandlePacket(server, "G" + all_registers_hex + ";thread:0047;", "OK"); ASSERT_TRUE(write_result.get()); } TEST_F(GDBRemoteCommunicationClientTest, WriteRegisterNoSuffix) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; const uint32_t reg_num = 4; std::future write_result = std::async(std::launch::async, [&] { return client.WriteRegister(tid, reg_num, one_register); }); Handle_QThreadSuffixSupported(server, false); HandlePacket(server, "Hg47", "OK"); HandlePacket(server, "P4=" + one_register_hex, "OK"); ASSERT_TRUE(write_result.get()); write_result = std::async(std::launch::async, [&] { return client.WriteAllRegisters(tid, all_registers); }); HandlePacket(server, "G" + all_registers_hex, "OK"); ASSERT_TRUE(write_result.get()); } TEST_F(GDBRemoteCommunicationClientTest, ReadRegister) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; const uint32_t reg_num = 4; std::future async_result = std::async( std::launch::async, [&] { return client.GetpPacketSupported(tid); }); Handle_QThreadSuffixSupported(server, true); HandlePacket(server, "p0;thread:0047;", one_register_hex); ASSERT_TRUE(async_result.get()); std::future read_result = std::async( std::launch::async, [&] { return client.ReadRegister(tid, reg_num); }); HandlePacket(server, "p4;thread:0047;", "41424344"); auto buffer_sp = read_result.get(); ASSERT_TRUE(bool(buffer_sp)); ASSERT_EQ(0, memcmp(buffer_sp->GetBytes(), one_register, sizeof one_register)); read_result = std::async(std::launch::async, [&] { return client.ReadAllRegisters(tid); }); HandlePacket(server, "g;thread:0047;", all_registers_hex); buffer_sp = read_result.get(); ASSERT_TRUE(bool(buffer_sp)); ASSERT_EQ(0, memcmp(buffer_sp->GetBytes(), all_registers, sizeof all_registers)); } TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; uint32_t save_id; std::future async_result = std::async(std::launch::async, [&] { return client.SaveRegisterState(tid, save_id); }); Handle_QThreadSuffixSupported(server, false); HandlePacket(server, "Hg47", "OK"); HandlePacket(server, "QSaveRegisterState", "1"); ASSERT_TRUE(async_result.get()); EXPECT_EQ(1u, save_id); async_result = std::async(std::launch::async, [&] { return client.RestoreRegisterState(tid, save_id); }); HandlePacket(server, "QRestoreRegisterState:1", "OK"); ASSERT_TRUE(async_result.get()); } TEST_F(GDBRemoteCommunicationClientTest, SyncThreadState) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; const lldb::tid_t tid = 0x47; std::future async_result = std::async( std::launch::async, [&] { return client.SyncThreadState(tid); }); HandlePacket(server, "qSyncThreadStateSupported", "OK"); HandlePacket(server, "QSyncThreadState:0047;", "OK"); ASSERT_TRUE(async_result.get()); } TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; llvm::Triple triple("i386-pc-linux"); FileSpec file_specs[] = { FileSpec("/foo/bar.so", false, FileSpec::ePathSyntaxPosix), - FileSpec("/foo/baz.so", false, FileSpec::ePathSyntaxPosix)}; + FileSpec("/foo/baz.so", false, FileSpec::ePathSyntaxPosix), + + // This is a bit dodgy but we currently depend on GetModulesInfo not + // performing denormalization. It can go away once the users + // (DynamicLoaderPOSIXDYLD, at least) correctly set the path syntax for + // the FileSpecs they create. + FileSpec("/foo/baw.so", false, FileSpec::ePathSyntaxWindows), + }; std::future>> async_result = std::async(std::launch::async, [&] { return client.GetModulesInfo(file_specs, triple); }); HandlePacket( server, "jModulesInfo:[" R"({"file":"/foo/bar.so","triple":"i386-pc-linux"},)" - R"({"file":"/foo/baz.so","triple":"i386-pc-linux"}])", + R"({"file":"/foo/baz.so","triple":"i386-pc-linux"},)" + R"({"file":"/foo/baw.so","triple":"i386-pc-linux"}])", R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])"); auto result = async_result.get(); ASSERT_TRUE(result.hasValue()); ASSERT_EQ(1u, result->size()); EXPECT_EQ("/foo/bar.so", result.getValue()[0].GetFileSpec().GetPath()); EXPECT_EQ(triple, result.getValue()[0].GetArchitecture().GetTriple()); EXPECT_EQ(UUID("@ABCDEFGHIJKLMNO", 16), result.getValue()[0].GetUUID()); EXPECT_EQ(0u, result.getValue()[0].GetObjectOffset()); EXPECT_EQ(1234u, result.getValue()[0].GetObjectSize()); } TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; llvm::Triple triple("i386-pc-linux"); FileSpec file_spec("/foo/bar.so", false, FileSpec::ePathSyntaxPosix); const char *invalid_responses[] = { "OK", "E47", "[]", // no UUID R"([{"triple":"i386-pc-linux",)" R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}])", // no triple R"([{"uuid":"404142434445464748494a4b4c4d4e4f",)" R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}])", // no file_path R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" R"("file_offset":0,"file_size":1234}])", // no file_offset R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" R"("file_path":"/foo/bar.so","file_size":1234}])", // no file_size R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)" R"("file_path":"/foo/bar.so","file_offset":0}])", }; for (const char *response : invalid_responses) { std::future>> async_result = std::async(std::launch::async, [&] { return client.GetModulesInfo(file_spec, triple); }); HandlePacket( server, R"(jModulesInfo:[{"file":"/foo/bar.so","triple":"i386-pc-linux"}])", response); ASSERT_FALSE(async_result.get().hasValue()) << "response was: " << response; } } TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) { TestClient client; MockServer server; Connect(client, server); if (HasFailure()) return; std::thread server_thread([&server] { for (;;) { StringExtractorGDBRemote request; PacketResult result = server.GetPacket(request); if (result == PacketResult::ErrorDisconnected) return; ASSERT_EQ(PacketResult::Success, result); StringRef ref = request.GetStringRef(); ASSERT_TRUE(ref.consume_front("qSpeedTest:response_size:")); int size; ASSERT_FALSE(ref.consumeInteger(10, size)) << "ref: " << ref; std::string response(size, 'X'); ASSERT_EQ(PacketResult::Success, server.SendPacket(response)); } }); StreamString ss; client.TestPacketSpeed(10, 32, 32, 4096, true, ss); client.Disconnect(); server_thread.join(); auto object_sp = StructuredData::ParseJSON(ss.GetString()); ASSERT_TRUE(bool(object_sp)); auto dict_sp = object_sp->GetAsDictionary(); ASSERT_TRUE(bool(dict_sp)); object_sp = dict_sp->GetValueForKey("packet_speeds"); ASSERT_TRUE(bool(object_sp)); dict_sp = object_sp->GetAsDictionary(); ASSERT_TRUE(bool(dict_sp)); int num_packets; ASSERT_TRUE(dict_sp->GetValueForKeyAsInteger("num_packets", num_packets)) << ss.GetString(); ASSERT_EQ(10, num_packets); }