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