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 &register_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