diff --git a/debuginfo-tests/llvm-prettyprinters/gdb/llvm-support.cpp b/debuginfo-tests/llvm-prettyprinters/gdb/llvm-support.cpp index a6e535b56833..54c26dc14236 100644 --- a/debuginfo-tests/llvm-prettyprinters/gdb/llvm-support.cpp +++ b/debuginfo-tests/llvm-prettyprinters/gdb/llvm-support.cpp @@ -1,65 +1,67 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/Twine.h" #include "llvm/ADT/ilist.h" #include "llvm/Support/Error.h" int Array[] = {1, 2, 3}; auto IntPtr = reinterpret_cast(0xabc); llvm::ArrayRef ArrayRef(Array); llvm::MutableArrayRef MutableArrayRef(Array); llvm::DenseMap DenseMap = {{4, 5}, {6, 7}}; +llvm::StringMap StringMap = {{"foo", 123}, {"bar", 456}}; llvm::Expected ExpectedValue(8); llvm::Expected ExpectedError(llvm::createStringError({}, "")); llvm::Optional OptionalValue(9); llvm::Optional OptionalNone(llvm::None); llvm::SmallVector SmallVector = {10, 11, 12}; llvm::SmallString<5> SmallString("foo"); llvm::StringRef StringRef = "bar"; llvm::Twine Twine = llvm::Twine(SmallString) + StringRef; llvm::PointerIntPair PointerIntPair(IntPtr, 1); struct alignas(8) Z {}; llvm::PointerUnion PointerUnion(IntPtr); // No members which instantiate PointerUnionUIntTraits (e.g. get()) // are called, and this instance will therefore be raw-printed. llvm::PointerUnion RawPrintingPointerUnion(nullptr); using IlistTag = llvm::ilist_tag; using SimpleIlistTag = llvm::ilist_tag; struct IlistNode : llvm::ilist_node, llvm::ilist_node { int Value; }; auto Ilist = [] { llvm::ilist Result; for (int I : {13, 14, 15}) { Result.push_back(new IlistNode); Result.back().Value = I; } return Result; }(); auto SimpleIlist = []() { llvm::simple_ilist Result; for (auto &Node : Ilist) Result.push_front(Node); return Result; }(); int main() { // Reference symbols that might otherwise be stripped. ArrayRef[0]; MutableArrayRef[0]; !ExpectedValue; !ExpectedError; *OptionalValue; *OptionalNone; return 0; } diff --git a/debuginfo-tests/llvm-prettyprinters/gdb/llvm-support.gdb b/debuginfo-tests/llvm-prettyprinters/gdb/llvm-support.gdb index 6ae1c7016b68..b07eabf32867 100644 --- a/debuginfo-tests/llvm-prettyprinters/gdb/llvm-support.gdb +++ b/debuginfo-tests/llvm-prettyprinters/gdb/llvm-support.gdb @@ -1,133 +1,136 @@ # RUN: gdb -q -batch -n -iex 'source %llvm_src_root/utils/gdb-scripts/prettyprinters.py' -x %s %llvm_tools_dir/check-gdb-llvm-support | FileCheck %s # REQUIRES: debug-info break main run # CHECK: llvm::ArrayRef of length 3 = {1, 2, 3} p ArrayRef # CHECK: llvm::ArrayRef of length 3 = {1, 2, 3} p MutableArrayRef # CHECK: llvm::DenseMap with 2 elements = { # CHECK: [4] = 5, # CHECK: [6] = 7, # CHECK: } p DenseMap # CHECK: llvm::Expected = {value = 8} p ExpectedValue # CHECK: llvm::Expected is error p ExpectedError # CHECK: llvm::Optional = {value = 9} p OptionalValue # CHECK: llvm::Optional is not initialized p OptionalNone # CHECK: llvm::SmallVector of Size 3, Capacity 5 = {10, 11, 12} p SmallVector # CHECK: "foo" p SmallString # CHECK: "bar" p StringRef # CHECK: "\"foo\"\"bar\"" p Twine +# CHECK: llvm::StringMap with 2 elements = {["foo"] = 123, ["bar"] = 456} +p StringMap + # CHECK: {pointer = 0xabc, value = 1} p PointerIntPair # CHECK: Containing int * = {pointer = 0xabc} p PointerUnion # CHECK: PointerUnionMembers, p RawPrintingPointerUnion # Switch to print pretty adds newlines to the following statements. set print pretty # CHECK: { # CHECK: [0] = { # CHECK: >> = { # CHECK: prev = [[Ilist_Sentinel:0x.*]] , # CHECK: next = [[Node_14:0x.*]] # CHECK: }, # CHECK: >> = { # CHECK: prev = [[Node_14]], # CHECK: next = [[SimpleIlist_Sentinel:0x.*]] # CHECK: }, # CHECK: members of IlistNode: # CHECK: Value = 13 # CHECK: }, # CHECK: [1] = { # CHECK: >> = { # CHECK: prev = [[Node_13:0x.*]], # CHECK: next = [[Node_15:0x.*]] # CHECK: }, # CHECK: >> = { # CHECK: prev = [[Node_15]], # CHECK: next = [[Node_13]] # CHECK: }, # CHECK: members of IlistNode: # CHECK: Value = 14 # CHECK: }, # CHECK: [2] = { # CHECK: >> = { # CHECK: prev = [[Node_14]], # CHECK: next = [[Ilist_Sentinel]] # CHECK: }, # CHECK: >> = { # CHECK: prev = [[SimpleIlist_Sentinel]] , # CHECK: next = [[Node_14]] # CHECK: }, # CHECK: members of IlistNode: # CHECK: Value = 15 # CHECK: } # CHECK: } p Ilist # CHECK: { # CHECK: [0] = { # CHECK: >> = { # CHECK: prev = [[Node_14]], # CHECK: next = [[Ilist_Sentinel]] # CHECK: }, # CHECK: >> = { # CHECK: prev = [[SimpleIlist_Sentinel]] , # CHECK: next = [[Node_14]] # CHECK: }, # CHECK: members of IlistNode: # CHECK: Value = 15 # CHECK: }, # CHECK: [1] = { # CHECK: >> = { # CHECK: prev = [[Node_13]], # CHECK: next = [[Node_15]] # CHECK: }, # CHECK: >> = { # CHECK: prev = [[Node_15]], # CHECK: next = [[Node_13]] # CHECK: }, # CHECK: members of IlistNode: # CHECK: Value = 14 # CHECK: }, # CHECK: [2] = { # CHECK: >> = { # CHECK: prev = [[Ilist_Sentinel]] , # CHECK: next = [[Node_14]] # CHECK: }, # CHECK: >> = { # CHECK: prev = [[Node_14]], # CHECK: next = [[SimpleIlist_Sentinel]] # CHECK: }, # CHECK: members of IlistNode: # CHECK: Value = 13 # CHECK: } # CHECK: } p SimpleIlist diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h index 840f328db796..e7180b02a9c8 100644 --- a/llvm/include/llvm/ADT/StringMap.h +++ b/llvm/include/llvm/ADT/StringMap.h @@ -1,475 +1,477 @@ //===- StringMap.h - String Hash table map interface ------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the StringMap class. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_STRINGMAP_H #define LLVM_ADT_STRINGMAP_H #include "llvm/ADT/StringMapEntry.h" #include "llvm/Support/AllocatorBase.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include #include namespace llvm { template class StringMapConstIterator; template class StringMapIterator; template class StringMapKeyIterator; /// StringMapImpl - This is the base class of StringMap that is shared among /// all of its instantiations. class StringMapImpl { protected: // Array of NumBuckets pointers to entries, null pointers are holes. // TheTable[NumBuckets] contains a sentinel value for easy iteration. Followed // by an array of the actual hash values as unsigned integers. StringMapEntryBase **TheTable = nullptr; unsigned NumBuckets = 0; unsigned NumItems = 0; unsigned NumTombstones = 0; unsigned ItemSize; protected: explicit StringMapImpl(unsigned itemSize) : ItemSize(itemSize) {} StringMapImpl(StringMapImpl &&RHS) : TheTable(RHS.TheTable), NumBuckets(RHS.NumBuckets), NumItems(RHS.NumItems), NumTombstones(RHS.NumTombstones), ItemSize(RHS.ItemSize) { RHS.TheTable = nullptr; RHS.NumBuckets = 0; RHS.NumItems = 0; RHS.NumTombstones = 0; } StringMapImpl(unsigned InitSize, unsigned ItemSize); unsigned RehashTable(unsigned BucketNo = 0); /// LookupBucketFor - Look up the bucket that the specified string should end /// up in. If it already exists as a key in the map, the Item pointer for the /// specified bucket will be non-null. Otherwise, it will be null. In either /// case, the FullHashValue field of the bucket will be set to the hash value /// of the string. unsigned LookupBucketFor(StringRef Key); /// FindKey - Look up the bucket that contains the specified key. If it exists /// in the map, return the bucket number of the key. Otherwise return -1. /// This does not modify the map. int FindKey(StringRef Key) const; /// RemoveKey - Remove the specified StringMapEntry from the table, but do not /// delete it. This aborts if the value isn't in the table. void RemoveKey(StringMapEntryBase *V); /// RemoveKey - Remove the StringMapEntry for the specified key from the /// table, returning it. If the key is not in the table, this returns null. StringMapEntryBase *RemoveKey(StringRef Key); /// Allocate the table with the specified number of buckets and otherwise /// setup the map as empty. void init(unsigned Size); public: + static constexpr uintptr_t TombstoneIntVal = + static_cast(-1) + << PointerLikeTypeTraits::NumLowBitsAvailable; + static StringMapEntryBase *getTombstoneVal() { - uintptr_t Val = static_cast(-1); - Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; - return reinterpret_cast(Val); + return reinterpret_cast(TombstoneIntVal); } unsigned getNumBuckets() const { return NumBuckets; } unsigned getNumItems() const { return NumItems; } bool empty() const { return NumItems == 0; } unsigned size() const { return NumItems; } void swap(StringMapImpl &Other) { std::swap(TheTable, Other.TheTable); std::swap(NumBuckets, Other.NumBuckets); std::swap(NumItems, Other.NumItems); std::swap(NumTombstones, Other.NumTombstones); } }; /// StringMap - This is an unconventional map that is specialized for handling /// keys that are "strings", which are basically ranges of bytes. This does some /// funky memory allocation and hashing things to make it extremely efficient, /// storing the string data *after* the value in the map. template class StringMap : public StringMapImpl { AllocatorTy Allocator; public: using MapEntryTy = StringMapEntry; StringMap() : StringMapImpl(static_cast(sizeof(MapEntryTy))) {} explicit StringMap(unsigned InitialSize) : StringMapImpl(InitialSize, static_cast(sizeof(MapEntryTy))) {} explicit StringMap(AllocatorTy A) : StringMapImpl(static_cast(sizeof(MapEntryTy))), Allocator(A) { } StringMap(unsigned InitialSize, AllocatorTy A) : StringMapImpl(InitialSize, static_cast(sizeof(MapEntryTy))), Allocator(A) {} StringMap(std::initializer_list> List) : StringMapImpl(List.size(), static_cast(sizeof(MapEntryTy))) { for (const auto &P : List) { insert(P); } } StringMap(StringMap &&RHS) : StringMapImpl(std::move(RHS)), Allocator(std::move(RHS.Allocator)) {} StringMap(const StringMap &RHS) : StringMapImpl(static_cast(sizeof(MapEntryTy))), Allocator(RHS.Allocator) { if (RHS.empty()) return; // Allocate TheTable of the same size as RHS's TheTable, and set the // sentinel appropriately (and NumBuckets). init(RHS.NumBuckets); unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1), *RHSHashTable = (unsigned *)(RHS.TheTable + NumBuckets + 1); NumItems = RHS.NumItems; NumTombstones = RHS.NumTombstones; for (unsigned I = 0, E = NumBuckets; I != E; ++I) { StringMapEntryBase *Bucket = RHS.TheTable[I]; if (!Bucket || Bucket == getTombstoneVal()) { TheTable[I] = Bucket; continue; } TheTable[I] = MapEntryTy::Create( static_cast(Bucket)->getKey(), Allocator, static_cast(Bucket)->getValue()); HashTable[I] = RHSHashTable[I]; } // Note that here we've copied everything from the RHS into this object, // tombstones included. We could, instead, have re-probed for each key to // instantiate this new object without any tombstone buckets. The // assumption here is that items are rarely deleted from most StringMaps, // and so tombstones are rare, so the cost of re-probing for all inputs is // not worthwhile. } StringMap &operator=(StringMap RHS) { StringMapImpl::swap(RHS); std::swap(Allocator, RHS.Allocator); return *this; } ~StringMap() { // Delete all the elements in the map, but don't reset the elements // to default values. This is a copy of clear(), but avoids unnecessary // work not required in the destructor. if (!empty()) { for (unsigned I = 0, E = NumBuckets; I != E; ++I) { StringMapEntryBase *Bucket = TheTable[I]; if (Bucket && Bucket != getTombstoneVal()) { static_cast(Bucket)->Destroy(Allocator); } } } free(TheTable); } AllocatorTy &getAllocator() { return Allocator; } const AllocatorTy &getAllocator() const { return Allocator; } using key_type = const char *; using mapped_type = ValueTy; using value_type = StringMapEntry; using size_type = size_t; using const_iterator = StringMapConstIterator; using iterator = StringMapIterator; iterator begin() { return iterator(TheTable, NumBuckets == 0); } iterator end() { return iterator(TheTable + NumBuckets, true); } const_iterator begin() const { return const_iterator(TheTable, NumBuckets == 0); } const_iterator end() const { return const_iterator(TheTable + NumBuckets, true); } iterator_range> keys() const { return make_range(StringMapKeyIterator(begin()), StringMapKeyIterator(end())); } iterator find(StringRef Key) { int Bucket = FindKey(Key); if (Bucket == -1) return end(); return iterator(TheTable + Bucket, true); } const_iterator find(StringRef Key) const { int Bucket = FindKey(Key); if (Bucket == -1) return end(); return const_iterator(TheTable + Bucket, true); } /// lookup - Return the entry for the specified key, or a default /// constructed value if no such entry exists. ValueTy lookup(StringRef Key) const { const_iterator it = find(Key); if (it != end()) return it->second; return ValueTy(); } /// Lookup the ValueTy for the \p Key, or create a default constructed value /// if the key is not in the map. ValueTy &operator[](StringRef Key) { return try_emplace(Key).first->second; } /// count - Return 1 if the element is in the map, 0 otherwise. size_type count(StringRef Key) const { return find(Key) == end() ? 0 : 1; } template size_type count(const StringMapEntry &MapEntry) const { return count(MapEntry.getKey()); } /// equal - check whether both of the containers are equal. bool operator==(const StringMap &RHS) const { if (size() != RHS.size()) return false; for (const auto &KeyValue : *this) { auto FindInRHS = RHS.find(KeyValue.getKey()); if (FindInRHS == RHS.end()) return false; if (!(KeyValue.getValue() == FindInRHS->getValue())) return false; } return true; } bool operator!=(const StringMap &RHS) const { return !(*this == RHS); } /// insert - Insert the specified key/value pair into the map. If the key /// already exists in the map, return false and ignore the request, otherwise /// insert it and return true. bool insert(MapEntryTy *KeyValue) { unsigned BucketNo = LookupBucketFor(KeyValue->getKey()); StringMapEntryBase *&Bucket = TheTable[BucketNo]; if (Bucket && Bucket != getTombstoneVal()) return false; // Already exists in map. if (Bucket == getTombstoneVal()) --NumTombstones; Bucket = KeyValue; ++NumItems; assert(NumItems + NumTombstones <= NumBuckets); RehashTable(); return true; } /// insert - Inserts the specified key/value pair into the map if the key /// isn't already in the map. The bool component of the returned pair is true /// if and only if the insertion takes place, and the iterator component of /// the pair points to the element with key equivalent to the key of the pair. std::pair insert(std::pair KV) { return try_emplace(KV.first, std::move(KV.second)); } /// Inserts an element or assigns to the current element if the key already /// exists. The return type is the same as try_emplace. template std::pair insert_or_assign(StringRef Key, V &&Val) { auto Ret = try_emplace(Key, std::forward(Val)); if (!Ret.second) Ret.first->second = std::forward(Val); return Ret; } /// Emplace a new element for the specified key into the map if the key isn't /// already in the map. The bool component of the returned pair is true /// if and only if the insertion takes place, and the iterator component of /// the pair points to the element with key equivalent to the key of the pair. template std::pair try_emplace(StringRef Key, ArgsTy &&... Args) { unsigned BucketNo = LookupBucketFor(Key); StringMapEntryBase *&Bucket = TheTable[BucketNo]; if (Bucket && Bucket != getTombstoneVal()) return std::make_pair(iterator(TheTable + BucketNo, false), false); // Already exists in map. if (Bucket == getTombstoneVal()) --NumTombstones; Bucket = MapEntryTy::Create(Key, Allocator, std::forward(Args)...); ++NumItems; assert(NumItems + NumTombstones <= NumBuckets); BucketNo = RehashTable(BucketNo); return std::make_pair(iterator(TheTable + BucketNo, false), true); } // clear - Empties out the StringMap void clear() { if (empty()) return; // Zap all values, resetting the keys back to non-present (not tombstone), // which is safe because we're removing all elements. for (unsigned I = 0, E = NumBuckets; I != E; ++I) { StringMapEntryBase *&Bucket = TheTable[I]; if (Bucket && Bucket != getTombstoneVal()) { static_cast(Bucket)->Destroy(Allocator); } Bucket = nullptr; } NumItems = 0; NumTombstones = 0; } /// remove - Remove the specified key/value pair from the map, but do not /// erase it. This aborts if the key is not in the map. void remove(MapEntryTy *KeyValue) { RemoveKey(KeyValue); } void erase(iterator I) { MapEntryTy &V = *I; remove(&V); V.Destroy(Allocator); } bool erase(StringRef Key) { iterator I = find(Key); if (I == end()) return false; erase(I); return true; } }; template class StringMapIterBase : public iterator_facade_base { protected: StringMapEntryBase **Ptr = nullptr; public: StringMapIterBase() = default; explicit StringMapIterBase(StringMapEntryBase **Bucket, bool NoAdvance = false) : Ptr(Bucket) { if (!NoAdvance) AdvancePastEmptyBuckets(); } DerivedTy &operator=(const DerivedTy &Other) { Ptr = Other.Ptr; return static_cast(*this); } bool operator==(const DerivedTy &RHS) const { return Ptr == RHS.Ptr; } DerivedTy &operator++() { // Preincrement ++Ptr; AdvancePastEmptyBuckets(); return static_cast(*this); } DerivedTy operator++(int) { // Post-increment DerivedTy Tmp(Ptr); ++*this; return Tmp; } private: void AdvancePastEmptyBuckets() { while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal()) ++Ptr; } }; template class StringMapConstIterator : public StringMapIterBase, const StringMapEntry> { using base = StringMapIterBase, const StringMapEntry>; public: StringMapConstIterator() = default; explicit StringMapConstIterator(StringMapEntryBase **Bucket, bool NoAdvance = false) : base(Bucket, NoAdvance) {} const StringMapEntry &operator*() const { return *static_cast *>(*this->Ptr); } }; template class StringMapIterator : public StringMapIterBase, StringMapEntry> { using base = StringMapIterBase, StringMapEntry>; public: StringMapIterator() = default; explicit StringMapIterator(StringMapEntryBase **Bucket, bool NoAdvance = false) : base(Bucket, NoAdvance) {} StringMapEntry &operator*() const { return *static_cast *>(*this->Ptr); } operator StringMapConstIterator() const { return StringMapConstIterator(this->Ptr, true); } }; template class StringMapKeyIterator : public iterator_adaptor_base, StringMapConstIterator, std::forward_iterator_tag, StringRef> { using base = iterator_adaptor_base, StringMapConstIterator, std::forward_iterator_tag, StringRef>; public: StringMapKeyIterator() = default; explicit StringMapKeyIterator(StringMapConstIterator Iter) : base(std::move(Iter)) {} StringRef &operator*() { Key = this->wrapped()->getKey(); return Key; } private: StringRef Key; }; } // end namespace llvm #endif // LLVM_ADT_STRINGMAP_H diff --git a/llvm/utils/gdb-scripts/prettyprinters.py b/llvm/utils/gdb-scripts/prettyprinters.py index 1a61321aa97b..787269c7bb96 100644 --- a/llvm/utils/gdb-scripts/prettyprinters.py +++ b/llvm/utils/gdb-scripts/prettyprinters.py @@ -1,451 +1,491 @@ from __future__ import print_function +import struct import sys import gdb.printing import gdb.types class Iterator: def __iter__(self): return self if sys.version_info.major == 2: def next(self): return self.__next__() def children(self): return self def escape_bytes(val, l): return '"' + val.string(encoding='Latin-1', length=l).encode('unicode_escape').decode() + '"' class SmallStringPrinter: """Print an llvm::SmallString object.""" def __init__(self, val): self.val = val def to_string(self): begin = self.val['BeginX'] return escape_bytes(begin.cast(gdb.lookup_type('char').pointer()), self.val['Size']) class StringRefPrinter: """Print an llvm::StringRef object.""" def __init__(self, val): self.val = val def to_string(self): return escape_bytes(self.val['Data'], self.val['Length']) class SmallVectorPrinter(Iterator): """Print an llvm::SmallVector object.""" def __init__(self, val): self.val = val t = val.type.template_argument(0).pointer() self.begin = val['BeginX'].cast(t) self.size = val['Size'] self.i = 0 def __next__(self): if self.i == self.size: raise StopIteration ret = '[{}]'.format(self.i), (self.begin+self.i).dereference() self.i += 1 return ret def to_string(self): return 'llvm::SmallVector of Size {}, Capacity {}'.format(self.size, self.val['Capacity']) def display_hint (self): return 'array' class ArrayRefPrinter: """Print an llvm::ArrayRef object.""" class _iterator: def __init__(self, begin, end): self.cur = begin self.end = end self.count = 0 def __iter__(self): return self def __next__(self): if self.cur == self.end: raise StopIteration count = self.count self.count = self.count + 1 cur = self.cur self.cur = self.cur + 1 return '[%d]' % count, cur.dereference() if sys.version_info.major == 2: next = __next__ def __init__(self, val): self.val = val def children(self): data = self.val['Data'] return self._iterator(data, data + self.val['Length']) def to_string(self): return 'llvm::ArrayRef of length %d' % (self.val['Length']) def display_hint (self): return 'array' class ExpectedPrinter(Iterator): """Print an llvm::Expected object.""" def __init__(self, val): self.val = val def __next__(self): val = self.val if val is None: raise StopIteration self.val = None if val['HasError']: return ('error', val['ErrorStorage'].address.cast( gdb.lookup_type('llvm::ErrorInfoBase').pointer()).dereference()) return ('value', val['TStorage'].address.cast( val.type.template_argument(0).pointer()).dereference()) def to_string(self): return 'llvm::Expected{}'.format(' is error' if self.val['HasError'] else '') class OptionalPrinter(Iterator): """Print an llvm::Optional object.""" def __init__(self, val): self.val = val def __next__(self): val = self.val if val is None: raise StopIteration self.val = None if not val['Storage']['hasVal']: raise StopIteration return ('value', val['Storage']['value']) def to_string(self): return 'llvm::Optional{}'.format('' if self.val['Storage']['hasVal'] else ' is not initialized') class DenseMapPrinter: "Print a DenseMap" class _iterator: def __init__(self, key_info_t, begin, end): self.key_info_t = key_info_t self.cur = begin self.end = end self.advancePastEmptyBuckets() self.first = True def __iter__(self): return self def advancePastEmptyBuckets(self): # disabled until the comments below can be addressed # keeping as notes/posterity/hints for future contributors return n = self.key_info_t.name is_equal = gdb.parse_and_eval(n + '::isEqual') empty = gdb.parse_and_eval(n + '::getEmptyKey()') tombstone = gdb.parse_and_eval(n + '::getTombstoneKey()') # the following is invalid, GDB fails with: # Python Exception Attempt to take address of value # not located in memory. # because isEqual took parameter (for the unsigned long key I was testing) # by const ref, and GDB # It's also not entirely general - we should be accessing the "getFirst()" # member function, not the 'first' member variable, but I've yet to figure # out how to find/call member functions (especially (const) overloaded # ones) on a gdb.Value. while self.cur != self.end and (is_equal(self.cur.dereference()['first'], empty) or is_equal(self.cur.dereference()['first'], tombstone)): self.cur = self.cur + 1 def __next__(self): if self.cur == self.end: raise StopIteration cur = self.cur v = cur.dereference()['first' if self.first else 'second'] if not self.first: self.cur = self.cur + 1 self.advancePastEmptyBuckets() self.first = True else: self.first = False return 'x', v if sys.version_info.major == 2: next = __next__ def __init__(self, val): self.val = val def children(self): t = self.val.type.template_argument(3).pointer() begin = self.val['Buckets'].cast(t) end = (begin + self.val['NumBuckets']).cast(t) return self._iterator(self.val.type.template_argument(2), begin, end) def to_string(self): return 'llvm::DenseMap with %d elements' % (self.val['NumEntries']) def display_hint(self): return 'map' +class StringMapPrinter: + "Print a StringMap" + + def __init__(self, val): + self.val = val + + def children(self): + it = self.val['TheTable'] + end = (it + self.val['NumBuckets']) + value_ty = self.val.type.template_argument(0) + entry_ty = gdb.lookup_type('llvm::StringMapEntry<{}>'.format(value_ty.name)) + tombstone = gdb.parse_and_eval('llvm::StringMapImpl::TombstoneIntVal'); + + while it != end: + it_deref = it.dereference() + if it_deref == 0 or it_deref == tombstone: + it = it + 1 + continue + + entry_ptr = it_deref.cast(entry_ty.pointer()) + entry = entry_ptr.dereference() + + str_len = entry['keyLength'] + str_data = (entry_ptr + 1).cast(gdb.lookup_type('char').const().pointer()) + string_ref = gdb.Value(struct.pack('PN', int(str_data), int(str_len)), gdb.lookup_type('llvm::StringRef')) + yield 'key', string_ref + + value = entry['second'] + yield 'value', value + + it = it + 1 + + def to_string(self): + return 'llvm::StringMap with %d elements' % (self.val['NumItems']) + + def display_hint(self): + return 'map' + class TwinePrinter: "Print a Twine" def __init__(self, val): self._val = val def display_hint(self): return 'string' def string_from_pretty_printer_lookup(self, val): '''Lookup the default pretty-printer for val and use it. If no pretty-printer is defined for the type of val, print an error and return a placeholder string.''' pp = gdb.default_visualizer(val) if pp: s = pp.to_string() # The pretty-printer may return a LazyString instead of an actual Python # string. Convert it to a Python string. However, GDB doesn't seem to # register the LazyString type, so we can't check # "type(s) == gdb.LazyString". if 'LazyString' in type(s).__name__: s = s.value().address.string() else: print(('No pretty printer for {} found. The resulting Twine ' + 'representation will be incomplete.').format(val.type.name)) s = '(missing {})'.format(val.type.name) return s def is_twine_kind(self, kind, expected): if not kind.endswith(expected): return False # apparently some GDB versions add the NodeKind:: namespace # (happens for me on GDB 7.11) return kind in ('llvm::Twine::' + expected, 'llvm::Twine::NodeKind::' + expected) def string_from_child(self, child, kind): '''Return the string representation of the Twine::Child child.''' if self.is_twine_kind(kind, 'EmptyKind') or self.is_twine_kind(kind, 'NullKind'): return '' if self.is_twine_kind(kind, 'TwineKind'): return self.string_from_twine_object(child['twine'].dereference()) if self.is_twine_kind(kind, 'CStringKind'): return child['cString'].string() if self.is_twine_kind(kind, 'StdStringKind'): val = child['stdString'].dereference() return self.string_from_pretty_printer_lookup(val) if self.is_twine_kind(kind, 'StringRefKind'): val = child['stringRef'].dereference() pp = StringRefPrinter(val) return pp.to_string() if self.is_twine_kind(kind, 'SmallStringKind'): val = child['smallString'].dereference() pp = SmallStringPrinter(val) return pp.to_string() if self.is_twine_kind(kind, 'CharKind'): return chr(child['character']) if self.is_twine_kind(kind, 'DecUIKind'): return str(child['decUI']) if self.is_twine_kind(kind, 'DecIKind'): return str(child['decI']) if self.is_twine_kind(kind, 'DecULKind'): return str(child['decUL'].dereference()) if self.is_twine_kind(kind, 'DecLKind'): return str(child['decL'].dereference()) if self.is_twine_kind(kind, 'DecULLKind'): return str(child['decULL'].dereference()) if self.is_twine_kind(kind, 'DecLLKind'): return str(child['decLL'].dereference()) if self.is_twine_kind(kind, 'UHexKind'): val = child['uHex'].dereference() return hex(int(val)) print(('Unhandled NodeKind {} in Twine pretty-printer. The result will be ' 'incomplete.').format(kind)) return '(unhandled {})'.format(kind) def string_from_twine_object(self, twine): '''Return the string representation of the Twine object twine.''' lhs_str = '' rhs_str = '' lhs = twine['LHS'] rhs = twine['RHS'] lhs_kind = str(twine['LHSKind']) rhs_kind = str(twine['RHSKind']) lhs_str = self.string_from_child(lhs, lhs_kind) rhs_str = self.string_from_child(rhs, rhs_kind) return lhs_str + rhs_str def to_string(self): return self.string_from_twine_object(self._val) def get_pointer_int_pair(val): """Get tuple from llvm::PointerIntPair.""" info_name = val.type.template_argument(4).strip_typedefs().name # Note: this throws a gdb.error if the info type is not used (by means of a # call to getPointer() or similar) in the current translation unit. enum_type = gdb.lookup_type(info_name + '::MaskAndShiftConstants') enum_dict = gdb.types.make_enum_dict(enum_type) ptr_mask = enum_dict[info_name + '::PointerBitMask'] int_shift = enum_dict[info_name + '::IntShift'] int_mask = enum_dict[info_name + '::IntMask'] pair_union = val['Value'] pointer = (pair_union & ptr_mask) value = ((pair_union >> int_shift) & int_mask) return (pointer, value) class PointerIntPairPrinter: """Print a PointerIntPair.""" def __init__(self, pointer, value): self.pointer = pointer self.value = value def children(self): yield ('pointer', self.pointer) yield ('value', self.value) def make_pointer_int_pair_printer(val): """Factory for an llvm::PointerIntPair printer.""" try: pointer, value = get_pointer_int_pair(val) except gdb.error: return None # If PointerIntPair cannot be analyzed, print as raw value. pointer_type = val.type.template_argument(0) value_type = val.type.template_argument(2) return PointerIntPairPrinter(pointer.cast(pointer_type), value.cast(value_type)) class PointerUnionPrinter: """Print a PointerUnion.""" def __init__(self, pointer): self.pointer = pointer def children(self): yield ('pointer', self.pointer) def to_string(self): return "Containing %s" % self.pointer.type def make_pointer_union_printer(val): """Factory for an llvm::PointerUnion printer.""" try: pointer, value = get_pointer_int_pair(val['Val']) except gdb.error: return None # If PointerIntPair cannot be analyzed, print as raw value. pointer_type = val.type.template_argument(int(value)) return PointerUnionPrinter(pointer.cast(pointer_type)) class IlistNodePrinter: """Print an llvm::ilist_node object.""" def __init__(self, val): impl_type = val.type.fields()[0].type base_type = impl_type.fields()[0].type derived_type = val.type.template_argument(0) def get_prev_and_sentinel(base): # One of Prev and PrevAndSentinel exists. Depending on #defines used to # compile LLVM, the base_type's template argument is either true of false. if base_type.template_argument(0): return get_pointer_int_pair(base['PrevAndSentinel']) return base['Prev'], None # Casts a base_type pointer to the appropriate derived type. def cast_pointer(pointer): sentinel = get_prev_and_sentinel(pointer.dereference())[1] pointer = pointer.cast(impl_type.pointer()) if sentinel: return pointer return pointer.cast(derived_type.pointer()) # Repeated cast becaue val.type's base_type is ambiguous when using tags. base = val.cast(impl_type).cast(base_type) (prev, sentinel) = get_prev_and_sentinel(base) prev = prev.cast(base_type.pointer()) self.prev = cast_pointer(prev) self.next = cast_pointer(val['Next']) self.sentinel = sentinel def children(self): if self.sentinel: yield 'sentinel', 'yes' yield 'prev', self.prev yield 'next', self.next class IlistPrinter: """Print an llvm::simple_ilist or llvm::iplist object.""" def __init__(self, val): self.node_type = val.type.template_argument(0) sentinel = val['Sentinel'] # First field is common base type of sentinel and ilist_node. base_type = sentinel.type.fields()[0].type self.sentinel = sentinel.address.cast(base_type.pointer()) def _pointers(self): pointer = self.sentinel while True: pointer = pointer['Next'].cast(pointer.type) if pointer == self.sentinel: return yield pointer.cast(self.node_type.pointer()) def children(self): for k, v in enumerate(self._pointers()): yield ('[%d]' % k, v.dereference()) pp = gdb.printing.RegexpCollectionPrettyPrinter("LLVMSupport") pp.add_printer('llvm::SmallString', '^llvm::SmallString<.*>$', SmallStringPrinter) pp.add_printer('llvm::StringRef', '^llvm::StringRef$', StringRefPrinter) pp.add_printer('llvm::SmallVectorImpl', '^llvm::SmallVector(Impl)?<.*>$', SmallVectorPrinter) pp.add_printer('llvm::ArrayRef', '^llvm::(Mutable)?ArrayRef<.*>$', ArrayRefPrinter) pp.add_printer('llvm::Expected', '^llvm::Expected<.*>$', ExpectedPrinter) pp.add_printer('llvm::Optional', '^llvm::Optional<.*>$', OptionalPrinter) pp.add_printer('llvm::DenseMap', '^llvm::DenseMap<.*>$', DenseMapPrinter) +pp.add_printer('llvm::StringMap', '^llvm::StringMap<.*>$', StringMapPrinter) pp.add_printer('llvm::Twine', '^llvm::Twine$', TwinePrinter) pp.add_printer('llvm::PointerIntPair', '^llvm::PointerIntPair<.*>$', make_pointer_int_pair_printer) pp.add_printer('llvm::PointerUnion', '^llvm::PointerUnion<.*>$', make_pointer_union_printer) pp.add_printer('llvm::ilist_node', '^llvm::ilist_node<.*>$', IlistNodePrinter) pp.add_printer('llvm::iplist', '^llvm::iplist<.*>$', IlistPrinter) pp.add_printer('llvm::simple_ilist', '^llvm::simple_ilist<.*>$', IlistPrinter) gdb.printing.register_pretty_printer(gdb.current_objfile(), pp)