148104Syokota//===-- UnwindPlan.cpp ----------------------------------------------------===// 250477Speter// 348104Syokota// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 448104Syokota// See https://llvm.org/LICENSE.txt for license information. 548104Syokota// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 648104Syokota// 748104Syokota//===----------------------------------------------------------------------===// 855205Speter 948104Syokota#include "lldb/Symbol/UnwindPlan.h" 1048104Syokota 1148104Syokota#include "lldb/Target/Process.h" 1248104Syokota#include "lldb/Target/RegisterContext.h" 1348104Syokota#include "lldb/Target/Target.h" 1448104Syokota#include "lldb/Target/Thread.h" 1548104Syokota#include "lldb/Utility/ConstString.h" 1648104Syokota#include "lldb/Utility/LLDBLog.h" 1748104Syokota#include "lldb/Utility/Log.h" 18162711Sru#include "llvm/DebugInfo/DIContext.h" 1948104Syokota#include "llvm/DebugInfo/DWARF/DWARFExpression.h" 2048104Syokota#include <optional> 21162711Sru 2248104Syokotausing namespace lldb; 2348104Syokotausing namespace lldb_private; 24153072Sru 2548104Syokotabool UnwindPlan::Row::RegisterLocation:: 26162711Sruoperator==(const UnwindPlan::Row::RegisterLocation &rhs) const { 27162711Sru if (m_type == rhs.m_type) { 2848104Syokota switch (m_type) { 2948104Syokota case unspecified: 3048104Syokota case undefined: 3148104Syokota case same: 3248104Syokota return true; 3348104Syokota 3448104Syokota case atCFAPlusOffset: 3548104Syokota case isCFAPlusOffset: 3648104Syokota case atAFAPlusOffset: 37162711Sru case isAFAPlusOffset: 3848104Syokota return m_location.offset == rhs.m_location.offset; 3948104Syokota 4048104Syokota case inOtherRegister: 4148104Syokota return m_location.reg_num == rhs.m_location.reg_num; 4248104Syokota 4348104Syokota case atDWARFExpression: 44162711Sru case isDWARFExpression: 4548104Syokota if (m_location.expr.length == rhs.m_location.expr.length) 4648104Syokota return !memcmp(m_location.expr.opcodes, rhs.m_location.expr.opcodes, 4748104Syokota m_location.expr.length); 4848104Syokota break; 4948104Syokota } 5048104Syokota } 5148104Syokota return false; 5248104Syokota} 5348104Syokota 5448104Syokota// This function doesn't copy the dwarf expression bytes; they must remain in 5548104Syokota// allocated memory for the lifespan of this UnwindPlan object. 5648104Syokotavoid UnwindPlan::Row::RegisterLocation::SetAtDWARFExpression( 5748104Syokota const uint8_t *opcodes, uint32_t len) { 58162711Sru m_type = atDWARFExpression; 5948104Syokota m_location.expr.opcodes = opcodes; 6048104Syokota m_location.expr.length = len; 61162711Sru} 6248104Syokota 6348104Syokota// This function doesn't copy the dwarf expression bytes; they must remain in 6448104Syokota// allocated memory for the lifespan of this UnwindPlan object. 6548104Syokotavoid UnwindPlan::Row::RegisterLocation::SetIsDWARFExpression( 6648104Syokota const uint8_t *opcodes, uint32_t len) { 6748104Syokota m_type = isDWARFExpression; 6848104Syokota m_location.expr.opcodes = opcodes; 6948104Syokota m_location.expr.length = len; 7048104Syokota} 7148104Syokota 72148017Semaxstatic std::optional<std::pair<lldb::ByteOrder, uint32_t>> 73148017SemaxGetByteOrderAndAddrSize(Thread *thread) { 74148017Semax if (!thread) 75148017Semax return std::nullopt; 76148017Semax ProcessSP process_sp = thread->GetProcess(); 77148017Semax if (!process_sp) 78153072Sru return std::nullopt; 79148017Semax ArchSpec arch = process_sp->GetTarget().GetArchitecture(); 80148017Semax return std::make_pair(arch.GetByteOrder(), arch.GetAddressByteSize()); 81148017Semax} 82148017Semax 8348104Syokotastatic void DumpDWARFExpr(Stream &s, llvm::ArrayRef<uint8_t> expr, Thread *thread) { 8448104Syokota if (auto order_and_width = GetByteOrderAndAddrSize(thread)) { 8554543Syokota llvm::DataExtractor data(expr, order_and_width->first == eByteOrderLittle, 8648104Syokota order_and_width->second); 8748104Syokota llvm::DWARFExpression(data, order_and_width->second, llvm::dwarf::DWARF32) 8848104Syokota .print(s.AsRawOstream(), llvm::DIDumpOptions(), nullptr); 8948104Syokota } else 9048104Syokota s.PutCString("dwarf-expr"); 9154543Syokota} 9248104Syokota 9348104Syokotavoid UnwindPlan::Row::RegisterLocation::Dump(Stream &s, 9448104Syokota const UnwindPlan *unwind_plan, 9548104Syokota const UnwindPlan::Row *row, 9648104Syokota Thread *thread, 9748104Syokota bool verbose) const { 9848104Syokota switch (m_type) { 9948104Syokota case unspecified: 10048104Syokota if (verbose) 10148104Syokota s.PutCString("=<unspec>"); 10248104Syokota else 10348104Syokota s.PutCString("=!"); 10448104Syokota break; 10548104Syokota case undefined: 10648104Syokota if (verbose) 10748104Syokota s.PutCString("=<undef>"); 10848104Syokota else 109197330Sed s.PutCString("=?"); 11048104Syokota break; 11148104Syokota case same: 11248104Syokota s.PutCString("= <same>"); 11348104Syokota break; 11448104Syokota 11548104Syokota case atCFAPlusOffset: 11648104Syokota case isCFAPlusOffset: { 11748104Syokota s.PutChar('='); 11848104Syokota if (m_type == atCFAPlusOffset) 11948104Syokota s.PutChar('['); 12048104Syokota s.Printf("CFA%+d", m_location.offset); 12148104Syokota if (m_type == atCFAPlusOffset) 12248104Syokota s.PutChar(']'); 123224126Sed } break; 124224126Sed 125224126Sed case atAFAPlusOffset: 126224126Sed case isAFAPlusOffset: { 127224126Sed s.PutChar('='); 128224126Sed if (m_type == atAFAPlusOffset) 129224126Sed s.PutChar('['); 130224126Sed s.Printf("AFA%+d", m_location.offset); 131224126Sed if (m_type == atAFAPlusOffset) 132224126Sed s.PutChar(']'); 133224126Sed } break; 134224126Sed 135224126Sed case inOtherRegister: { 136224126Sed const RegisterInfo *other_reg_info = nullptr; 13748104Syokota if (unwind_plan) 13848104Syokota other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num); 13948104Syokota if (other_reg_info) 14048104Syokota s.Printf("=%s", other_reg_info->name); 14148104Syokota else 14248104Syokota s.Printf("=reg(%u)", m_location.reg_num); 14348104Syokota } break; 14448104Syokota 14548104Syokota case atDWARFExpression: 14648104Syokota case isDWARFExpression: { 14748104Syokota s.PutChar('='); 14848104Syokota if (m_type == atDWARFExpression) 14948104Syokota s.PutChar('['); 15048104Syokota DumpDWARFExpr( 15148104Syokota s, llvm::ArrayRef(m_location.expr.opcodes, m_location.expr.length), 15248104Syokota thread); 15348104Syokota if (m_type == atDWARFExpression) 15448104Syokota s.PutChar(']'); 15548104Syokota } break; 15648104Syokota } 15748104Syokota} 15848104Syokota 15948104Syokotastatic void DumpRegisterName(Stream &s, const UnwindPlan *unwind_plan, 16048104Syokota Thread *thread, uint32_t reg_num) { 16148104Syokota const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo(thread, reg_num); 16248104Syokota if (reg_info) 16348104Syokota s.PutCString(reg_info->name); 16448104Syokota else 16548104Syokota s.Printf("reg(%u)", reg_num); 16648104Syokota} 16748104Syokota 16848104Syokotabool UnwindPlan::Row::FAValue:: 16948104Syokotaoperator==(const UnwindPlan::Row::FAValue &rhs) const { 17048104Syokota if (m_type == rhs.m_type) { 17148104Syokota switch (m_type) { 17248104Syokota case unspecified: 17348104Syokota case isRaSearch: 17448104Syokota return m_value.ra_search_offset == rhs.m_value.ra_search_offset; 17548104Syokota 17648104Syokota case isRegisterPlusOffset: 17748104Syokota return m_value.reg.offset == rhs.m_value.reg.offset; 17848104Syokota 17948104Syokota case isRegisterDereferenced: 18048104Syokota return m_value.reg.reg_num == rhs.m_value.reg.reg_num; 18148104Syokota 18248104Syokota case isDWARFExpression: 18348104Syokota if (m_value.expr.length == rhs.m_value.expr.length) 18448104Syokota return !memcmp(m_value.expr.opcodes, rhs.m_value.expr.opcodes, 18548104Syokota m_value.expr.length); 18654380Syokota break; 18754380Syokota } 18854380Syokota } 18954380Syokota return false; 19054380Syokota} 19154380Syokota 19254380Syokotavoid UnwindPlan::Row::FAValue::Dump(Stream &s, const UnwindPlan *unwind_plan, 19365759Sdwmalone Thread *thread) const { 19465759Sdwmalone switch (m_type) { 19574118Sache case isRegisterPlusOffset: 19648104Syokota DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num); 19748104Syokota s.Printf("%+3d", m_value.reg.offset); 19848104Syokota break; 19948104Syokota case isRegisterDereferenced: 20048104Syokota s.PutChar('['); 20148104Syokota DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num); 20248104Syokota s.PutChar(']'); 20348104Syokota break; 20448104Syokota case isDWARFExpression: 20548104Syokota DumpDWARFExpr(s, llvm::ArrayRef(m_value.expr.opcodes, m_value.expr.length), 20648104Syokota thread); 20748104Syokota break; 20848104Syokota case unspecified: 20948104Syokota s.PutCString("unspecified"); 21048104Syokota break; 21148104Syokota case isRaSearch: 21248104Syokota s.Printf("RaSearch@SP%+d", m_value.ra_search_offset); 21348104Syokota break; 21448104Syokota } 21548104Syokota} 21648104Syokota 21748104Syokotavoid UnwindPlan::Row::Clear() { 21848104Syokota m_cfa_value.SetUnspecified(); 21948104Syokota m_afa_value.SetUnspecified(); 22048104Syokota m_offset = 0; 22148104Syokota m_unspecified_registers_are_undefined = false; 22248104Syokota m_register_locations.clear(); 22348104Syokota} 22448104Syokota 22548104Syokotavoid UnwindPlan::Row::Dump(Stream &s, const UnwindPlan *unwind_plan, 22648104Syokota Thread *thread, addr_t base_addr) const { 22748104Syokota if (base_addr != LLDB_INVALID_ADDRESS) 22848104Syokota s.Printf("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset()); 22948104Syokota else 23048104Syokota s.Printf("%4" PRId64 ": CFA=", GetOffset()); 23148104Syokota 23248104Syokota m_cfa_value.Dump(s, unwind_plan, thread); 233153072Sru 23448104Syokota if (!m_afa_value.IsUnspecified()) { 23548104Syokota s.Printf(" AFA="); 23648104Syokota m_afa_value.Dump(s, unwind_plan, thread); 237197330Sed } 238197330Sed 239197330Sed s.Printf(" => "); 240224126Sed for (collection::const_iterator idx = m_register_locations.begin(); 241224126Sed idx != m_register_locations.end(); ++idx) { 242224126Sed DumpRegisterName(s, unwind_plan, thread, idx->first); 243224126Sed const bool verbose = false; 24448104Syokota idx->second.Dump(s, unwind_plan, this, thread, verbose); 24548104Syokota s.PutChar(' '); 24648104Syokota } 24748104Syokota} 24848104Syokota 24948104SyokotaUnwindPlan::Row::Row() : m_cfa_value(), m_afa_value(), m_register_locations() {} 25048104Syokota 251197247Sedbool UnwindPlan::Row::GetRegisterInfo( 252197247Sed uint32_t reg_num, 253197247Sed UnwindPlan::Row::RegisterLocation ®ister_location) const { 254197247Sed collection::const_iterator pos = m_register_locations.find(reg_num); 25548104Syokota if (pos != m_register_locations.end()) { 256197247Sed register_location = pos->second; 257197247Sed return true; 258197247Sed } 25948104Syokota if (m_unspecified_registers_are_undefined) { 260197247Sed register_location.SetUndefined(); 261197247Sed return true; 262197247Sed } 263197247Sed return false; 264197247Sed} 265197247Sed 266197247Sedvoid UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num) { 267197247Sed collection::const_iterator pos = m_register_locations.find(reg_num); 26848104Syokota if (pos != m_register_locations.end()) { 26948104Syokota m_register_locations.erase(pos); 270 } 271} 272 273void UnwindPlan::Row::SetRegisterInfo( 274 uint32_t reg_num, 275 const UnwindPlan::Row::RegisterLocation register_location) { 276 m_register_locations[reg_num] = register_location; 277} 278 279bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num, 280 int32_t offset, 281 bool can_replace) { 282 if (!can_replace && 283 m_register_locations.find(reg_num) != m_register_locations.end()) 284 return false; 285 RegisterLocation reg_loc; 286 reg_loc.SetAtCFAPlusOffset(offset); 287 m_register_locations[reg_num] = reg_loc; 288 return true; 289} 290 291bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num, 292 int32_t offset, 293 bool can_replace) { 294 if (!can_replace && 295 m_register_locations.find(reg_num) != m_register_locations.end()) 296 return false; 297 RegisterLocation reg_loc; 298 reg_loc.SetIsCFAPlusOffset(offset); 299 m_register_locations[reg_num] = reg_loc; 300 return true; 301} 302 303bool UnwindPlan::Row::SetRegisterLocationToUndefined( 304 uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) { 305 collection::iterator pos = m_register_locations.find(reg_num); 306 collection::iterator end = m_register_locations.end(); 307 308 if (pos != end) { 309 if (!can_replace) 310 return false; 311 if (can_replace_only_if_unspecified && !pos->second.IsUnspecified()) 312 return false; 313 } 314 RegisterLocation reg_loc; 315 reg_loc.SetUndefined(); 316 m_register_locations[reg_num] = reg_loc; 317 return true; 318} 319 320bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num, 321 bool can_replace) { 322 if (!can_replace && 323 m_register_locations.find(reg_num) != m_register_locations.end()) 324 return false; 325 RegisterLocation reg_loc; 326 reg_loc.SetUnspecified(); 327 m_register_locations[reg_num] = reg_loc; 328 return true; 329} 330 331bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num, 332 uint32_t other_reg_num, 333 bool can_replace) { 334 if (!can_replace && 335 m_register_locations.find(reg_num) != m_register_locations.end()) 336 return false; 337 RegisterLocation reg_loc; 338 reg_loc.SetInRegister(other_reg_num); 339 m_register_locations[reg_num] = reg_loc; 340 return true; 341} 342 343bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num, 344 bool must_replace) { 345 if (must_replace && 346 m_register_locations.find(reg_num) == m_register_locations.end()) 347 return false; 348 RegisterLocation reg_loc; 349 reg_loc.SetSame(); 350 m_register_locations[reg_num] = reg_loc; 351 return true; 352} 353 354bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const { 355 return m_offset == rhs.m_offset && m_cfa_value == rhs.m_cfa_value && 356 m_afa_value == rhs.m_afa_value && 357 m_unspecified_registers_are_undefined == 358 rhs.m_unspecified_registers_are_undefined && 359 m_register_locations == rhs.m_register_locations; 360} 361 362void UnwindPlan::AppendRow(const UnwindPlan::RowSP &row_sp) { 363 if (m_row_list.empty() || 364 m_row_list.back()->GetOffset() != row_sp->GetOffset()) 365 m_row_list.push_back(row_sp); 366 else 367 m_row_list.back() = row_sp; 368} 369 370void UnwindPlan::InsertRow(const UnwindPlan::RowSP &row_sp, 371 bool replace_existing) { 372 collection::iterator it = m_row_list.begin(); 373 while (it != m_row_list.end()) { 374 RowSP row = *it; 375 if (row->GetOffset() >= row_sp->GetOffset()) 376 break; 377 it++; 378 } 379 if (it == m_row_list.end() || (*it)->GetOffset() != row_sp->GetOffset()) 380 m_row_list.insert(it, row_sp); 381 else if (replace_existing) 382 *it = row_sp; 383} 384 385UnwindPlan::RowSP UnwindPlan::GetRowForFunctionOffset(int offset) const { 386 RowSP row; 387 if (!m_row_list.empty()) { 388 if (offset == -1) 389 row = m_row_list.back(); 390 else { 391 collection::const_iterator pos, end = m_row_list.end(); 392 for (pos = m_row_list.begin(); pos != end; ++pos) { 393 if ((*pos)->GetOffset() <= static_cast<lldb::offset_t>(offset)) 394 row = *pos; 395 else 396 break; 397 } 398 } 399 } 400 return row; 401} 402 403bool UnwindPlan::IsValidRowIndex(uint32_t idx) const { 404 return idx < m_row_list.size(); 405} 406 407const UnwindPlan::RowSP UnwindPlan::GetRowAtIndex(uint32_t idx) const { 408 if (idx < m_row_list.size()) 409 return m_row_list[idx]; 410 else { 411 Log *log = GetLog(LLDBLog::Unwind); 412 LLDB_LOGF(log, 413 "error: UnwindPlan::GetRowAtIndex(idx = %u) invalid index " 414 "(number rows is %u)", 415 idx, (uint32_t)m_row_list.size()); 416 return UnwindPlan::RowSP(); 417 } 418} 419 420const UnwindPlan::RowSP UnwindPlan::GetLastRow() const { 421 if (m_row_list.empty()) { 422 Log *log = GetLog(LLDBLog::Unwind); 423 LLDB_LOGF(log, "UnwindPlan::GetLastRow() when rows are empty"); 424 return UnwindPlan::RowSP(); 425 } 426 return m_row_list.back(); 427} 428 429int UnwindPlan::GetRowCount() const { return m_row_list.size(); } 430 431void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) { 432 if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0) 433 m_plan_valid_address_range = range; 434} 435 436bool UnwindPlan::PlanValidAtAddress(Address addr) { 437 // If this UnwindPlan has no rows, it is an invalid UnwindPlan. 438 if (GetRowCount() == 0) { 439 Log *log = GetLog(LLDBLog::Unwind); 440 if (log) { 441 StreamString s; 442 if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) { 443 LLDB_LOGF(log, 444 "UnwindPlan is invalid -- no unwind rows for UnwindPlan " 445 "'%s' at address %s", 446 m_source_name.GetCString(), s.GetData()); 447 } else { 448 LLDB_LOGF(log, 449 "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'", 450 m_source_name.GetCString()); 451 } 452 } 453 return false; 454 } 455 456 // If the 0th Row of unwind instructions is missing, or if it doesn't provide 457 // a register to use to find the Canonical Frame Address, this is not a valid 458 // UnwindPlan. 459 if (GetRowAtIndex(0).get() == nullptr || 460 GetRowAtIndex(0)->GetCFAValue().GetValueType() == 461 Row::FAValue::unspecified) { 462 Log *log = GetLog(LLDBLog::Unwind); 463 if (log) { 464 StreamString s; 465 if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) { 466 LLDB_LOGF(log, 467 "UnwindPlan is invalid -- no CFA register defined in row 0 " 468 "for UnwindPlan '%s' at address %s", 469 m_source_name.GetCString(), s.GetData()); 470 } else { 471 LLDB_LOGF(log, 472 "UnwindPlan is invalid -- no CFA register defined in row 0 " 473 "for UnwindPlan '%s'", 474 m_source_name.GetCString()); 475 } 476 } 477 return false; 478 } 479 480 if (!m_plan_valid_address_range.GetBaseAddress().IsValid() || 481 m_plan_valid_address_range.GetByteSize() == 0) 482 return true; 483 484 if (!addr.IsValid()) 485 return true; 486 487 if (m_plan_valid_address_range.ContainsFileAddress(addr)) 488 return true; 489 490 return false; 491} 492 493void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const { 494 if (!m_source_name.IsEmpty()) { 495 s.Printf("This UnwindPlan originally sourced from %s\n", 496 m_source_name.GetCString()); 497 } 498 if (m_lsda_address.IsValid() && m_personality_func_addr.IsValid()) { 499 TargetSP target_sp(thread->CalculateTarget()); 500 addr_t lsda_load_addr = m_lsda_address.GetLoadAddress(target_sp.get()); 501 addr_t personality_func_load_addr = 502 m_personality_func_addr.GetLoadAddress(target_sp.get()); 503 504 if (lsda_load_addr != LLDB_INVALID_ADDRESS && 505 personality_func_load_addr != LLDB_INVALID_ADDRESS) { 506 s.Printf("LSDA address 0x%" PRIx64 507 ", personality routine is at address 0x%" PRIx64 "\n", 508 lsda_load_addr, personality_func_load_addr); 509 } 510 } 511 s.Printf("This UnwindPlan is sourced from the compiler: "); 512 switch (m_plan_is_sourced_from_compiler) { 513 case eLazyBoolYes: 514 s.Printf("yes.\n"); 515 break; 516 case eLazyBoolNo: 517 s.Printf("no.\n"); 518 break; 519 case eLazyBoolCalculate: 520 s.Printf("not specified.\n"); 521 break; 522 } 523 s.Printf("This UnwindPlan is valid at all instruction locations: "); 524 switch (m_plan_is_valid_at_all_instruction_locations) { 525 case eLazyBoolYes: 526 s.Printf("yes.\n"); 527 break; 528 case eLazyBoolNo: 529 s.Printf("no.\n"); 530 break; 531 case eLazyBoolCalculate: 532 s.Printf("not specified.\n"); 533 break; 534 } 535 s.Printf("This UnwindPlan is for a trap handler function: "); 536 switch (m_plan_is_for_signal_trap) { 537 case eLazyBoolYes: 538 s.Printf("yes.\n"); 539 break; 540 case eLazyBoolNo: 541 s.Printf("no.\n"); 542 break; 543 case eLazyBoolCalculate: 544 s.Printf("not specified.\n"); 545 break; 546 } 547 if (m_plan_valid_address_range.GetBaseAddress().IsValid() && 548 m_plan_valid_address_range.GetByteSize() > 0) { 549 s.PutCString("Address range of this UnwindPlan: "); 550 TargetSP target_sp(thread->CalculateTarget()); 551 m_plan_valid_address_range.Dump(&s, target_sp.get(), 552 Address::DumpStyleSectionNameOffset); 553 s.EOL(); 554 } 555 collection::const_iterator pos, begin = m_row_list.begin(), 556 end = m_row_list.end(); 557 for (pos = begin; pos != end; ++pos) { 558 s.Printf("row[%u]: ", (uint32_t)std::distance(begin, pos)); 559 (*pos)->Dump(s, this, thread, base_addr); 560 s.Printf("\n"); 561 } 562} 563 564void UnwindPlan::SetSourceName(const char *source) { 565 m_source_name = ConstString(source); 566} 567 568ConstString UnwindPlan::GetSourceName() const { return m_source_name; } 569 570const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread, 571 uint32_t unwind_reg) const { 572 if (thread) { 573 RegisterContext *reg_ctx = thread->GetRegisterContext().get(); 574 if (reg_ctx) { 575 uint32_t reg; 576 if (m_register_kind == eRegisterKindLLDB) 577 reg = unwind_reg; 578 else 579 reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind, 580 unwind_reg); 581 if (reg != LLDB_INVALID_REGNUM) 582 return reg_ctx->GetRegisterInfoAtIndex(reg); 583 } 584 } 585 return nullptr; 586} 587