1 //===-- HashedNameToDIE.cpp -------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "HashedNameToDIE.h"
10 #include "llvm/ADT/StringRef.h"
11 
12 void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array,
13                                       DIEArray &die_offsets) {
14   const size_t count = die_info_array.size();
15   for (size_t i = 0; i < count; ++i)
16     die_offsets.emplace_back(die_info_array[i].cu_offset,
17                              die_info_array[i].offset);
18 }
19 
20 void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array,
21                                       const dw_tag_t tag,
22                                       DIEArray &die_offsets) {
23   if (tag == 0) {
24     ExtractDIEArray(die_info_array, die_offsets);
25   } else {
26     const size_t count = die_info_array.size();
27     for (size_t i = 0; i < count; ++i) {
28       const dw_tag_t die_tag = die_info_array[i].tag;
29       bool tag_matches = die_tag == 0 || tag == die_tag;
30       if (!tag_matches) {
31         if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type)
32           tag_matches =
33               tag == DW_TAG_structure_type || tag == DW_TAG_class_type;
34       }
35       if (tag_matches)
36         die_offsets.emplace_back(die_info_array[i].cu_offset,
37                                  die_info_array[i].offset);
38     }
39   }
40 }
41 
42 void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array,
43                                       const dw_tag_t tag,
44                                       const uint32_t qualified_name_hash,
45                                       DIEArray &die_offsets) {
46   if (tag == 0) {
47     ExtractDIEArray(die_info_array, die_offsets);
48   } else {
49     const size_t count = die_info_array.size();
50     for (size_t i = 0; i < count; ++i) {
51       if (qualified_name_hash != die_info_array[i].qualified_name_hash)
52         continue;
53       const dw_tag_t die_tag = die_info_array[i].tag;
54       bool tag_matches = die_tag == 0 || tag == die_tag;
55       if (!tag_matches) {
56         if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type)
57           tag_matches =
58               tag == DW_TAG_structure_type || tag == DW_TAG_class_type;
59       }
60       if (tag_matches)
61         die_offsets.emplace_back(die_info_array[i].cu_offset,
62                                  die_info_array[i].offset);
63     }
64   }
65 }
66 
67 void DWARFMappedHash::ExtractClassOrStructDIEArray(
68     const DIEInfoArray &die_info_array,
69     bool return_implementation_only_if_available, DIEArray &die_offsets) {
70   const size_t count = die_info_array.size();
71   for (size_t i = 0; i < count; ++i) {
72     const dw_tag_t die_tag = die_info_array[i].tag;
73     if (die_tag == 0 || die_tag == DW_TAG_class_type ||
74         die_tag == DW_TAG_structure_type) {
75       if (die_info_array[i].type_flags & eTypeFlagClassIsImplementation) {
76         if (return_implementation_only_if_available) {
77           // We found the one true definition for this class, so only return
78           // that
79           die_offsets.clear();
80           die_offsets.emplace_back(die_info_array[i].cu_offset,
81                                    die_info_array[i].offset);
82           return;
83         } else {
84           // Put the one true definition as the first entry so it matches first
85           die_offsets.emplace(die_offsets.begin(), die_info_array[i].cu_offset,
86                               die_info_array[i].offset);
87         }
88       } else {
89         die_offsets.emplace_back(die_info_array[i].cu_offset,
90                                  die_info_array[i].offset);
91       }
92     }
93   }
94 }
95 
96 void DWARFMappedHash::ExtractTypesFromDIEArray(
97     const DIEInfoArray &die_info_array, uint32_t type_flag_mask,
98     uint32_t type_flag_value, DIEArray &die_offsets) {
99   const size_t count = die_info_array.size();
100   for (size_t i = 0; i < count; ++i) {
101     if ((die_info_array[i].type_flags & type_flag_mask) == type_flag_value)
102       die_offsets.emplace_back(die_info_array[i].cu_offset,
103                                die_info_array[i].offset);
104   }
105 }
106 
107 const char *DWARFMappedHash::GetAtomTypeName(uint16_t atom) {
108   switch (atom) {
109   case eAtomTypeNULL:
110     return "NULL";
111   case eAtomTypeDIEOffset:
112     return "die-offset";
113   case eAtomTypeCUOffset:
114     return "cu-offset";
115   case eAtomTypeTag:
116     return "die-tag";
117   case eAtomTypeNameFlags:
118     return "name-flags";
119   case eAtomTypeTypeFlags:
120     return "type-flags";
121   case eAtomTypeQualNameHash:
122     return "qualified-name-hash";
123   }
124   return "<invalid>";
125 }
126 
127 DWARFMappedHash::DIEInfo::DIEInfo()
128     : cu_offset(DW_INVALID_OFFSET), offset(DW_INVALID_OFFSET), tag(0),
129       type_flags(0), qualified_name_hash(0) {}
130 
131 DWARFMappedHash::DIEInfo::DIEInfo(dw_offset_t c, dw_offset_t o, dw_tag_t t,
132                                   uint32_t f, uint32_t h)
133     : cu_offset(c), offset(o), tag(t), type_flags(f), qualified_name_hash(h) {}
134 
135 DWARFMappedHash::Prologue::Prologue(dw_offset_t _die_base_offset)
136     : die_base_offset(_die_base_offset), atoms(), atom_mask(0),
137       min_hash_data_byte_size(0), hash_data_has_fixed_byte_size(true) {
138   // Define an array of DIE offsets by first defining an array, and then define
139   // the atom type for the array, in this case we have an array of DIE offsets
140   AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4);
141 }
142 
143 void DWARFMappedHash::Prologue::ClearAtoms() {
144   hash_data_has_fixed_byte_size = true;
145   min_hash_data_byte_size = 0;
146   atom_mask = 0;
147   atoms.clear();
148 }
149 
150 bool DWARFMappedHash::Prologue::ContainsAtom(AtomType atom_type) const {
151   return (atom_mask & (1u << atom_type)) != 0;
152 }
153 
154 void DWARFMappedHash::Prologue::Clear() {
155   die_base_offset = 0;
156   ClearAtoms();
157 }
158 
159 void DWARFMappedHash::Prologue::AppendAtom(AtomType type, dw_form_t form) {
160   atoms.push_back({type, form});
161   atom_mask |= 1u << type;
162   switch (form) {
163   case DW_FORM_indirect:
164   case DW_FORM_exprloc:
165   case DW_FORM_flag_present:
166   case DW_FORM_ref_sig8:
167     llvm_unreachable("Unhandled atom form");
168 
169   case DW_FORM_string:
170   case DW_FORM_block:
171   case DW_FORM_block1:
172   case DW_FORM_sdata:
173   case DW_FORM_udata:
174   case DW_FORM_ref_udata:
175   case DW_FORM_GNU_addr_index:
176   case DW_FORM_GNU_str_index:
177     hash_data_has_fixed_byte_size = false;
178     LLVM_FALLTHROUGH;
179   case DW_FORM_flag:
180   case DW_FORM_data1:
181   case DW_FORM_ref1:
182   case DW_FORM_sec_offset:
183     min_hash_data_byte_size += 1;
184     break;
185 
186   case DW_FORM_block2:
187     hash_data_has_fixed_byte_size = false;
188     LLVM_FALLTHROUGH;
189   case DW_FORM_data2:
190   case DW_FORM_ref2:
191     min_hash_data_byte_size += 2;
192     break;
193 
194   case DW_FORM_block4:
195     hash_data_has_fixed_byte_size = false;
196     LLVM_FALLTHROUGH;
197   case DW_FORM_data4:
198   case DW_FORM_ref4:
199   case DW_FORM_addr:
200   case DW_FORM_ref_addr:
201   case DW_FORM_strp:
202     min_hash_data_byte_size += 4;
203     break;
204 
205   case DW_FORM_data8:
206   case DW_FORM_ref8:
207     min_hash_data_byte_size += 8;
208     break;
209   }
210 }
211 
212 lldb::offset_t
213 DWARFMappedHash::Prologue::Read(const lldb_private::DataExtractor &data,
214                                 lldb::offset_t offset) {
215   ClearAtoms();
216 
217   die_base_offset = data.GetU32(&offset);
218 
219   const uint32_t atom_count = data.GetU32(&offset);
220   if (atom_count == 0x00060003u) {
221     // Old format, deal with contents of old pre-release format
222     while (data.GetU32(&offset))
223       /* do nothing */;
224 
225     // Hardcode to the only known value for now.
226     AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4);
227   } else {
228     for (uint32_t i = 0; i < atom_count; ++i) {
229       AtomType type = (AtomType)data.GetU16(&offset);
230       dw_form_t form = (dw_form_t)data.GetU16(&offset);
231       AppendAtom(type, form);
232     }
233   }
234   return offset;
235 }
236 
237 size_t DWARFMappedHash::Prologue::GetByteSize() const {
238   // Add an extra count to the atoms size for the zero termination Atom that
239   // gets written to disk
240   return sizeof(die_base_offset) + sizeof(uint32_t) +
241          atoms.size() * sizeof(Atom);
242 }
243 
244 size_t DWARFMappedHash::Prologue::GetMinimumHashDataByteSize() const {
245   return min_hash_data_byte_size;
246 }
247 
248 bool DWARFMappedHash::Prologue::HashDataHasFixedByteSize() const {
249   return hash_data_has_fixed_byte_size;
250 }
251 
252 size_t DWARFMappedHash::Header::GetByteSize(const HeaderData &header_data) {
253   return header_data.GetByteSize();
254 }
255 
256 lldb::offset_t DWARFMappedHash::Header::Read(lldb_private::DataExtractor &data,
257                                              lldb::offset_t offset) {
258   offset = MappedHash::Header<Prologue>::Read(data, offset);
259   if (offset != UINT32_MAX) {
260     offset = header_data.Read(data, offset);
261   }
262   return offset;
263 }
264 
265 bool DWARFMappedHash::Header::Read(const lldb_private::DWARFDataExtractor &data,
266                                    lldb::offset_t *offset_ptr,
267                                    DIEInfo &hash_data) const {
268   const size_t num_atoms = header_data.atoms.size();
269   if (num_atoms == 0)
270     return false;
271 
272   for (size_t i = 0; i < num_atoms; ++i) {
273     DWARFFormValue form_value(NULL, header_data.atoms[i].form);
274 
275     if (!form_value.ExtractValue(data, offset_ptr))
276       return false;
277 
278     switch (header_data.atoms[i].type) {
279     case eAtomTypeDIEOffset: // DIE offset, check form for encoding
280       hash_data.offset =
281           DWARFFormValue::IsDataForm(form_value.Form())
282               ? form_value.Unsigned()
283               : form_value.Reference(header_data.die_base_offset);
284       break;
285 
286     case eAtomTypeTag: // DW_TAG value for the DIE
287       hash_data.tag = (dw_tag_t)form_value.Unsigned();
288       break;
289 
290     case eAtomTypeTypeFlags: // Flags from enum TypeFlags
291       hash_data.type_flags = (uint32_t)form_value.Unsigned();
292       break;
293 
294     case eAtomTypeQualNameHash: // Flags from enum TypeFlags
295       hash_data.qualified_name_hash = form_value.Unsigned();
296       break;
297 
298     default:
299       // We can always skip atoms we don't know about
300       break;
301     }
302   }
303   return true;
304 }
305 
306 void DWARFMappedHash::Header::Dump(lldb_private::Stream &strm,
307                                    const DIEInfo &hash_data) const {
308   const size_t num_atoms = header_data.atoms.size();
309   for (size_t i = 0; i < num_atoms; ++i) {
310     if (i > 0)
311       strm.PutCString(", ");
312 
313     DWARFFormValue form_value(NULL, header_data.atoms[i].form);
314     switch (header_data.atoms[i].type) {
315     case eAtomTypeDIEOffset: // DIE offset, check form for encoding
316       strm.Printf("{0x%8.8x}", hash_data.offset);
317       break;
318 
319     case eAtomTypeTag: // DW_TAG value for the DIE
320     {
321       const char *tag_cstr = lldb_private::DW_TAG_value_to_name(hash_data.tag);
322       if (tag_cstr)
323         strm.PutCString(tag_cstr);
324       else
325         strm.Printf("DW_TAG_(0x%4.4x)", hash_data.tag);
326     } break;
327 
328     case eAtomTypeTypeFlags: // Flags from enum TypeFlags
329       strm.Printf("0x%2.2x", hash_data.type_flags);
330       if (hash_data.type_flags) {
331         strm.PutCString(" (");
332         if (hash_data.type_flags & eTypeFlagClassIsImplementation)
333           strm.PutCString(" implementation");
334         strm.PutCString(" )");
335       }
336       break;
337 
338     case eAtomTypeQualNameHash: // Flags from enum TypeFlags
339       strm.Printf("0x%8.8x", hash_data.qualified_name_hash);
340       break;
341 
342     default:
343       strm.Printf("AtomType(0x%x)", header_data.atoms[i].type);
344       break;
345     }
346   }
347 }
348 
349 DWARFMappedHash::MemoryTable::MemoryTable(
350     lldb_private::DWARFDataExtractor &table_data,
351     const lldb_private::DWARFDataExtractor &string_table, const char *name)
352     : MappedHash::MemoryTable<uint32_t, Header, DIEInfoArray>(table_data),
353       m_data(table_data), m_string_table(string_table), m_name(name) {}
354 
355 const char *
356 DWARFMappedHash::MemoryTable::GetStringForKeyType(KeyType key) const {
357   // The key in the DWARF table is the .debug_str offset for the string
358   return m_string_table.PeekCStr(key);
359 }
360 
361 bool DWARFMappedHash::MemoryTable::ReadHashData(uint32_t hash_data_offset,
362                                                 HashData &hash_data) const {
363   lldb::offset_t offset = hash_data_offset;
364   offset += 4; // Skip string table offset that contains offset of hash name in
365                // .debug_str
366   const uint32_t count = m_data.GetU32(&offset);
367   if (count > 0) {
368     hash_data.resize(count);
369     for (uint32_t i = 0; i < count; ++i) {
370       if (!m_header.Read(m_data, &offset, hash_data[i]))
371         return false;
372     }
373   } else
374     hash_data.clear();
375   return true;
376 }
377 
378 DWARFMappedHash::MemoryTable::Result
379 DWARFMappedHash::MemoryTable::GetHashDataForName(
380     llvm::StringRef name, lldb::offset_t *hash_data_offset_ptr,
381     Pair &pair) const {
382   pair.key = m_data.GetU32(hash_data_offset_ptr);
383   pair.value.clear();
384 
385   // If the key is zero, this terminates our chain of HashData objects for this
386   // hash value.
387   if (pair.key == 0)
388     return eResultEndOfHashData;
389 
390   // There definitely should be a string for this string offset, if there
391   // isn't, there is something wrong, return and error
392   const char *strp_cstr = m_string_table.PeekCStr(pair.key);
393   if (strp_cstr == NULL) {
394     *hash_data_offset_ptr = UINT32_MAX;
395     return eResultError;
396   }
397 
398   const uint32_t count = m_data.GetU32(hash_data_offset_ptr);
399   const size_t min_total_hash_data_size =
400       count * m_header.header_data.GetMinimumHashDataByteSize();
401   if (count > 0 &&
402       m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr,
403                                       min_total_hash_data_size)) {
404     // We have at least one HashData entry, and we have enough data to parse at
405     // least "count" HashData entries.
406 
407     // First make sure the entire C string matches...
408     const bool match = name == strp_cstr;
409 
410     if (!match && m_header.header_data.HashDataHasFixedByteSize()) {
411       // If the string doesn't match and we have fixed size data, we can just
412       // add the total byte size of all HashData objects to the hash data
413       // offset and be done...
414       *hash_data_offset_ptr += min_total_hash_data_size;
415     } else {
416       // If the string does match, or we don't have fixed size data then we
417       // need to read the hash data as a stream. If the string matches we also
418       // append all HashData objects to the value array.
419       for (uint32_t i = 0; i < count; ++i) {
420         DIEInfo die_info;
421         if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) {
422           // Only happened if the HashData of the string matched...
423           if (match)
424             pair.value.push_back(die_info);
425         } else {
426           // Something went wrong while reading the data
427           *hash_data_offset_ptr = UINT32_MAX;
428           return eResultError;
429         }
430       }
431     }
432     // Return the correct response depending on if the string matched or not...
433     if (match)
434       return eResultKeyMatch; // The key (cstring) matches and we have lookup
435                               // results!
436     else
437       return eResultKeyMismatch; // The key doesn't match, this function will
438                                  // get called
439     // again for the next key/value or the key terminator which in our case is
440     // a zero .debug_str offset.
441   } else {
442     *hash_data_offset_ptr = UINT32_MAX;
443     return eResultError;
444   }
445 }
446 
447 DWARFMappedHash::MemoryTable::Result
448 DWARFMappedHash::MemoryTable::AppendHashDataForRegularExpression(
449     const lldb_private::RegularExpression &regex,
450     lldb::offset_t *hash_data_offset_ptr, Pair &pair) const {
451   pair.key = m_data.GetU32(hash_data_offset_ptr);
452   // If the key is zero, this terminates our chain of HashData objects for this
453   // hash value.
454   if (pair.key == 0)
455     return eResultEndOfHashData;
456 
457   // There definitely should be a string for this string offset, if there
458   // isn't, there is something wrong, return and error
459   const char *strp_cstr = m_string_table.PeekCStr(pair.key);
460   if (strp_cstr == NULL)
461     return eResultError;
462 
463   const uint32_t count = m_data.GetU32(hash_data_offset_ptr);
464   const size_t min_total_hash_data_size =
465       count * m_header.header_data.GetMinimumHashDataByteSize();
466   if (count > 0 &&
467       m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr,
468                                       min_total_hash_data_size)) {
469     const bool match = regex.Execute(llvm::StringRef(strp_cstr));
470 
471     if (!match && m_header.header_data.HashDataHasFixedByteSize()) {
472       // If the regex doesn't match and we have fixed size data, we can just
473       // add the total byte size of all HashData objects to the hash data
474       // offset and be done...
475       *hash_data_offset_ptr += min_total_hash_data_size;
476     } else {
477       // If the string does match, or we don't have fixed size data then we
478       // need to read the hash data as a stream. If the string matches we also
479       // append all HashData objects to the value array.
480       for (uint32_t i = 0; i < count; ++i) {
481         DIEInfo die_info;
482         if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) {
483           // Only happened if the HashData of the string matched...
484           if (match)
485             pair.value.push_back(die_info);
486         } else {
487           // Something went wrong while reading the data
488           *hash_data_offset_ptr = UINT32_MAX;
489           return eResultError;
490         }
491       }
492     }
493     // Return the correct response depending on if the string matched or not...
494     if (match)
495       return eResultKeyMatch; // The key (cstring) matches and we have lookup
496                               // results!
497     else
498       return eResultKeyMismatch; // The key doesn't match, this function will
499                                  // get called
500     // again for the next key/value or the key terminator which in our case is
501     // a zero .debug_str offset.
502   } else {
503     *hash_data_offset_ptr = UINT32_MAX;
504     return eResultError;
505   }
506 }
507 
508 size_t DWARFMappedHash::MemoryTable::AppendAllDIEsThatMatchingRegex(
509     const lldb_private::RegularExpression &regex,
510     DIEInfoArray &die_info_array) const {
511   const uint32_t hash_count = m_header.hashes_count;
512   Pair pair;
513   for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) {
514     lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx);
515     while (hash_data_offset != UINT32_MAX) {
516       const lldb::offset_t prev_hash_data_offset = hash_data_offset;
517       Result hash_result =
518           AppendHashDataForRegularExpression(regex, &hash_data_offset, pair);
519       if (prev_hash_data_offset == hash_data_offset)
520         break;
521 
522       // Check the result of getting our hash data
523       switch (hash_result) {
524       case eResultKeyMatch:
525       case eResultKeyMismatch:
526         // Whether we matches or not, it doesn't matter, we keep looking.
527         break;
528 
529       case eResultEndOfHashData:
530       case eResultError:
531         hash_data_offset = UINT32_MAX;
532         break;
533       }
534     }
535   }
536   die_info_array.swap(pair.value);
537   return die_info_array.size();
538 }
539 
540 size_t DWARFMappedHash::MemoryTable::AppendAllDIEsInRange(
541     const uint32_t die_offset_start, const uint32_t die_offset_end,
542     DIEInfoArray &die_info_array) const {
543   const uint32_t hash_count = m_header.hashes_count;
544   for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) {
545     bool done = false;
546     lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx);
547     while (!done && hash_data_offset != UINT32_MAX) {
548       KeyType key = m_data.GetU32(&hash_data_offset);
549       // If the key is zero, this terminates our chain of HashData objects for
550       // this hash value.
551       if (key == 0)
552         break;
553 
554       const uint32_t count = m_data.GetU32(&hash_data_offset);
555       for (uint32_t i = 0; i < count; ++i) {
556         DIEInfo die_info;
557         if (m_header.Read(m_data, &hash_data_offset, die_info)) {
558           if (die_info.offset == 0)
559             done = true;
560           if (die_offset_start <= die_info.offset &&
561               die_info.offset < die_offset_end)
562             die_info_array.push_back(die_info);
563         }
564       }
565     }
566   }
567   return die_info_array.size();
568 }
569 
570 size_t DWARFMappedHash::MemoryTable::FindByName(llvm::StringRef name,
571                                                 DIEArray &die_offsets) {
572   if (name.empty())
573     return 0;
574 
575   DIEInfoArray die_info_array;
576   if (FindByName(name, die_info_array))
577     DWARFMappedHash::ExtractDIEArray(die_info_array, die_offsets);
578   return die_info_array.size();
579 }
580 
581 size_t DWARFMappedHash::MemoryTable::FindByNameAndTag(llvm::StringRef name,
582                                                       const dw_tag_t tag,
583                                                       DIEArray &die_offsets) {
584   DIEInfoArray die_info_array;
585   if (FindByName(name, die_info_array))
586     DWARFMappedHash::ExtractDIEArray(die_info_array, tag, die_offsets);
587   return die_info_array.size();
588 }
589 
590 size_t DWARFMappedHash::MemoryTable::FindByNameAndTagAndQualifiedNameHash(
591     llvm::StringRef name, const dw_tag_t tag,
592     const uint32_t qualified_name_hash, DIEArray &die_offsets) {
593   DIEInfoArray die_info_array;
594   if (FindByName(name, die_info_array))
595     DWARFMappedHash::ExtractDIEArray(die_info_array, tag, qualified_name_hash,
596                                      die_offsets);
597   return die_info_array.size();
598 }
599 
600 size_t DWARFMappedHash::MemoryTable::FindCompleteObjCClassByName(
601     llvm::StringRef name, DIEArray &die_offsets, bool must_be_implementation) {
602   DIEInfoArray die_info_array;
603   if (FindByName(name, die_info_array)) {
604     if (must_be_implementation &&
605         GetHeader().header_data.ContainsAtom(eAtomTypeTypeFlags)) {
606       // If we have two atoms, then we have the DIE offset and the type flags
607       // so we can find the objective C class efficiently.
608       DWARFMappedHash::ExtractTypesFromDIEArray(die_info_array, UINT32_MAX,
609                                                 eTypeFlagClassIsImplementation,
610                                                 die_offsets);
611     } else {
612       // We don't only want the one true definition, so try and see what we can
613       // find, and only return class or struct DIEs. If we do have the full
614       // implementation, then return it alone, else return all possible
615       // matches.
616       const bool return_implementation_only_if_available = true;
617       DWARFMappedHash::ExtractClassOrStructDIEArray(
618           die_info_array, return_implementation_only_if_available, die_offsets);
619     }
620   }
621   return die_offsets.size();
622 }
623 
624 size_t DWARFMappedHash::MemoryTable::FindByName(llvm::StringRef name,
625                                                 DIEInfoArray &die_info_array) {
626   if (name.empty())
627     return 0;
628 
629   Pair kv_pair;
630   size_t old_size = die_info_array.size();
631   if (Find(name, kv_pair)) {
632     die_info_array.swap(kv_pair.value);
633     return die_info_array.size() - old_size;
634   }
635   return 0;
636 }
637