//===-- RegisterContextUnwind.cpp -----------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "lldb/Target/RegisterContextUnwind.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/AddressRange.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/Value.h"
#include "lldb/Expression/DWARFExpressionList.h"
#include "lldb/Symbol/ArmUnwindInfo.h"
#include "lldb/Symbol/CallFrameInfo.h"
#include "lldb/Symbol/DWARFCallFrameInfo.h"
#include "lldb/Symbol/FuncUnwinders.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/SymbolFile.h"
#include "lldb/Target/ABI.h"
#include "lldb/Target/DynamicLoader.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/LanguageRuntime.h"
#include "lldb/Target/Platform.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/DataBufferHeap.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegisterValue.h"
#include "lldb/Utility/VASPrintf.h"
#include "lldb/lldb-private.h"

#include <cassert>
#include <memory>

using namespace lldb;
using namespace lldb_private;

static ConstString GetSymbolOrFunctionName(const SymbolContext &sym_ctx) {
  if (sym_ctx.symbol)
    return sym_ctx.symbol->GetName();
  else if (sym_ctx.function)
    return sym_ctx.function->GetName();
  return ConstString();
}

static bool CallFrameAddressIsValid(ABISP abi_sp, lldb::addr_t cfa) {
  if (cfa == LLDB_INVALID_ADDRESS)
    return false;
  if (abi_sp)
    return abi_sp->CallFrameAddressIsValid(cfa);
  return cfa != 0 && cfa != 1;
}

RegisterContextUnwind::RegisterContextUnwind(Thread &thread,
                                             const SharedPtr &next_frame,
                                             SymbolContext &sym_ctx,
                                             uint32_t frame_number,
                                             UnwindLLDB &unwind_lldb)
    : RegisterContext(thread, frame_number), m_thread(thread),
      m_fast_unwind_plan_sp(), m_full_unwind_plan_sp(),
      m_fallback_unwind_plan_sp(), m_all_registers_available(false),
      m_frame_type(-1), m_cfa(LLDB_INVALID_ADDRESS),
      m_afa(LLDB_INVALID_ADDRESS), m_start_pc(), m_current_pc(),
      m_current_offset(0), m_current_offset_backed_up_one(0),
      m_behaves_like_zeroth_frame(false), m_sym_ctx(sym_ctx),
      m_sym_ctx_valid(false), m_frame_number(frame_number), m_registers(),
      m_parent_unwind(unwind_lldb) {
  m_sym_ctx.Clear(false);
  m_sym_ctx_valid = false;

  if (IsFrameZero()) {
    InitializeZerothFrame();
  } else {
    InitializeNonZerothFrame();
  }

  // This same code exists over in the GetFullUnwindPlanForFrame() but it may
  // not have been executed yet
  if (IsFrameZero() || next_frame->m_frame_type == eTrapHandlerFrame ||
      next_frame->m_frame_type == eDebuggerFrame) {
    m_all_registers_available = true;
  }
}

bool RegisterContextUnwind::IsUnwindPlanValidForCurrentPC(
    std::shared_ptr<const UnwindPlan> unwind_plan_sp) {
  if (!unwind_plan_sp)
    return false;

  // check if m_current_pc is valid
  if (unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
    // yes - current offset can be used as is
    return true;
  }

  // If don't have an offset or we're at the start of the function, we've got
  // nothing else to try.
  if (!m_current_offset || m_current_offset == 0)
    return false;

  // check pc - 1 to see if it's valid
  Address pc_minus_one(m_current_pc);
  pc_minus_one.SetOffset(m_current_pc.GetOffset() - 1);
  if (unwind_plan_sp->PlanValidAtAddress(pc_minus_one)) {
    return true;
  }

  return false;
}

// Initialize a RegisterContextUnwind which is the first frame of a stack -- the
// zeroth frame or currently executing frame.

void RegisterContextUnwind::InitializeZerothFrame() {
  Log *log = GetLog(LLDBLog::Unwind);
  ExecutionContext exe_ctx(m_thread.shared_from_this());
  RegisterContextSP reg_ctx_sp = m_thread.GetRegisterContext();

  if (reg_ctx_sp.get() == nullptr) {
    m_frame_type = eNotAValidFrame;
    UnwindLogMsg("frame does not have a register context");
    return;
  }

  addr_t current_pc = reg_ctx_sp->GetPC();

  if (current_pc == LLDB_INVALID_ADDRESS) {
    m_frame_type = eNotAValidFrame;
    UnwindLogMsg("frame does not have a pc");
    return;
  }

  Process *process = exe_ctx.GetProcessPtr();

  // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs
  // this will strip bit zero in case we read a PC from memory or from the LR.
  // (which would be a no-op in frame 0 where we get it from the register set,
  // but still a good idea to make the call here for other ABIs that may
  // exist.)
  if (ABISP abi_sp = process->GetABI())
    current_pc = abi_sp->FixCodeAddress(current_pc);

  std::shared_ptr<const UnwindPlan> lang_runtime_plan_sp =
      LanguageRuntime::GetRuntimeUnwindPlan(m_thread, this,
                                            m_behaves_like_zeroth_frame);
  if (lang_runtime_plan_sp.get()) {
    UnwindLogMsg("This is an async frame");
  }

  // Initialize m_current_pc, an Address object, based on current_pc, an
  // addr_t.
  m_current_pc.SetLoadAddress(current_pc, &process->GetTarget());

  // If we don't have a Module for some reason, we're not going to find
  // symbol/function information - just stick in some reasonable defaults and
  // hope we can unwind past this frame.
  ModuleSP pc_module_sp(m_current_pc.GetModule());
  if (!m_current_pc.IsValid() || !pc_module_sp) {
    UnwindLogMsg("using architectural default unwind method");
  }

  m_sym_ctx_valid = m_current_pc.ResolveFunctionScope(m_sym_ctx);

  if (m_sym_ctx.symbol) {
    UnwindLogMsg("with pc value of 0x%" PRIx64 ", symbol name is '%s'",
                 current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
  } else if (m_sym_ctx.function) {
    UnwindLogMsg("with pc value of 0x%" PRIx64 ", function name is '%s'",
                 current_pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
  } else {
    UnwindLogMsg("with pc value of 0x%" PRIx64
                 ", no symbol/function name is known.",
                 current_pc);
  }

  if (IsTrapHandlerSymbol(process, m_sym_ctx)) {
    m_frame_type = eTrapHandlerFrame;
  } else {
    // FIXME:  Detect eDebuggerFrame here.
    m_frame_type = eNormalFrame;
  }

  // If we were able to find a symbol/function, set addr_range to the bounds of
  // that symbol/function. else treat the current pc value as the start_pc and
  // record no offset.
  if (m_sym_ctx_valid) {
    m_start_pc = m_sym_ctx.GetFunctionOrSymbolAddress();
    if (m_current_pc.GetModule() == m_start_pc.GetModule()) {
      m_current_offset =
          m_current_pc.GetFileAddress() - m_start_pc.GetFileAddress();
    }
    m_current_offset_backed_up_one = m_current_offset;
  } else {
    m_start_pc = m_current_pc;
    m_current_offset = std::nullopt;
    m_current_offset_backed_up_one = std::nullopt;
  }

  // We've set m_frame_type and m_sym_ctx before these calls.

  m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame();
  m_full_unwind_plan_sp = GetFullUnwindPlanForFrame();

  const UnwindPlan::Row *active_row = nullptr;
  lldb::RegisterKind row_register_kind = eRegisterKindGeneric;

  // If we have LanguageRuntime UnwindPlan for this unwind, use those
  // rules to find the caller frame instead of the function's normal
  // UnwindPlans.  The full unwind plan for this frame will be
  // the LanguageRuntime-provided unwind plan, and there will not be a
  // fast unwind plan.
  if (lang_runtime_plan_sp.get()) {
    active_row =
        lang_runtime_plan_sp->GetRowForFunctionOffset(m_current_offset);
    row_register_kind = lang_runtime_plan_sp->GetRegisterKind();
    if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(),
                          m_cfa)) {
      UnwindLogMsg("Cannot set cfa");
    } else {
      m_full_unwind_plan_sp = lang_runtime_plan_sp;
      if (log) {
        StreamString active_row_strm;
        active_row->Dump(active_row_strm, lang_runtime_plan_sp.get(), &m_thread,
                         m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
        UnwindLogMsg("async active row: %s", active_row_strm.GetData());
      }
      UnwindLogMsg("m_cfa = 0x%" PRIx64 " m_afa = 0x%" PRIx64, m_cfa, m_afa);
      UnwindLogMsg(
          "initialized async frame current pc is 0x%" PRIx64
          " cfa is 0x%" PRIx64 " afa is 0x%" PRIx64,
          (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
          (uint64_t)m_cfa, (uint64_t)m_afa);

      return;
    }
  }

  if (m_full_unwind_plan_sp &&
      m_full_unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
    active_row =
        m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
    row_register_kind = m_full_unwind_plan_sp->GetRegisterKind();
    PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp);
    if (active_row && log) {
      StreamString active_row_strm;
      active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread,
                       m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
      UnwindLogMsg("%s", active_row_strm.GetData());
    }
  }

  if (!active_row) {
    UnwindLogMsg("could not find an unwindplan row for this frame's pc");
    m_frame_type = eNotAValidFrame;
    return;
  }

  if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(), m_cfa)) {
    // Try the fall back unwind plan since the
    // full unwind plan failed.
    FuncUnwindersSP func_unwinders_sp;
    std::shared_ptr<const UnwindPlan> call_site_unwind_plan;
    bool cfa_status = false;

    if (m_sym_ctx_valid) {
      func_unwinders_sp =
          pc_module_sp->GetUnwindTable().GetFuncUnwindersContainingAddress(
              m_current_pc, m_sym_ctx);
    }

    if (func_unwinders_sp.get() != nullptr)
      call_site_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(
          process->GetTarget(), m_thread);

    if (call_site_unwind_plan != nullptr) {
      m_fallback_unwind_plan_sp = call_site_unwind_plan;
      if (TryFallbackUnwindPlan())
        cfa_status = true;
    }
    if (!cfa_status) {
      UnwindLogMsg("could not read CFA value for first frame.");
      m_frame_type = eNotAValidFrame;
      return;
    }
  } else
    ReadFrameAddress(row_register_kind, active_row->GetAFAValue(), m_afa);

  if (m_cfa == LLDB_INVALID_ADDRESS && m_afa == LLDB_INVALID_ADDRESS) {
    UnwindLogMsg(
        "could not read CFA or AFA values for first frame, not valid.");
    m_frame_type = eNotAValidFrame;
    return;
  }

  // Give the Architecture a chance to replace the UnwindPlan.
  TryAdoptArchitectureUnwindPlan();

  UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64
               " afa is 0x%" PRIx64 " using %s UnwindPlan",
               (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
               (uint64_t)m_cfa,
               (uint64_t)m_afa,
               m_full_unwind_plan_sp->GetSourceName().GetCString());
}

// Initialize a RegisterContextUnwind for the non-zeroth frame -- rely on the
// RegisterContextUnwind "below" it to provide things like its current pc value.

void RegisterContextUnwind::InitializeNonZerothFrame() {
  Log *log = GetLog(LLDBLog::Unwind);
  if (IsFrameZero()) {
    m_frame_type = eNotAValidFrame;
    UnwindLogMsg("non-zeroth frame tests positive for IsFrameZero -- that "
                 "shouldn't happen.");
    return;
  }

  if (!GetNextFrame().get() || !GetNextFrame()->IsValid()) {
    m_frame_type = eNotAValidFrame;
    UnwindLogMsg("Could not get next frame, marking this frame as invalid.");
    return;
  }
  if (!m_thread.GetRegisterContext()) {
    m_frame_type = eNotAValidFrame;
    UnwindLogMsg("Could not get register context for this thread, marking this "
                 "frame as invalid.");
    return;
  }

  ExecutionContext exe_ctx(m_thread.shared_from_this());
  Process *process = exe_ctx.GetProcessPtr();

  // Some languages may have a logical parent stack frame which is
  // not a real stack frame, but the programmer would consider it to
  // be the caller of the frame, e.g. Swift asynchronous frames.
  //
  // A LanguageRuntime may provide an UnwindPlan that is used in this
  // stack trace base on the RegisterContext contents, intsead
  // of the normal UnwindPlans we would use for the return-pc.
  std::shared_ptr<const UnwindPlan> lang_runtime_plan_sp =
      LanguageRuntime::GetRuntimeUnwindPlan(m_thread, this,
                                            m_behaves_like_zeroth_frame);
  if (lang_runtime_plan_sp.get()) {
    UnwindLogMsg("This is an async frame");
  }

  addr_t pc;
  if (!ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) {
    UnwindLogMsg("could not get pc value");
    m_frame_type = eNotAValidFrame;
    return;
  }

  // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs
  // this will strip bit zero in case we read a PC from memory or from the LR.
  ABISP abi_sp = process->GetABI();
  if (abi_sp)
    pc = abi_sp->FixCodeAddress(pc);

  if (log) {
    UnwindLogMsg("pc = 0x%" PRIx64, pc);
    addr_t reg_val;
    if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, reg_val))
      UnwindLogMsg("fp = 0x%" PRIx64, reg_val);
    if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, reg_val))
      UnwindLogMsg("sp = 0x%" PRIx64, reg_val);
  }

  // A pc of 0x0 means it's the end of the stack crawl unless we're above a trap
  // handler function
  bool above_trap_handler = false;
  if (GetNextFrame().get() && GetNextFrame()->IsValid() &&
      GetNextFrame()->IsTrapHandlerFrame())
    above_trap_handler = true;

  if (pc == 0 || pc == 0x1) {
    if (!above_trap_handler) {
      m_frame_type = eNotAValidFrame;
      UnwindLogMsg("this frame has a pc of 0x0");
      return;
    }
  }

  const bool allow_section_end = true;
  m_current_pc.SetLoadAddress(pc, &process->GetTarget(), allow_section_end);

  // If we don't have a Module for some reason, we're not going to find
  // symbol/function information - just stick in some reasonable defaults and
  // hope we can unwind past this frame.  If we're above a trap handler,
  // we may be at a bogus address because we jumped through a bogus function
  // pointer and trapped, so don't force the arch default unwind plan in that
  // case.
  ModuleSP pc_module_sp(m_current_pc.GetModule());
  if ((!m_current_pc.IsValid() || !pc_module_sp) &&
      above_trap_handler == false) {
    UnwindLogMsg("using architectural default unwind method");

    // Test the pc value to see if we know it's in an unmapped/non-executable
    // region of memory.
    uint32_t permissions;
    if (process->GetLoadAddressPermissions(pc, permissions) &&
        (permissions & ePermissionsExecutable) == 0) {
      // If this is the second frame off the stack, we may have unwound the
      // first frame incorrectly.  But using the architecture default unwind
      // plan may get us back on track -- albeit possibly skipping a real
      // frame.  Give this frame a clearly-invalid pc and see if we can get any
      // further.
      if (GetNextFrame().get() && GetNextFrame()->IsValid() &&
          GetNextFrame()->IsFrameZero()) {
        UnwindLogMsg("had a pc of 0x%" PRIx64 " which is not in executable "
                                              "memory but on frame 1 -- "
                                              "allowing it once.",
                     (uint64_t)pc);
        m_frame_type = eSkipFrame;
      } else {
        // anywhere other than the second frame, a non-executable pc means
        // we're off in the weeds -- stop now.
        m_frame_type = eNotAValidFrame;
        UnwindLogMsg("pc is in a non-executable section of memory and this "
                     "isn't the 2nd frame in the stack walk.");
        return;
      }
    }

    if (abi_sp) {
      m_fast_unwind_plan_sp.reset();
      m_full_unwind_plan_sp = abi_sp->CreateDefaultUnwindPlan();
      if (m_frame_type != eSkipFrame) // don't override eSkipFrame
      {
        m_frame_type = eNormalFrame;
      }
      m_all_registers_available = false;
      m_current_offset = std::nullopt;
      m_current_offset_backed_up_one = std::nullopt;
      RegisterKind row_register_kind = m_full_unwind_plan_sp->GetRegisterKind();
      if (const UnwindPlan::Row *row =
              m_full_unwind_plan_sp->GetRowForFunctionOffset(0)) {
        if (!ReadFrameAddress(row_register_kind, row->GetCFAValue(), m_cfa)) {
          UnwindLogMsg("failed to get cfa value");
          if (m_frame_type != eSkipFrame) // don't override eSkipFrame
          {
            m_frame_type = eNotAValidFrame;
          }
          return;
        }

        ReadFrameAddress(row_register_kind, row->GetAFAValue(), m_afa);

        // A couple of sanity checks..
        if (!CallFrameAddressIsValid(abi_sp, m_cfa)) {
          UnwindLogMsg("could not find a valid cfa address");
          m_frame_type = eNotAValidFrame;
          return;
        }

        // m_cfa should point into the stack memory; if we can query memory
        // region permissions, see if the memory is allocated & readable.
        if (process->GetLoadAddressPermissions(m_cfa, permissions) &&
            (permissions & ePermissionsReadable) == 0) {
          m_frame_type = eNotAValidFrame;
          UnwindLogMsg(
              "the CFA points to a region of memory that is not readable");
          return;
        }
      } else {
        UnwindLogMsg("could not find a row for function offset zero");
        m_frame_type = eNotAValidFrame;
        return;
      }

      if (CheckIfLoopingStack()) {
        TryFallbackUnwindPlan();
        if (CheckIfLoopingStack()) {
          UnwindLogMsg("same CFA address as next frame, assuming the unwind is "
                       "looping - stopping");
          m_frame_type = eNotAValidFrame;
          return;
        }
      }

      // Give the Architecture a chance to replace the UnwindPlan.
      TryAdoptArchitectureUnwindPlan();

      UnwindLogMsg("initialized frame cfa is 0x%" PRIx64 " afa is 0x%" PRIx64,
                   (uint64_t)m_cfa, (uint64_t)m_afa);
      return;
    }
    m_frame_type = eNotAValidFrame;
    UnwindLogMsg("could not find any symbol for this pc, or a default unwind "
                 "plan, to continue unwind.");
    return;
  }

  m_sym_ctx_valid = m_current_pc.ResolveFunctionScope(m_sym_ctx);

  if (m_sym_ctx.symbol) {
    UnwindLogMsg("with pc value of 0x%" PRIx64 ", symbol name is '%s'", pc,
                 GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
  } else if (m_sym_ctx.function) {
    UnwindLogMsg("with pc value of 0x%" PRIx64 ", function name is '%s'", pc,
                 GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
  } else {
    UnwindLogMsg("with pc value of 0x%" PRIx64
                 ", no symbol/function name is known.",
                 pc);
  }

  bool decr_pc_and_recompute_addr_range;

  if (!m_sym_ctx_valid) {
    // Always decrement and recompute if the symbol lookup failed
    decr_pc_and_recompute_addr_range = true;
  } else if (GetNextFrame()->m_frame_type == eTrapHandlerFrame ||
             GetNextFrame()->m_frame_type == eDebuggerFrame) {
    // Don't decrement if we're "above" an asynchronous event like
    // sigtramp.
    decr_pc_and_recompute_addr_range = false;
  } else if (Address addr = m_sym_ctx.GetFunctionOrSymbolAddress();
             addr != m_current_pc) {
    // If our "current" pc isn't the start of a function, decrement the pc
    // if we're up the stack.
    if (m_behaves_like_zeroth_frame)
      decr_pc_and_recompute_addr_range = false;
    else
      decr_pc_and_recompute_addr_range = true;
  } else if (IsTrapHandlerSymbol(process, m_sym_ctx)) {
    // Signal dispatch may set the return address of the handler it calls to
    // point to the first byte of a return trampoline (like __kernel_rt_sigreturn),
    // so do not decrement and recompute if the symbol we already found is a trap
    // handler.
    decr_pc_and_recompute_addr_range = false;
  } else if (m_behaves_like_zeroth_frame) {
    decr_pc_and_recompute_addr_range = false;
  } else {
    // Decrement to find the function containing the call.
    decr_pc_and_recompute_addr_range = true;
  }

  // We need to back up the pc by 1 byte and re-search for the Symbol to handle
  // the case where the "saved pc" value is pointing to the next function, e.g.
  // if a function ends with a CALL instruction.
  // FIXME this may need to be an architectural-dependent behavior; if so we'll
  // need to add a member function
  // to the ABI plugin and consult that.
  if (decr_pc_and_recompute_addr_range) {
    UnwindLogMsg("Backing up the pc value of 0x%" PRIx64
                 " by 1 and re-doing symbol lookup; old symbol was %s",
                 pc, GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
    Address temporary_pc;
    temporary_pc.SetLoadAddress(pc - 1, &process->GetTarget());
    m_sym_ctx.Clear(false);
    m_sym_ctx_valid = temporary_pc.ResolveFunctionScope(m_sym_ctx);

    UnwindLogMsg("Symbol is now %s",
                 GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
  }

  // If we were able to find a symbol/function, set addr_range_ptr to the
  // bounds of that symbol/function. else treat the current pc value as the
  // start_pc and record no offset.
  if (m_sym_ctx_valid) {
    m_start_pc = m_sym_ctx.GetFunctionOrSymbolAddress();
    m_current_offset = pc - m_start_pc.GetLoadAddress(&process->GetTarget());
    m_current_offset_backed_up_one = m_current_offset;
    if (decr_pc_and_recompute_addr_range &&
        m_current_offset_backed_up_one != 0) {
      --*m_current_offset_backed_up_one;
      if (m_sym_ctx_valid) {
        m_current_pc.SetLoadAddress(pc - 1, &process->GetTarget());
      }
    }
  } else {
    m_start_pc = m_current_pc;
    m_current_offset = std::nullopt;
    m_current_offset_backed_up_one = std::nullopt;
  }

  if (IsTrapHandlerSymbol(process, m_sym_ctx)) {
    m_frame_type = eTrapHandlerFrame;
  } else {
    // FIXME:  Detect eDebuggerFrame here.
    if (m_frame_type != eSkipFrame) // don't override eSkipFrame
    {
      m_frame_type = eNormalFrame;
    }
  }

  const UnwindPlan::Row *active_row;
  RegisterKind row_register_kind = eRegisterKindGeneric;

  // If we have LanguageRuntime UnwindPlan for this unwind, use those
  // rules to find the caller frame instead of the function's normal
  // UnwindPlans.  The full unwind plan for this frame will be
  // the LanguageRuntime-provided unwind plan, and there will not be a
  // fast unwind plan.
  if (lang_runtime_plan_sp.get()) {
    active_row =
        lang_runtime_plan_sp->GetRowForFunctionOffset(m_current_offset);
    row_register_kind = lang_runtime_plan_sp->GetRegisterKind();
    if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(),
                          m_cfa)) {
      UnwindLogMsg("Cannot set cfa");
    } else {
      m_full_unwind_plan_sp = lang_runtime_plan_sp;
      if (log) {
        StreamString active_row_strm;
        active_row->Dump(active_row_strm, lang_runtime_plan_sp.get(), &m_thread,
                         m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
        UnwindLogMsg("async active row: %s", active_row_strm.GetData());
      }
      UnwindLogMsg("m_cfa = 0x%" PRIx64 " m_afa = 0x%" PRIx64, m_cfa, m_afa);
      UnwindLogMsg(
          "initialized async frame current pc is 0x%" PRIx64
          " cfa is 0x%" PRIx64 " afa is 0x%" PRIx64,
          (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
          (uint64_t)m_cfa, (uint64_t)m_afa);

      return;
    }
  }

  // We've set m_frame_type and m_sym_ctx before this call.
  m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame();

  // Try to get by with just the fast UnwindPlan if possible - the full
  // UnwindPlan may be expensive to get (e.g. if we have to parse the entire
  // eh_frame section of an ObjectFile for the first time.)

  if (m_fast_unwind_plan_sp &&
      m_fast_unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
    active_row =
        m_fast_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
    row_register_kind = m_fast_unwind_plan_sp->GetRegisterKind();
    PropagateTrapHandlerFlagFromUnwindPlan(m_fast_unwind_plan_sp);
    if (active_row && log) {
      StreamString active_row_strm;
      active_row->Dump(active_row_strm, m_fast_unwind_plan_sp.get(), &m_thread,
                       m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
      UnwindLogMsg("Using fast unwind plan '%s'",
                   m_fast_unwind_plan_sp->GetSourceName().AsCString());
      UnwindLogMsg("active row: %s", active_row_strm.GetData());
    }
  } else {
    m_full_unwind_plan_sp = GetFullUnwindPlanForFrame();
    if (IsUnwindPlanValidForCurrentPC(m_full_unwind_plan_sp)) {
      active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset(
          m_current_offset_backed_up_one);
      row_register_kind = m_full_unwind_plan_sp->GetRegisterKind();
      PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp);
      if (active_row && log) {
        StreamString active_row_strm;
        active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(),
                         &m_thread,
                         m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
        UnwindLogMsg("Using full unwind plan '%s'",
                     m_full_unwind_plan_sp->GetSourceName().AsCString());
        UnwindLogMsg("active row: %s", active_row_strm.GetData());
      }
    }
  }

  if (!active_row) {
    m_frame_type = eNotAValidFrame;
    UnwindLogMsg("could not find unwind row for this pc");
    return;
  }

  if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(), m_cfa)) {
    UnwindLogMsg("failed to get cfa");
    m_frame_type = eNotAValidFrame;
    return;
  }

  ReadFrameAddress(row_register_kind, active_row->GetAFAValue(), m_afa);

  UnwindLogMsg("m_cfa = 0x%" PRIx64 " m_afa = 0x%" PRIx64, m_cfa, m_afa);

  if (CheckIfLoopingStack()) {
    TryFallbackUnwindPlan();
    if (CheckIfLoopingStack()) {
      UnwindLogMsg("same CFA address as next frame, assuming the unwind is "
                   "looping - stopping");
      m_frame_type = eNotAValidFrame;
      return;
    }
  }

  // Give the Architecture a chance to replace the UnwindPlan.
  TryAdoptArchitectureUnwindPlan();

  UnwindLogMsg("initialized frame current pc is 0x%" PRIx64
               " cfa is 0x%" PRIx64 " afa is 0x%" PRIx64,
               (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
               (uint64_t)m_cfa,
               (uint64_t)m_afa);
}

bool RegisterContextUnwind::CheckIfLoopingStack() {
  // If we have a bad stack setup, we can get the same CFA value multiple times
  // -- or even more devious, we can actually oscillate between two CFA values.
  // Detect that here and break out to avoid a possible infinite loop in lldb
  // trying to unwind the stack. To detect when we have the same CFA value
  // multiple times, we compare the
  // CFA of the current
  // frame with the 2nd next frame because in some specail case (e.g. signal
  // hanlders, hand written assembly without ABI compliance) we can have 2
  // frames with the same
  // CFA (in theory we
  // can have arbitrary number of frames with the same CFA, but more then 2 is
  // very unlikely)

  RegisterContextUnwind::SharedPtr next_frame = GetNextFrame();
  if (next_frame) {
    RegisterContextUnwind::SharedPtr next_next_frame =
        next_frame->GetNextFrame();
    addr_t next_next_frame_cfa = LLDB_INVALID_ADDRESS;
    if (next_next_frame && next_next_frame->GetCFA(next_next_frame_cfa)) {
      if (next_next_frame_cfa == m_cfa) {
        // We have a loop in the stack unwind
        return true;
      }
    }
  }
  return false;
}

bool RegisterContextUnwind::IsFrameZero() const { return m_frame_number == 0; }

bool RegisterContextUnwind::BehavesLikeZerothFrame() const {
  if (m_frame_number == 0)
    return true;
  if (m_behaves_like_zeroth_frame)
    return true;
  return false;
}

// Find a fast unwind plan for this frame, if possible.
//
// On entry to this method,
//
//   1. m_frame_type should already be set to eTrapHandlerFrame/eDebuggerFrame
//   if either of those are correct,
//   2. m_sym_ctx should already be filled in, and
//   3. m_current_pc should have the current pc value for this frame
//   4. m_current_offset_backed_up_one should have the current byte offset into
//   the function, maybe backed up by 1, std::nullopt if unknown

std::shared_ptr<const UnwindPlan>
RegisterContextUnwind::GetFastUnwindPlanForFrame() {
  ModuleSP pc_module_sp(m_current_pc.GetModule());

  if (!m_current_pc.IsValid() || !pc_module_sp ||
      pc_module_sp->GetObjectFile() == nullptr)
    return nullptr;

  if (IsFrameZero())
    return nullptr;

  FuncUnwindersSP func_unwinders_sp(
      pc_module_sp->GetUnwindTable().GetFuncUnwindersContainingAddress(
          m_current_pc, m_sym_ctx));
  if (!func_unwinders_sp)
    return nullptr;

  // If we're in _sigtramp(), unwinding past this frame requires special
  // knowledge.
  if (m_frame_type == eTrapHandlerFrame || m_frame_type == eDebuggerFrame)
    return nullptr;

  if (std::shared_ptr<const UnwindPlan> unwind_plan_sp =
          func_unwinders_sp->GetUnwindPlanFastUnwind(
              *m_thread.CalculateTarget(), m_thread)) {
    if (unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
      m_frame_type = eNormalFrame;
      return unwind_plan_sp;
    }
  }
  return nullptr;
}

// On entry to this method,
//
//   1. m_frame_type should already be set to eTrapHandlerFrame/eDebuggerFrame
//   if either of those are correct,
//   2. m_sym_ctx should already be filled in, and
//   3. m_current_pc should have the current pc value for this frame
//   4. m_current_offset_backed_up_one should have the current byte offset into
//   the function, maybe backed up by 1, std::nullopt if unknown

std::shared_ptr<const UnwindPlan>
RegisterContextUnwind::GetFullUnwindPlanForFrame() {
  std::shared_ptr<const UnwindPlan> arch_default_unwind_plan_sp;
  ExecutionContext exe_ctx(m_thread.shared_from_this());
  Process *process = exe_ctx.GetProcessPtr();
  ABI *abi = process ? process->GetABI().get() : nullptr;
  if (abi) {
    arch_default_unwind_plan_sp = abi->CreateDefaultUnwindPlan();
  } else {
    UnwindLogMsg(
        "unable to get architectural default UnwindPlan from ABI plugin");
  }

  if (IsFrameZero() || GetNextFrame()->m_frame_type == eTrapHandlerFrame ||
      GetNextFrame()->m_frame_type == eDebuggerFrame) {
    m_behaves_like_zeroth_frame = true;
    // If this frame behaves like a 0th frame (currently executing or
    // interrupted asynchronously), all registers can be retrieved.
    m_all_registers_available = true;
  }

  // If we've done a jmp 0x0 / bl 0x0 (called through a null function pointer)
  // so the pc is 0x0 in the zeroth frame, we need to use the "unwind at first
  // instruction" arch default UnwindPlan Also, if this Process can report on
  // memory region attributes, any non-executable region means we jumped
  // through a bad function pointer - handle the same way as 0x0. Note, if we
  // have a symbol context & a symbol, we don't want to follow this code path.
  // This is for jumping to memory regions without any information available.

  if ((!m_sym_ctx_valid ||
       (m_sym_ctx.function == nullptr && m_sym_ctx.symbol == nullptr)) &&
      m_behaves_like_zeroth_frame && m_current_pc.IsValid()) {
    uint32_t permissions;
    addr_t current_pc_addr =
        m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr());
    if (current_pc_addr == 0 ||
        (process &&
         process->GetLoadAddressPermissions(current_pc_addr, permissions) &&
         (permissions & ePermissionsExecutable) == 0)) {
      if (abi) {
        m_frame_type = eNormalFrame;
        return abi->CreateFunctionEntryUnwindPlan();
      }
    }
  }

  // No Module for the current pc, try using the architecture default unwind.
  ModuleSP pc_module_sp(m_current_pc.GetModule());
  if (!m_current_pc.IsValid() || !pc_module_sp ||
      pc_module_sp->GetObjectFile() == nullptr) {
    m_frame_type = eNormalFrame;
    return arch_default_unwind_plan_sp;
  }

  FuncUnwindersSP func_unwinders_sp;
  if (m_sym_ctx_valid) {
    func_unwinders_sp =
        pc_module_sp->GetUnwindTable().GetFuncUnwindersContainingAddress(
            m_current_pc, m_sym_ctx);
  }

  // No FuncUnwinders available for this pc (stripped function symbols, lldb
  // could not augment its function table with another source, like
  // LC_FUNCTION_STARTS or eh_frame in ObjectFileMachO). See if eh_frame or the
  // .ARM.exidx tables have unwind information for this address, else fall back
  // to the architectural default unwind.
  if (!func_unwinders_sp) {
    m_frame_type = eNormalFrame;

    if (!pc_module_sp || !pc_module_sp->GetObjectFile() ||
        !m_current_pc.IsValid())
      return arch_default_unwind_plan_sp;

    // Even with -fomit-frame-pointer, we can try eh_frame to get back on
    // track.
    if (DWARFCallFrameInfo *eh_frame =
            pc_module_sp->GetUnwindTable().GetEHFrameInfo()) {
      if (std::unique_ptr<UnwindPlan> plan_up =
              eh_frame->GetUnwindPlan(m_current_pc))
        return plan_up;
    }

    ArmUnwindInfo *arm_exidx =
        pc_module_sp->GetUnwindTable().GetArmUnwindInfo();
    if (arm_exidx) {
      auto unwind_plan_sp =
          std::make_shared<UnwindPlan>(lldb::eRegisterKindGeneric);
      if (arm_exidx->GetUnwindPlan(exe_ctx.GetTargetRef(), m_current_pc,
                                   *unwind_plan_sp))
        return unwind_plan_sp;
    }

    CallFrameInfo *object_file_unwind =
        pc_module_sp->GetUnwindTable().GetObjectFileUnwindInfo();
    if (object_file_unwind) {
      if (std::unique_ptr<UnwindPlan> plan_up =
              object_file_unwind->GetUnwindPlan(m_current_pc))
        return plan_up;
    }

    return arch_default_unwind_plan_sp;
  }

  if (m_frame_type == eTrapHandlerFrame && process) {
    m_fast_unwind_plan_sp.reset();

    // On some platforms the unwind information for signal handlers is not
    // present or correct. Give the platform plugins a chance to provide
    // substitute plan. Otherwise, use eh_frame.
    if (m_sym_ctx_valid) {
      lldb::PlatformSP platform = process->GetTarget().GetPlatform();
      const ArchSpec arch = process->GetTarget().GetArchitecture();
      if (auto unwind_plan_sp = platform->GetTrapHandlerUnwindPlan(
              arch, GetSymbolOrFunctionName(m_sym_ctx)))
        return unwind_plan_sp;
    }

    auto unwind_plan_sp =
        func_unwinders_sp->GetEHFrameUnwindPlan(process->GetTarget());
    if (!unwind_plan_sp)
      unwind_plan_sp =
          func_unwinders_sp->GetObjectFileUnwindPlan(process->GetTarget());
    if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc) &&
        unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes) {
      return unwind_plan_sp;
    }
  }

  // Ask the DynamicLoader if the eh_frame CFI should be trusted in this frame
  // even when it's frame zero This comes up if we have hand-written functions
  // in a Module and hand-written eh_frame.  The assembly instruction
  // inspection may fail and the eh_frame CFI were probably written with some
  // care to do the right thing.  It'd be nice if there was a way to ask the
  // eh_frame directly if it is asynchronous (can be trusted at every
  // instruction point) or synchronous (the normal case - only at call sites).
  // But there is not.
  if (process && process->GetDynamicLoader() &&
      process->GetDynamicLoader()->AlwaysRelyOnEHUnwindInfo(m_sym_ctx)) {
    // We must specifically call the GetEHFrameUnwindPlan() method here --
    // normally we would call GetUnwindPlanAtCallSite() -- because CallSite may
    // return an unwind plan sourced from either eh_frame (that's what we
    // intend) or compact unwind (this won't work)
    auto unwind_plan_sp =
        func_unwinders_sp->GetEHFrameUnwindPlan(process->GetTarget());
    if (!unwind_plan_sp)
      unwind_plan_sp =
          func_unwinders_sp->GetObjectFileUnwindPlan(process->GetTarget());
    if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
      UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because the "
                          "DynamicLoader suggested we prefer it",
                          unwind_plan_sp->GetSourceName().GetCString());
      return unwind_plan_sp;
    }
  }

  // Typically the NonCallSite UnwindPlan is the unwind created by inspecting
  // the assembly language instructions
  if (m_behaves_like_zeroth_frame && process) {
    auto unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite(
        process->GetTarget(), m_thread);
    if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc)) {
      if (unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) {
        // We probably have an UnwindPlan created by inspecting assembly
        // instructions. The assembly profilers work really well with compiler-
        // generated functions but hand- written assembly can be problematic.
        // We set the eh_frame based unwind plan as our fallback unwind plan if
        // instruction emulation doesn't work out even for non call sites if it
        // is available and use the architecture default unwind plan if it is
        // not available. The eh_frame unwind plan is more reliable even on non
        // call sites then the architecture default plan and for hand written
        // assembly code it is often written in a way that it valid at all
        // location what helps in the most common cases when the instruction
        // emulation fails.
        std::shared_ptr<const UnwindPlan> call_site_unwind_plan =
            func_unwinders_sp->GetUnwindPlanAtCallSite(process->GetTarget(),
                                                       m_thread);
        if (call_site_unwind_plan &&
            call_site_unwind_plan.get() != unwind_plan_sp.get() &&
            call_site_unwind_plan->GetSourceName() !=
                unwind_plan_sp->GetSourceName()) {
          m_fallback_unwind_plan_sp = call_site_unwind_plan;
        } else {
          m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp;
        }
      }
      UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because this "
                          "is the non-call site unwind plan and this is a "
                          "zeroth frame",
                          unwind_plan_sp->GetSourceName().GetCString());
      return unwind_plan_sp;
    }

    // If we're on the first instruction of a function, and we have an
    // architectural default UnwindPlan for the initial instruction of a
    // function, use that.
    if (m_current_offset == 0) {
      unwind_plan_sp =
          func_unwinders_sp->GetUnwindPlanArchitectureDefaultAtFunctionEntry(
              m_thread);
      if (unwind_plan_sp) {
        UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because we are at "
                            "the first instruction of a function",
                            unwind_plan_sp->GetSourceName().GetCString());
        return unwind_plan_sp;
      }
    }
  }

  std::shared_ptr<const UnwindPlan> unwind_plan_sp;
  // Typically this is unwind info from an eh_frame section intended for
  // exception handling; only valid at call sites
  if (process) {
    unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite(
        process->GetTarget(), m_thread);
  }
  if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp)) {
    UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because this "
                        "is the call-site unwind plan",
                        unwind_plan_sp->GetSourceName().GetCString());
    return unwind_plan_sp;
  }

  // We'd prefer to use an UnwindPlan intended for call sites when we're at a
  // call site but if we've struck out on that, fall back to using the non-
  // call-site assembly inspection UnwindPlan if possible.
  if (process) {
    unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite(
        process->GetTarget(), m_thread);
  }
  if (unwind_plan_sp &&
      unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) {
    // We probably have an UnwindPlan created by inspecting assembly
    // instructions. The assembly profilers work really well with compiler-
    // generated functions but hand- written assembly can be problematic. We
    // set the eh_frame based unwind plan as our fallback unwind plan if
    // instruction emulation doesn't work out even for non call sites if it is
    // available and use the architecture default unwind plan if it is not
    // available. The eh_frame unwind plan is more reliable even on non call
    // sites then the architecture default plan and for hand written assembly
    // code it is often written in a way that it valid at all location what
    // helps in the most common cases when the instruction emulation fails.
    std::shared_ptr<const UnwindPlan> call_site_unwind_plan =
        func_unwinders_sp->GetUnwindPlanAtCallSite(process->GetTarget(),
                                                   m_thread);
    if (call_site_unwind_plan &&
        call_site_unwind_plan.get() != unwind_plan_sp.get() &&
        call_site_unwind_plan->GetSourceName() !=
            unwind_plan_sp->GetSourceName()) {
      m_fallback_unwind_plan_sp = call_site_unwind_plan;
    } else {
      m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp;
    }
  }

  if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp)) {
    UnwindLogMsgVerbose("frame uses %s for full UnwindPlan because we "
                        "failed to find a call-site unwind plan that would work",
                        unwind_plan_sp->GetSourceName().GetCString());
    return unwind_plan_sp;
  }

  // If nothing else, use the architectural default UnwindPlan and hope that
  // does the job.
  if (arch_default_unwind_plan_sp)
    UnwindLogMsgVerbose(
        "frame uses %s for full UnwindPlan because we are falling back "
        "to the arch default plan",
        arch_default_unwind_plan_sp->GetSourceName().GetCString());
  else
    UnwindLogMsg(
        "Unable to find any UnwindPlan for full unwind of this frame.");

  return arch_default_unwind_plan_sp;
}

void RegisterContextUnwind::InvalidateAllRegisters() {
  m_frame_type = eNotAValidFrame;
}

size_t RegisterContextUnwind::GetRegisterCount() {
  return m_thread.GetRegisterContext()->GetRegisterCount();
}

const RegisterInfo *RegisterContextUnwind::GetRegisterInfoAtIndex(size_t reg) {
  return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg);
}

size_t RegisterContextUnwind::GetRegisterSetCount() {
  return m_thread.GetRegisterContext()->GetRegisterSetCount();
}

const RegisterSet *RegisterContextUnwind::GetRegisterSet(size_t reg_set) {
  return m_thread.GetRegisterContext()->GetRegisterSet(reg_set);
}

uint32_t RegisterContextUnwind::ConvertRegisterKindToRegisterNumber(
    lldb::RegisterKind kind, uint32_t num) {
  return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber(
      kind, num);
}

bool RegisterContextUnwind::ReadRegisterValueFromRegisterLocation(
    lldb_private::UnwindLLDB::ConcreteRegisterLocation regloc,
    const RegisterInfo *reg_info, RegisterValue &value) {
  if (!IsValid())
    return false;
  bool success = false;

  switch (regloc.type) {
  case UnwindLLDB::ConcreteRegisterLocation::eRegisterInLiveRegisterContext: {
    const RegisterInfo *other_reg_info =
        GetRegisterInfoAtIndex(regloc.location.register_number);

    if (!other_reg_info)
      return false;

    success =
        m_thread.GetRegisterContext()->ReadRegister(other_reg_info, value);
  } break;
  case UnwindLLDB::ConcreteRegisterLocation::eRegisterInRegister: {
    const RegisterInfo *other_reg_info =
        GetRegisterInfoAtIndex(regloc.location.register_number);

    if (!other_reg_info)
      return false;

    if (IsFrameZero()) {
      success =
          m_thread.GetRegisterContext()->ReadRegister(other_reg_info, value);
    } else {
      success = GetNextFrame()->ReadRegister(other_reg_info, value);
    }
  } break;
  case UnwindLLDB::ConcreteRegisterLocation::eRegisterIsRegisterPlusOffset: {
    auto regnum = regloc.location.reg_plus_offset.register_number;
    const RegisterInfo *other_reg_info =
        GetRegisterInfoAtIndex(regloc.location.reg_plus_offset.register_number);

    if (!other_reg_info)
      return false;

    if (IsFrameZero()) {
      success =
          m_thread.GetRegisterContext()->ReadRegister(other_reg_info, value);
    } else {
      success = GetNextFrame()->ReadRegister(other_reg_info, value);
    }
    if (success) {
      UnwindLogMsg("read (%d)'s location", regnum);
      value = value.GetAsUInt64(~0ull, &success) +
              regloc.location.reg_plus_offset.offset;
      UnwindLogMsg("success %s", success ? "yes" : "no");
    }
  } break;
  case UnwindLLDB::ConcreteRegisterLocation::eRegisterValueInferred:
    success =
        value.SetUInt(regloc.location.inferred_value, reg_info->byte_size);
    break;

  case UnwindLLDB::ConcreteRegisterLocation::eRegisterNotSaved:
    break;
  case UnwindLLDB::ConcreteRegisterLocation::eRegisterSavedAtHostMemoryLocation:
    llvm_unreachable("FIXME debugger inferior function call unwind");
  case UnwindLLDB::ConcreteRegisterLocation::eRegisterSavedAtMemoryLocation: {
    Status error(ReadRegisterValueFromMemory(
        reg_info, regloc.location.target_memory_location, reg_info->byte_size,
        value));
    success = error.Success();
  } break;
  default:
    llvm_unreachable("Unknown ConcreteRegisterLocation type.");
  }
  return success;
}

bool RegisterContextUnwind::WriteRegisterValueToRegisterLocation(
    lldb_private::UnwindLLDB::ConcreteRegisterLocation regloc,
    const RegisterInfo *reg_info, const RegisterValue &value) {
  if (!IsValid())
    return false;

  bool success = false;

  switch (regloc.type) {
  case UnwindLLDB::ConcreteRegisterLocation::eRegisterInLiveRegisterContext: {
    const RegisterInfo *other_reg_info =
        GetRegisterInfoAtIndex(regloc.location.register_number);
    success =
        m_thread.GetRegisterContext()->WriteRegister(other_reg_info, value);
  } break;
  case UnwindLLDB::ConcreteRegisterLocation::eRegisterInRegister: {
    const RegisterInfo *other_reg_info =
        GetRegisterInfoAtIndex(regloc.location.register_number);
    if (IsFrameZero()) {
      success =
          m_thread.GetRegisterContext()->WriteRegister(other_reg_info, value);
    } else {
      success = GetNextFrame()->WriteRegister(other_reg_info, value);
    }
  } break;
  case UnwindLLDB::ConcreteRegisterLocation::eRegisterIsRegisterPlusOffset:
  case UnwindLLDB::ConcreteRegisterLocation::eRegisterValueInferred:
  case UnwindLLDB::ConcreteRegisterLocation::eRegisterNotSaved:
    break;
  case UnwindLLDB::ConcreteRegisterLocation::eRegisterSavedAtHostMemoryLocation:
    llvm_unreachable("FIXME debugger inferior function call unwind");
  case UnwindLLDB::ConcreteRegisterLocation::eRegisterSavedAtMemoryLocation: {
    Status error(WriteRegisterValueToMemory(
        reg_info, regloc.location.target_memory_location, reg_info->byte_size,
        value));
    success = error.Success();
  } break;
  default:
    llvm_unreachable("Unknown ConcreteRegisterLocation type.");
  }
  return success;
}

bool RegisterContextUnwind::IsValid() const {
  return m_frame_type != eNotAValidFrame;
}

// After the final stack frame in a stack walk we'll get one invalid
// (eNotAValidFrame) stack frame -- one past the end of the stack walk.  But
// higher-level code will need to tell the difference between "the unwind plan
// below this frame failed" versus "we successfully completed the stack walk"
// so this method helps to disambiguate that.

bool RegisterContextUnwind::IsTrapHandlerFrame() const {
  return m_frame_type == eTrapHandlerFrame;
}

// A skip frame is a bogus frame on the stack -- but one where we're likely to
// find a real frame farther
// up the stack if we keep looking.  It's always the second frame in an unwind
// (i.e. the first frame after frame zero) where unwinding can be the
// trickiest.  Ideally we'll mark up this frame in some way so the user knows
// we're displaying bad data and we may have skipped one frame of their real
// program in the process of getting back on track.

bool RegisterContextUnwind::IsSkipFrame() const {
  return m_frame_type == eSkipFrame;
}

bool RegisterContextUnwind::IsTrapHandlerSymbol(
    lldb_private::Process *process,
    const lldb_private::SymbolContext &m_sym_ctx) const {
  PlatformSP platform_sp(process->GetTarget().GetPlatform());
  if (platform_sp) {
    const std::vector<ConstString> trap_handler_names(
        platform_sp->GetTrapHandlerSymbolNames());
    for (ConstString name : trap_handler_names) {
      if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) ||
          (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name)) {
        return true;
      }
    }
  }
  const std::vector<ConstString> user_specified_trap_handler_names(
      m_parent_unwind.GetUserSpecifiedTrapHandlerFunctionNames());
  for (ConstString name : user_specified_trap_handler_names) {
    if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == name) ||
        (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == name)) {
      return true;
    }
  }

  return false;
}

// Search this stack frame's UnwindPlans for the AbstractRegisterLocation
// for this register.
//
// \param[in] lldb_regnum
//     The register number (in the eRegisterKindLLDB register numbering)
//     we are searching for.
//
// \param[out] kind
//     Set to the RegisterKind of the UnwindPlan which is the basis for
//     the returned AbstractRegisterLocation; if the location is in terms
//     of another register number, this Kind is needed to interpret it
//     correctly.
//
// \return
//     An empty optional indicaTes that there was an error in processing
//     the request.
//
//     If there is no unwind rule for a volatile (caller-preserved) register,
//     the returned AbstractRegisterLocation will be IsUndefined,
//     indicating that we should stop searching.
//
//     If there is no unwind rule for a non-volatile (callee-preserved)
//     register, the returned AbstractRegisterLocation will be IsSame.
//     In frame 0, IsSame means get the value from the live register context.
//     Else it means to continue descending down the stack to more-live frames
//     looking for a location/value.
//
//     If an AbstractRegisterLocation is found in an UnwindPlan, that will
//     be returned, with no consideration of the current ABI rules for
//     registers.  Functions using an alternate ABI calling convention
//     will work as long as the UnwindPlans are exhaustive about what
//     registers are volatile/non-volatile.
std::optional<UnwindPlan::Row::AbstractRegisterLocation>
RegisterContextUnwind::GetAbstractRegisterLocation(uint32_t lldb_regnum,
                                                   lldb::RegisterKind &kind) {
  RegisterNumber regnum(m_thread, eRegisterKindLLDB, lldb_regnum);
  Log *log = GetLog(LLDBLog::Unwind);

  kind = eRegisterKindLLDB;
  UnwindPlan::Row::AbstractRegisterLocation unwindplan_regloc;

  // First, try to find a register location via the FastUnwindPlan
  if (m_fast_unwind_plan_sp) {
    const UnwindPlan::Row *active_row =
        m_fast_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
    if (regnum.GetAsKind(kind) == LLDB_INVALID_REGNUM) {
      UnwindLogMsg("could not convert lldb regnum %s (%d) into %d RegisterKind "
                   "reg numbering scheme",
                   regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
                   (int)kind);
      return {};
    }
    kind = m_fast_unwind_plan_sp->GetRegisterKind();
    // The Fast UnwindPlan typically only provides fp & pc as we move up
    // the stack, without requiring additional parsing or memory reads.
    // It may mark all other registers as IsUndefined() because, indicating
    // that it doesn't know if they were spilled to stack or not.
    // If this case, for an IsUndefined register, we should continue on
    // to the Full UnwindPlan which may have more accurate information
    // about register locations of all registers.
    if (active_row &&
        active_row->GetRegisterInfo(regnum.GetAsKind(kind),
                                    unwindplan_regloc) &&
        !unwindplan_regloc.IsUndefined()) {
      UnwindLogMsg(
          "supplying caller's saved %s (%d)'s location using FastUnwindPlan",
          regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
      return unwindplan_regloc;
    }
  }

  // Second, try to find a register location via the FullUnwindPlan.
  bool got_new_full_unwindplan = false;
  if (!m_full_unwind_plan_sp) {
    m_full_unwind_plan_sp = GetFullUnwindPlanForFrame();
    got_new_full_unwindplan = true;
  }
  if (m_full_unwind_plan_sp) {
    RegisterNumber pc_regnum(m_thread, eRegisterKindGeneric,
                             LLDB_REGNUM_GENERIC_PC);

    const UnwindPlan::Row *active_row =
        m_full_unwind_plan_sp->GetRowForFunctionOffset(
            m_current_offset_backed_up_one);
    kind = m_full_unwind_plan_sp->GetRegisterKind();

    if (got_new_full_unwindplan && active_row && log) {
      StreamString active_row_strm;
      ExecutionContext exe_ctx(m_thread.shared_from_this());
      active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread,
                       m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
      UnwindLogMsg("Using full unwind plan '%s'",
                   m_full_unwind_plan_sp->GetSourceName().AsCString());
      UnwindLogMsg("active row: %s", active_row_strm.GetData());
    }

    if (regnum.GetAsKind(kind) == LLDB_INVALID_REGNUM) {
      if (kind == eRegisterKindGeneric)
        UnwindLogMsg("could not convert lldb regnum %s (%d) into "
                     "eRegisterKindGeneric reg numbering scheme",
                     regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
      else
        UnwindLogMsg("could not convert lldb regnum %s (%d) into %d "
                     "RegisterKind reg numbering scheme",
                     regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
                     (int)kind);
      return {};
    }

    if (regnum.IsValid() && active_row &&
        active_row->GetRegisterInfo(regnum.GetAsKind(kind),
                                    unwindplan_regloc)) {
      UnwindLogMsg(
          "supplying caller's saved %s (%d)'s location using %s UnwindPlan",
          regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
          m_full_unwind_plan_sp->GetSourceName().GetCString());
      return unwindplan_regloc;
    }

    // When asking for the caller's pc, and did not find a register
    // location for PC above in the UnwindPlan.  Check if we have a
    // Return Address register on this target.
    //
    // On a Return Address Register architecture like arm/mips/riscv,
    // the caller's pc is in the RA register, and will be spilled to
    // stack before any other function is called.  If no function
    // has been called yet, the return address may still be in the
    // live RA reg.
    //
    // There's a lot of variety of what we might see in an UnwindPlan.
    // We may have
    //   ra=IsSame {unncessary}
    //   ra=StackAddr {caller's return addr spilled to stack}
    // or no reg location for pc or ra at all, in a frameless function -
    // the caller's return address is in live ra reg.
    //
    // If a function has been interrupted in a non-call way --
    // async signal/sigtramp, or a hardware exception / interrupt / fault --
    // then the "pc" and "ra" are two distinct values, and must be
    // handled separately.  The "pc" is the pc value at the point
    // the function was interrupted.  The "ra" is the return address
    // register value at that point.
    // The UnwindPlan for the sigtramp/trap handler will normally have
    // register loations for both pc and lr, and so we'll have already
    // fetched them above.
    if (pc_regnum.IsValid() && pc_regnum == regnum) {
      uint32_t return_address_regnum = LLDB_INVALID_REGNUM;

      // Get the return address register number from the UnwindPlan
      // or the register set definition.
      if (m_full_unwind_plan_sp->GetReturnAddressRegister() !=
          LLDB_INVALID_REGNUM) {
        return_address_regnum =
            m_full_unwind_plan_sp->GetReturnAddressRegister();
      } else {
        RegisterNumber arch_default_ra_regnum(m_thread, eRegisterKindGeneric,
                                              LLDB_REGNUM_GENERIC_RA);
        return_address_regnum = arch_default_ra_regnum.GetAsKind(kind);
      }

      // This system is using a return address register.
      if (return_address_regnum != LLDB_INVALID_REGNUM) {
        RegisterNumber return_address_reg;
        return_address_reg.init(m_thread,
                                m_full_unwind_plan_sp->GetRegisterKind(),
                                return_address_regnum);
        UnwindLogMsg("requested caller's saved PC but this UnwindPlan uses a "
                     "RA reg; getting %s (%d) instead",
                     return_address_reg.GetName(),
                     return_address_reg.GetAsKind(eRegisterKindLLDB));

        // Do we have a location for the ra register?
        if (active_row &&
            active_row->GetRegisterInfo(return_address_reg.GetAsKind(kind),
                                        unwindplan_regloc)) {
          UnwindLogMsg("supplying caller's saved %s (%d)'s location using "
                       "%s UnwindPlan",
                       return_address_reg.GetName(),
                       return_address_reg.GetAsKind(eRegisterKindLLDB),
                       m_full_unwind_plan_sp->GetSourceName().GetCString());
          // If we have "ra=IsSame", rewrite to "ra=InRegister(ra)" because the
          // calling function thinks it is fetching "pc" and if we return an
          // IsSame register location, it will try to read pc.
          if (unwindplan_regloc.IsSame())
            unwindplan_regloc.SetInRegister(return_address_reg.GetAsKind(kind));
          return unwindplan_regloc;
        } else {
          // No unwind rule for the return address reg on frame 0, or an
          // interrupted function, means that the caller's address is still in
          // RA reg (0th frame) or the trap handler below this one (sigtramp
          // etc) has a save location for the RA reg.
          if (BehavesLikeZerothFrame()) {
            unwindplan_regloc.SetInRegister(return_address_reg.GetAsKind(kind));
            return unwindplan_regloc;
          }
        }
      }
    }
  }

  ExecutionContext exe_ctx(m_thread.shared_from_this());
  Process *process = exe_ctx.GetProcessPtr();

  // Third, try finding a register location via the ABI
  // FallbackRegisterLocation.
  //
  // If the UnwindPlan failed to give us an unwind location for this
  // register, we may be able to fall back to some ABI-defined default.  For
  // example, some ABIs allow to determine the caller's SP via the CFA. Also,
  // the ABI willset volatile registers to the undefined state.
  ABI *abi = process ? process->GetABI().get() : nullptr;
  if (abi) {
    const RegisterInfo *reg_info =
        GetRegisterInfoAtIndex(regnum.GetAsKind(eRegisterKindLLDB));
    if (reg_info &&
        abi->GetFallbackRegisterLocation(reg_info, unwindplan_regloc)) {
      if (!unwindplan_regloc.IsUndefined())
        UnwindLogMsg(
            "supplying caller's saved %s (%d)'s location using ABI default",
            regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
      // ABI defined volatile registers with no register location
      // will be returned as IsUndefined, stopping the search down
      // the stack.
      return unwindplan_regloc;
    }
  }

  // We have no AbstractRegisterLocation, and the ABI says this is a
  // non-volatile / callee-preserved register.  Continue down the stack
  // or to frame 0 & the live RegisterContext.
  std::string unwindplan_name;
  if (m_full_unwind_plan_sp) {
    unwindplan_name += "via '";
    unwindplan_name += m_full_unwind_plan_sp->GetSourceName().AsCString();
    unwindplan_name += "'";
  }
  UnwindLogMsg("no save location for %s (%d) %s", regnum.GetName(),
               regnum.GetAsKind(eRegisterKindLLDB), unwindplan_name.c_str());

  unwindplan_regloc.SetSame();
  return unwindplan_regloc;
}

// Answer the question: Where did THIS frame save the CALLER frame ("previous"
// frame)'s register value?

enum UnwindLLDB::RegisterSearchResult
RegisterContextUnwind::SavedLocationForRegister(
    uint32_t lldb_regnum,
    lldb_private::UnwindLLDB::ConcreteRegisterLocation &regloc) {
  RegisterNumber regnum(m_thread, eRegisterKindLLDB, lldb_regnum);
  Log *log = GetLog(LLDBLog::Unwind);

  // Have we already found this register location?
  if (!m_registers.empty()) {
    auto iterator = m_registers.find(regnum.GetAsKind(eRegisterKindLLDB));
    if (iterator != m_registers.end()) {
      regloc = iterator->second;
      UnwindLogMsg("supplying caller's saved %s (%d)'s location, cached",
                   regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
      return UnwindLLDB::RegisterSearchResult::eRegisterFound;
    }
  }

  RegisterKind abs_regkind;
  std::optional<UnwindPlan::Row::AbstractRegisterLocation> abs_regloc =
      GetAbstractRegisterLocation(lldb_regnum, abs_regkind);

  if (!abs_regloc)
    return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;

  if (abs_regloc->IsUndefined()) {
    UnwindLogMsg(
        "did not supply reg location for %s (%d) because it is volatile",
        regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
    return UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile;
  }

  ExecutionContext exe_ctx(m_thread.shared_from_this());
  Process *process = exe_ctx.GetProcessPtr();
  // abs_regloc has valid contents about where to retrieve the register
  if (abs_regloc->IsUnspecified()) {
    lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc = {};
    new_regloc.type = UnwindLLDB::ConcreteRegisterLocation::eRegisterNotSaved;
    m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc;
    UnwindLogMsg("save location for %s (%d) is unspecified, continue searching",
                 regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
    return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
  }

  if (abs_regloc->IsSame()) {
    if (IsFrameZero()) {
      regloc.type =
          UnwindLLDB::ConcreteRegisterLocation::eRegisterInLiveRegisterContext;
      regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB);
      m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
      UnwindLogMsg("supplying caller's register %s (%d) from the live "
                   "RegisterContext at frame 0",
                   regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
      return UnwindLLDB::RegisterSearchResult::eRegisterFound;
    }
    // PC/RA reg don't follow the usual "callee-saved aka non-volatile" versus
    // "caller saved aka volatile" system.  A stack frame can provide its caller
    // return address, but if we don't find a rule for pc/RA mid-stack, we
    // never want to iterate further down the stack looking for it.
    // Defensively prevent iterating down the stack for these two.
    if (!BehavesLikeZerothFrame() &&
        (regnum.GetAsKind(eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_PC ||
         regnum.GetAsKind(eRegisterKindGeneric) == LLDB_REGNUM_GENERIC_RA)) {
      UnwindLogMsg("register %s (%d) is marked as 'IsSame' - it is a pc or "
                   "return address reg on a frame which does not have all "
                   "registers available -- treat as if we have no information",
                   regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
      return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
    }

    regloc.type = UnwindLLDB::ConcreteRegisterLocation::eRegisterInRegister;
    regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB);
    m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
    UnwindLogMsg(
        "supplying caller's register %s (%d) value is unmodified in this frame",
        regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
    return UnwindLLDB::RegisterSearchResult::eRegisterFound;
  }

  if (abs_regloc->IsCFAPlusOffset()) {
    int offset = abs_regloc->GetOffset();
    regloc.type = UnwindLLDB::ConcreteRegisterLocation::eRegisterValueInferred;
    regloc.location.inferred_value = m_cfa + offset;
    m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
    UnwindLogMsg("supplying caller's register %s (%d), value is CFA plus "
                 "offset %d [value is 0x%" PRIx64 "]",
                 regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset,
                 regloc.location.inferred_value);
    return UnwindLLDB::RegisterSearchResult::eRegisterFound;
  }

  if (abs_regloc->IsAtCFAPlusOffset()) {
    int offset = abs_regloc->GetOffset();
    regloc.type =
        UnwindLLDB::ConcreteRegisterLocation::eRegisterSavedAtMemoryLocation;
    regloc.location.target_memory_location = m_cfa + offset;
    m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
    UnwindLogMsg("supplying caller's register %s (%d) from the stack, saved at "
                 "CFA plus offset %d [saved at 0x%" PRIx64 "]",
                 regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset,
                 regloc.location.target_memory_location);
    return UnwindLLDB::RegisterSearchResult::eRegisterFound;
  }

  if (abs_regloc->IsAFAPlusOffset()) {
    if (m_afa == LLDB_INVALID_ADDRESS)
        return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;

    int offset = abs_regloc->GetOffset();
    regloc.type = UnwindLLDB::ConcreteRegisterLocation::eRegisterValueInferred;
    regloc.location.inferred_value = m_afa + offset;
    m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
    UnwindLogMsg("supplying caller's register %s (%d), value is AFA plus "
                 "offset %d [value is 0x%" PRIx64 "]",
                 regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset,
                 regloc.location.inferred_value);
    return UnwindLLDB::RegisterSearchResult::eRegisterFound;
  }

  if (abs_regloc->IsAtAFAPlusOffset()) {
    if (m_afa == LLDB_INVALID_ADDRESS)
        return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;

    int offset = abs_regloc->GetOffset();
    regloc.type =
        UnwindLLDB::ConcreteRegisterLocation::eRegisterSavedAtMemoryLocation;
    regloc.location.target_memory_location = m_afa + offset;
    m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
    UnwindLogMsg("supplying caller's register %s (%d) from the stack, saved at "
                 "AFA plus offset %d [saved at 0x%" PRIx64 "]",
                 regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), offset,
                 regloc.location.target_memory_location);
    return UnwindLLDB::RegisterSearchResult::eRegisterFound;
  }

  if (abs_regloc->IsInOtherRegister()) {
    RegisterNumber row_regnum(m_thread, abs_regkind,
                              abs_regloc->GetRegisterNumber());
    if (row_regnum.GetAsKind(eRegisterKindLLDB) == LLDB_INVALID_REGNUM) {
      UnwindLogMsg("could not supply caller's %s (%d) location - was saved in "
                   "another reg but couldn't convert that regnum",
                   regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
      return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
    }
    regloc.type = UnwindLLDB::ConcreteRegisterLocation::eRegisterInRegister;
    regloc.location.register_number = row_regnum.GetAsKind(eRegisterKindLLDB);
    m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
    UnwindLogMsg(
        "supplying caller's register %s (%d), saved in register %s (%d)",
        regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
        row_regnum.GetName(), row_regnum.GetAsKind(eRegisterKindLLDB));
    return UnwindLLDB::RegisterSearchResult::eRegisterFound;
  }

  if (abs_regloc->IsDWARFExpression() || abs_regloc->IsAtDWARFExpression()) {
    DataExtractor dwarfdata(abs_regloc->GetDWARFExpressionBytes(),
                            abs_regloc->GetDWARFExpressionLength(),
                            process->GetByteOrder(),
                            process->GetAddressByteSize());
    ModuleSP opcode_ctx;
    DWARFExpressionList dwarfexpr(opcode_ctx, dwarfdata, nullptr);
    dwarfexpr.GetMutableExpressionAtAddress()->SetRegisterKind(abs_regkind);
    Value cfa_val = Scalar(m_cfa);
    cfa_val.SetValueType(Value::ValueType::LoadAddress);
    llvm::Expected<Value> result =
        dwarfexpr.Evaluate(&exe_ctx, this, 0, &cfa_val, nullptr);
    if (!result) {
      LLDB_LOG_ERROR(log, result.takeError(),
                     "DWARF expression failed to evaluate: {0}");
    } else {
      addr_t val;
      val = result->GetScalar().ULongLong();
      if (abs_regloc->IsDWARFExpression()) {
        regloc.type =
            UnwindLLDB::ConcreteRegisterLocation::eRegisterValueInferred;
        regloc.location.inferred_value = val;
        m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
        UnwindLogMsg("supplying caller's register %s (%d) via DWARF expression "
                     "(IsDWARFExpression)",
                     regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
        return UnwindLLDB::RegisterSearchResult::eRegisterFound;
      } else {
        regloc.type = UnwindLLDB::ConcreteRegisterLocation::
            eRegisterSavedAtMemoryLocation;
        regloc.location.target_memory_location = val;
        m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
        UnwindLogMsg("supplying caller's register %s (%d) via DWARF expression "
                     "(IsAtDWARFExpression)",
                     regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
        return UnwindLLDB::RegisterSearchResult::eRegisterFound;
      }
    }
    UnwindLogMsg("tried to use IsDWARFExpression or IsAtDWARFExpression for %s "
                 "(%d) but failed",
                 regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
    return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
  }

  if (abs_regloc->IsConstant()) {
    regloc.type = UnwindLLDB::ConcreteRegisterLocation::eRegisterValueInferred;
    regloc.location.inferred_value = abs_regloc->GetConstant();
    m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc;
    UnwindLogMsg("supplying caller's register %s (%d) via constant value",
                 regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
    return UnwindLLDB::RegisterSearchResult::eRegisterFound;
  }

  UnwindLogMsg("no save location for %s (%d) in this stack frame",
               regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));

  // FIXME UnwindPlan::Row types atDWARFExpression and isDWARFExpression are
  // unsupported.

  return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
}

UnwindPlanSP RegisterContextUnwind::TryAdoptArchitectureUnwindPlan() {
  if (!m_full_unwind_plan_sp)
    return {};
  ProcessSP process_sp = m_thread.GetProcess();
  if (!process_sp)
    return {};

  UnwindPlanSP arch_override_plan_sp;
  if (Architecture *arch = process_sp->GetTarget().GetArchitecturePlugin())
    arch_override_plan_sp =
        arch->GetArchitectureUnwindPlan(m_thread, this, m_full_unwind_plan_sp);

  if (arch_override_plan_sp) {
    m_full_unwind_plan_sp = arch_override_plan_sp;
    PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp);
    m_registers.clear();
    if (GetLog(LLDBLog::Unwind)) {
      UnwindLogMsg(
          "Replacing Full Unwindplan with Architecture UnwindPlan, '%s'",
          m_full_unwind_plan_sp->GetSourceName().AsCString());
      const UnwindPlan::Row *active_row =
          m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
      if (active_row) {
        StreamString active_row_strm;
        active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(),
                         &m_thread,
                         m_start_pc.GetLoadAddress(&process_sp->GetTarget()));
        UnwindLogMsg("%s", active_row_strm.GetData());
      }
    }
  }

  return {};
}

// TryFallbackUnwindPlan() -- this method is a little tricky.
//
// When this is called, the frame above -- the caller frame, the "previous"
// frame -- is invalid or bad.
//
// Instead of stopping the stack walk here, we'll try a different UnwindPlan
// and see if we can get a valid frame above us.
//
// This most often happens when an unwind plan based on assembly instruction
// inspection is not correct -- mostly with hand-written assembly functions or
// functions where the stack frame is set up "out of band", e.g. the kernel
// saved the register context and then called an asynchronous trap handler like
// _sigtramp.
//
// Often in these cases, if we just do a dumb stack walk we'll get past this
// tricky frame and our usual techniques can continue to be used.

bool RegisterContextUnwind::TryFallbackUnwindPlan() {
  if (m_fallback_unwind_plan_sp == nullptr)
    return false;

  if (m_full_unwind_plan_sp == nullptr)
    return false;

  if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() ||
      m_full_unwind_plan_sp->GetSourceName() ==
          m_fallback_unwind_plan_sp->GetSourceName()) {
    return false;
  }

  // If a compiler generated unwind plan failed, trying the arch default
  // unwindplan isn't going to do any better.
  if (m_full_unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes)
    return false;

  // Get the caller's pc value and our own CFA value. Swap in the fallback
  // unwind plan, re-fetch the caller's pc value and CFA value. If they're the
  // same, then the fallback unwind plan provides no benefit.

  RegisterNumber pc_regnum(m_thread, eRegisterKindGeneric,
                           LLDB_REGNUM_GENERIC_PC);

  addr_t old_caller_pc_value = LLDB_INVALID_ADDRESS;
  addr_t new_caller_pc_value = LLDB_INVALID_ADDRESS;
  UnwindLLDB::ConcreteRegisterLocation regloc = {};
  if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB),
                               regloc) ==
      UnwindLLDB::RegisterSearchResult::eRegisterFound) {
    const RegisterInfo *reg_info =
        GetRegisterInfoAtIndex(pc_regnum.GetAsKind(eRegisterKindLLDB));
    if (reg_info) {
      RegisterValue reg_value;
      if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, reg_value)) {
        old_caller_pc_value = reg_value.GetAsUInt64();
        if (ProcessSP process_sp = m_thread.GetProcess()) {
          if (ABISP abi_sp = process_sp->GetABI())
            old_caller_pc_value = abi_sp->FixCodeAddress(old_caller_pc_value);
        }
      }
    }
  }

  // This is a tricky wrinkle!  If SavedLocationForRegister() detects a really
  // impossible register location for the full unwind plan, it may call
  // ForceSwitchToFallbackUnwindPlan() which in turn replaces the full
  // unwindplan with the fallback... in short, we're done, we're using the
  // fallback UnwindPlan. We checked if m_fallback_unwind_plan_sp was nullptr
  // at the top -- the only way it became nullptr since then is via
  // SavedLocationForRegister().
  if (m_fallback_unwind_plan_sp == nullptr)
    return true;

  // Switch the full UnwindPlan to be the fallback UnwindPlan.  If we decide
  // this isn't working, we need to restore. We'll also need to save & restore
  // the value of the m_cfa ivar.  Save is down below a bit in 'old_cfa'.
  std::shared_ptr<const UnwindPlan> original_full_unwind_plan_sp =
      m_full_unwind_plan_sp;
  addr_t old_cfa = m_cfa;
  addr_t old_afa = m_afa;

  m_registers.clear();

  m_full_unwind_plan_sp = m_fallback_unwind_plan_sp;

  const UnwindPlan::Row *active_row =
      m_fallback_unwind_plan_sp->GetRowForFunctionOffset(
          m_current_offset_backed_up_one);

  if (active_row &&
      active_row->GetCFAValue().GetValueType() !=
          UnwindPlan::Row::FAValue::unspecified) {
    addr_t new_cfa;
    ProcessSP process_sp = m_thread.GetProcess();
    ABISP abi_sp = process_sp ? process_sp->GetABI() : nullptr;
    if (!ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
                          active_row->GetCFAValue(), new_cfa) ||
        !CallFrameAddressIsValid(abi_sp, new_cfa)) {
      UnwindLogMsg("failed to get cfa with fallback unwindplan");
      m_fallback_unwind_plan_sp.reset();
      m_full_unwind_plan_sp = original_full_unwind_plan_sp;
      return false;
    }
    m_cfa = new_cfa;

    ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
                     active_row->GetAFAValue(), m_afa);

    if (SavedLocationForRegister(pc_regnum.GetAsKind(eRegisterKindLLDB),
                                 regloc) ==
        UnwindLLDB::RegisterSearchResult::eRegisterFound) {
      const RegisterInfo *reg_info =
          GetRegisterInfoAtIndex(pc_regnum.GetAsKind(eRegisterKindLLDB));
      if (reg_info) {
        RegisterValue reg_value;
        if (ReadRegisterValueFromRegisterLocation(regloc, reg_info,
                                                  reg_value)) {
          new_caller_pc_value = reg_value.GetAsUInt64();
          if (process_sp)
            new_caller_pc_value =
                process_sp->FixCodeAddress(new_caller_pc_value);
        }
      }
    }

    if (new_caller_pc_value == LLDB_INVALID_ADDRESS) {
      UnwindLogMsg("failed to get a pc value for the caller frame with the "
                   "fallback unwind plan");
      m_fallback_unwind_plan_sp.reset();
      m_full_unwind_plan_sp = original_full_unwind_plan_sp;
      m_cfa = old_cfa;
      m_afa = old_afa;
      return false;
    }

    if (old_caller_pc_value == new_caller_pc_value &&
        m_cfa == old_cfa &&
        m_afa == old_afa) {
      UnwindLogMsg("fallback unwind plan got the same values for this frame "
                   "CFA and caller frame pc, not using");
      m_fallback_unwind_plan_sp.reset();
      m_full_unwind_plan_sp = original_full_unwind_plan_sp;
      return false;
    }

    UnwindLogMsg("trying to unwind from this function with the UnwindPlan '%s' "
                 "because UnwindPlan '%s' failed.",
                 m_fallback_unwind_plan_sp->GetSourceName().GetCString(),
                 original_full_unwind_plan_sp->GetSourceName().GetCString());

    // We've copied the fallback unwind plan into the full - now clear the
    // fallback.
    m_fallback_unwind_plan_sp.reset();
    PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp);
  }

  return true;
}

bool RegisterContextUnwind::ForceSwitchToFallbackUnwindPlan() {
  if (m_fallback_unwind_plan_sp == nullptr)
    return false;

  if (m_full_unwind_plan_sp == nullptr)
    return false;

  if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() ||
      m_full_unwind_plan_sp->GetSourceName() ==
          m_fallback_unwind_plan_sp->GetSourceName()) {
    return false;
  }

  const UnwindPlan::Row *active_row =
      m_fallback_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);

  if (active_row &&
      active_row->GetCFAValue().GetValueType() !=
          UnwindPlan::Row::FAValue::unspecified) {
    addr_t new_cfa;
    ProcessSP process_sp = m_thread.GetProcess();
    ABISP abi_sp = process_sp ? process_sp->GetABI() : nullptr;
    if (!ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
                          active_row->GetCFAValue(), new_cfa) ||
        !CallFrameAddressIsValid(abi_sp, new_cfa)) {
      UnwindLogMsg("failed to get cfa with fallback unwindplan");
      m_fallback_unwind_plan_sp.reset();
      return false;
    }

    ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
                     active_row->GetAFAValue(), m_afa);

    m_full_unwind_plan_sp = m_fallback_unwind_plan_sp;
    m_fallback_unwind_plan_sp.reset();

    m_registers.clear();

    m_cfa = new_cfa;

    PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp);

    UnwindLogMsg("switched unconditionally to the fallback unwindplan %s",
                 m_full_unwind_plan_sp->GetSourceName().GetCString());
    return true;
  }
  return false;
}

void RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan(
    std::shared_ptr<const UnwindPlan> unwind_plan) {
  if (unwind_plan->GetUnwindPlanForSignalTrap() != eLazyBoolYes) {
    // Unwind plan does not indicate trap handler.  Do nothing.  We may
    // already be flagged as trap handler flag due to the symbol being
    // in the trap handler symbol list, and that should take precedence.
    return;
  } else if (m_frame_type != eNormalFrame) {
    // If this is already a trap handler frame, nothing to do.
    // If this is a skip or debug or invalid frame, don't override that.
    return;
  }

  m_frame_type = eTrapHandlerFrame;
  UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan");

  if (m_current_offset_backed_up_one != m_current_offset) {
    // We backed up the pc by 1 to compute the symbol context, but
    // now need to undo that because the pc of the trap handler
    // frame may in fact be the first instruction of a signal return
    // trampoline, rather than the instruction after a call.  This
    // happens on systems where the signal handler dispatch code, rather
    // than calling the handler and being returned to, jumps to the
    // handler after pushing the address of a return trampoline on the
    // stack -- on these systems, when the handler returns, control will
    // be transferred to the return trampoline, so that's the best
    // symbol we can present in the callstack.
    UnwindLogMsg("Resetting current offset and re-doing symbol lookup; "
                 "old symbol was %s",
                 GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));
    m_current_offset_backed_up_one = m_current_offset;

    m_sym_ctx_valid = m_current_pc.ResolveFunctionScope(m_sym_ctx);

    UnwindLogMsg("Symbol is now %s",
                 GetSymbolOrFunctionName(m_sym_ctx).AsCString(""));

    ExecutionContext exe_ctx(m_thread.shared_from_this());
    Process *process = exe_ctx.GetProcessPtr();
    Target *target = &process->GetTarget();

    if (m_sym_ctx_valid) {
      m_start_pc = m_sym_ctx.GetFunctionOrSymbolAddress();
      m_current_offset = m_current_pc.GetLoadAddress(target) -
                         m_start_pc.GetLoadAddress(target);
    }
  }
}

bool RegisterContextUnwind::ReadFrameAddress(
    lldb::RegisterKind row_register_kind, const UnwindPlan::Row::FAValue &fa,
    addr_t &address) {
  RegisterValue reg_value;

  address = LLDB_INVALID_ADDRESS;
  addr_t cfa_reg_contents;
  ABISP abi_sp = m_thread.GetProcess()->GetABI();

  switch (fa.GetValueType()) {
  case UnwindPlan::Row::FAValue::isRegisterDereferenced: {
    UnwindLogMsg("CFA value via dereferencing reg");
    RegisterNumber regnum_to_deref(m_thread, row_register_kind,
                                   fa.GetRegisterNumber());
    addr_t reg_to_deref_contents;
    if (ReadGPRValue(regnum_to_deref, reg_to_deref_contents)) {
      const RegisterInfo *reg_info =
          GetRegisterInfoAtIndex(regnum_to_deref.GetAsKind(eRegisterKindLLDB));
      RegisterValue reg_value;
      if (reg_info) {
        Status error = ReadRegisterValueFromMemory(
            reg_info, reg_to_deref_contents, reg_info->byte_size, reg_value);
        if (error.Success()) {
          address = reg_value.GetAsUInt64();
          UnwindLogMsg(
              "CFA value via dereferencing reg %s (%d): reg has val 0x%" PRIx64
              ", CFA value is 0x%" PRIx64,
              regnum_to_deref.GetName(),
              regnum_to_deref.GetAsKind(eRegisterKindLLDB),
              reg_to_deref_contents, address);
          return true;
        } else {
          UnwindLogMsg("Tried to deref reg %s (%d) [0x%" PRIx64
                       "] but memory read failed.",
                       regnum_to_deref.GetName(),
                       regnum_to_deref.GetAsKind(eRegisterKindLLDB),
                       reg_to_deref_contents);
        }
      }
    }
    break;
  }
  case UnwindPlan::Row::FAValue::isRegisterPlusOffset: {
    UnwindLogMsg("CFA value via register plus offset");
    RegisterNumber cfa_reg(m_thread, row_register_kind,
                           fa.GetRegisterNumber());
    if (ReadGPRValue(cfa_reg, cfa_reg_contents)) {
      if (!CallFrameAddressIsValid(abi_sp, cfa_reg_contents)) {
        UnwindLogMsg(
            "Got an invalid CFA register value - reg %s (%d), value 0x%" PRIx64,
            cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB),
            cfa_reg_contents);
        return false;
      }
      address = cfa_reg_contents + fa.GetOffset();
      UnwindLogMsg(
          "CFA is 0x%" PRIx64 ": Register %s (%d) contents are 0x%" PRIx64
          ", offset is %d",
          address, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB),
          cfa_reg_contents, fa.GetOffset());
      return true;
    } else
      UnwindLogMsg("unable to read CFA register %s (%d)", cfa_reg.GetName(),
                   cfa_reg.GetAsKind(eRegisterKindLLDB));
    break;
  }
  case UnwindPlan::Row::FAValue::isDWARFExpression: {
    UnwindLogMsg("CFA value via DWARF expression");
    ExecutionContext exe_ctx(m_thread.shared_from_this());
    Process *process = exe_ctx.GetProcessPtr();
    DataExtractor dwarfdata(fa.GetDWARFExpressionBytes(),
                            fa.GetDWARFExpressionLength(),
                            process->GetByteOrder(),
                            process->GetAddressByteSize());
    ModuleSP opcode_ctx;
    DWARFExpressionList dwarfexpr(opcode_ctx, dwarfdata, nullptr);
    dwarfexpr.GetMutableExpressionAtAddress()->SetRegisterKind(
        row_register_kind);
    llvm::Expected<Value> result =
        dwarfexpr.Evaluate(&exe_ctx, this, 0, nullptr, nullptr);
    if (result) {
      address = result->GetScalar().ULongLong();
      UnwindLogMsg("CFA value set by DWARF expression is 0x%" PRIx64,
                   address);
      return true;
    }
    UnwindLogMsg("Failed to set CFA value via DWARF expression: %s",
                 llvm::toString(result.takeError()).c_str());
    break;
  }
  case UnwindPlan::Row::FAValue::isRaSearch: {
    UnwindLogMsg("CFA value via heuristic search");
    Process &process = *m_thread.GetProcess();
    lldb::addr_t return_address_hint = GetReturnAddressHint(fa.GetOffset());
    if (return_address_hint == LLDB_INVALID_ADDRESS)
      return false;
    const unsigned max_iterations = 256;
    for (unsigned i = 0; i < max_iterations; ++i) {
      Status st;
      lldb::addr_t candidate_addr =
          return_address_hint + i * process.GetAddressByteSize();
      lldb::addr_t candidate =
          process.ReadPointerFromMemory(candidate_addr, st);
      if (st.Fail()) {
        UnwindLogMsg("Cannot read memory at 0x%" PRIx64 ": %s", candidate_addr,
                     st.AsCString());
        return false;
      }
      Address addr;
      uint32_t permissions;
      if (process.GetLoadAddressPermissions(candidate, permissions) &&
          permissions & lldb::ePermissionsExecutable) {
        address = candidate_addr;
        UnwindLogMsg("Heuristically found CFA: 0x%" PRIx64, address);
        return true;
      }
    }
    UnwindLogMsg("No suitable CFA found");
    break;
  }
  case UnwindPlan::Row::FAValue::isConstant: {
    address = fa.GetConstant();
    UnwindLogMsg("CFA value set by constant is 0x%" PRIx64, address);
    return true;
  }
  default:
    return false;
  }
  return false;
}

lldb::addr_t RegisterContextUnwind::GetReturnAddressHint(int32_t plan_offset) {
  addr_t hint;
  if (!ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, hint))
    return LLDB_INVALID_ADDRESS;
  if (!m_sym_ctx.module_sp || !m_sym_ctx.symbol)
    return LLDB_INVALID_ADDRESS;
  if (ABISP abi_sp = m_thread.GetProcess()->GetABI())
    hint = abi_sp->FixCodeAddress(hint);

  hint += plan_offset;

  if (auto next = GetNextFrame()) {
    if (!next->m_sym_ctx.module_sp || !next->m_sym_ctx.symbol)
      return LLDB_INVALID_ADDRESS;
    if (auto expected_size =
            next->m_sym_ctx.module_sp->GetSymbolFile()->GetParameterStackSize(
                *next->m_sym_ctx.symbol))
      hint += *expected_size;
    else {
      UnwindLogMsgVerbose("Could not retrieve parameter size: %s",
                          llvm::toString(expected_size.takeError()).c_str());
      return LLDB_INVALID_ADDRESS;
    }
  }
  return hint;
}

// Retrieve a general purpose register value for THIS frame, as saved by the
// NEXT frame, i.e. the frame that
// this frame called.  e.g.
//
//  foo () { }
//  bar () { foo (); }
//  main () { bar (); }
//
//  stopped in foo() so
//     frame 0 - foo
//     frame 1 - bar
//     frame 2 - main
//  and this RegisterContext is for frame 1 (bar) - if we want to get the pc
//  value for frame 1, we need to ask
//  where frame 0 (the "next" frame) saved that and retrieve the value.

bool RegisterContextUnwind::ReadGPRValue(lldb::RegisterKind register_kind,
                                         uint32_t regnum, addr_t &value) {
  if (!IsValid())
    return false;

  uint32_t lldb_regnum;
  if (register_kind == eRegisterKindLLDB) {
    lldb_regnum = regnum;
  } else if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds(
                 register_kind, regnum, eRegisterKindLLDB, lldb_regnum)) {
    return false;
  }

  const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum);
  assert(reg_info);
  if (!reg_info) {
    UnwindLogMsg(
        "Could not find RegisterInfo definition for lldb register number %d",
        lldb_regnum);
    return false;
  }

  uint32_t generic_regnum = LLDB_INVALID_REGNUM;
  if (register_kind == eRegisterKindGeneric)
    generic_regnum = regnum;
  else
    m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds(
        register_kind, regnum, eRegisterKindGeneric, generic_regnum);
  ABISP abi_sp = m_thread.GetProcess()->GetABI();

  RegisterValue reg_value;
  // if this is frame 0 (currently executing frame), get the requested reg
  // contents from the actual thread registers
  if (IsFrameZero()) {
    if (m_thread.GetRegisterContext()->ReadRegister(reg_info, reg_value)) {
      value = reg_value.GetAsUInt64();
      if (abi_sp && generic_regnum != LLDB_INVALID_REGNUM) {
        if (generic_regnum == LLDB_REGNUM_GENERIC_PC ||
            generic_regnum == LLDB_REGNUM_GENERIC_RA)
          value = abi_sp->FixCodeAddress(value);
      }
      return true;
    }
    return false;
  }

  bool pc_register = false;
  if (generic_regnum != LLDB_INVALID_REGNUM &&
      (generic_regnum == LLDB_REGNUM_GENERIC_PC ||
       generic_regnum == LLDB_REGNUM_GENERIC_RA))
    pc_register = true;

  lldb_private::UnwindLLDB::ConcreteRegisterLocation regloc;
  if (!m_parent_unwind.SearchForSavedLocationForRegister(
          lldb_regnum, regloc, m_frame_number - 1, pc_register)) {
    return false;
  }
  if (ReadRegisterValueFromRegisterLocation(regloc, reg_info, reg_value)) {
    value = reg_value.GetAsUInt64();
    if (pc_register) {
      if (ABISP abi_sp = m_thread.GetProcess()->GetABI()) {
        value = abi_sp->FixCodeAddress(value);
      }
    }
    return true;
  }
  return false;
}

bool RegisterContextUnwind::ReadGPRValue(const RegisterNumber &regnum,
                                         addr_t &value) {
  return ReadGPRValue(regnum.GetRegisterKind(), regnum.GetRegisterNumber(),
                      value);
}

// Find the value of a register in THIS frame

bool RegisterContextUnwind::ReadRegister(const RegisterInfo *reg_info,
                                         RegisterValue &value) {
  if (!IsValid())
    return false;

  const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
  UnwindLogMsgVerbose("looking for register saved location for reg %d",
                      lldb_regnum);

  // If this is the 0th frame, hand this over to the live register context
  if (IsFrameZero()) {
    UnwindLogMsgVerbose("passing along to the live register context for reg %d",
                        lldb_regnum);
    return m_thread.GetRegisterContext()->ReadRegister(reg_info, value);
  }

  bool is_pc_regnum = false;
  if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC ||
      reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_RA) {
    is_pc_regnum = true;
  }

  lldb_private::UnwindLLDB::ConcreteRegisterLocation regloc;
  // Find out where the NEXT frame saved THIS frame's register contents
  if (!m_parent_unwind.SearchForSavedLocationForRegister(
          lldb_regnum, regloc, m_frame_number - 1, is_pc_regnum))
    return false;

  bool result = ReadRegisterValueFromRegisterLocation(regloc, reg_info, value);
  if (result) {
    if (is_pc_regnum && value.GetType() == RegisterValue::eTypeUInt64) {
      addr_t reg_value = value.GetAsUInt64(LLDB_INVALID_ADDRESS);
      if (reg_value != LLDB_INVALID_ADDRESS) {
        if (ABISP abi_sp = m_thread.GetProcess()->GetABI())
          value = abi_sp->FixCodeAddress(reg_value);
      }
    }
  }
  return result;
}

bool RegisterContextUnwind::WriteRegister(const RegisterInfo *reg_info,
                                          const RegisterValue &value) {
  if (!IsValid())
    return false;

  const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB];
  UnwindLogMsgVerbose("looking for register saved location for reg %d",
                      lldb_regnum);

  // If this is the 0th frame, hand this over to the live register context
  if (IsFrameZero()) {
    UnwindLogMsgVerbose("passing along to the live register context for reg %d",
                        lldb_regnum);
    return m_thread.GetRegisterContext()->WriteRegister(reg_info, value);
  }

  lldb_private::UnwindLLDB::ConcreteRegisterLocation regloc;
  // Find out where the NEXT frame saved THIS frame's register contents
  if (!m_parent_unwind.SearchForSavedLocationForRegister(
          lldb_regnum, regloc, m_frame_number - 1, false))
    return false;

  return WriteRegisterValueToRegisterLocation(regloc, reg_info, value);
}

// Don't need to implement this one
bool RegisterContextUnwind::ReadAllRegisterValues(
    lldb::WritableDataBufferSP &data_sp) {
  return false;
}

// Don't need to implement this one
bool RegisterContextUnwind::WriteAllRegisterValues(
    const lldb::DataBufferSP &data_sp) {
  return false;
}

// Retrieve the pc value for THIS from

bool RegisterContextUnwind::GetCFA(addr_t &cfa) {
  if (!IsValid()) {
    return false;
  }
  if (m_cfa == LLDB_INVALID_ADDRESS) {
    return false;
  }
  cfa = m_cfa;
  return true;
}

RegisterContextUnwind::SharedPtr RegisterContextUnwind::GetNextFrame() const {
  RegisterContextUnwind::SharedPtr regctx;
  if (m_frame_number == 0)
    return regctx;
  return m_parent_unwind.GetRegisterContextForFrameNum(m_frame_number - 1);
}

RegisterContextUnwind::SharedPtr RegisterContextUnwind::GetPrevFrame() const {
  RegisterContextUnwind::SharedPtr regctx;
  return m_parent_unwind.GetRegisterContextForFrameNum(m_frame_number + 1);
}

// Retrieve the address of the start of the function of THIS frame

bool RegisterContextUnwind::GetStartPC(addr_t &start_pc) {
  if (!IsValid())
    return false;

  if (!m_start_pc.IsValid()) {
        bool read_successfully = ReadPC (start_pc);
        if (read_successfully)
        {
            ProcessSP process_sp (m_thread.GetProcess());
            if (process_sp)
            {
              if (ABISP abi_sp = process_sp->GetABI())
                start_pc = abi_sp->FixCodeAddress(start_pc);
            }
        }
        return read_successfully;
  }
  start_pc = m_start_pc.GetLoadAddress(CalculateTarget().get());
  return true;
}

// Retrieve the current pc value for THIS frame, as saved by the NEXT frame.

bool RegisterContextUnwind::ReadPC(addr_t &pc) {
  if (!IsValid())
    return false;

  bool above_trap_handler = false;
  if (GetNextFrame().get() && GetNextFrame()->IsValid() &&
      GetNextFrame()->IsTrapHandlerFrame())
    above_trap_handler = true;

  if (ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) {
    // A pc value of 0 or 1 is impossible in the middle of the stack -- it
    // indicates the end of a stack walk.
    // On the currently executing frame (or such a frame interrupted
    // asynchronously by sigtramp et al) this may occur if code has jumped
    // through a NULL pointer -- we want to be able to unwind past that frame
    // to help find the bug.

    if (ABISP abi_sp = m_thread.GetProcess()->GetABI())
      pc = abi_sp->FixCodeAddress(pc);

    return !(m_all_registers_available == false &&
             above_trap_handler == false && (pc == 0 || pc == 1));
  } else {
    return false;
  }
}

void RegisterContextUnwind::UnwindLogMsg(const char *fmt, ...) {
  Log *log = GetLog(LLDBLog::Unwind);
  if (!log)
    return;

  va_list args;
  va_start(args, fmt);

  llvm::SmallString<0> logmsg;
  if (VASprintf(logmsg, fmt, args)) {
    LLDB_LOGF(log, "%*sth%d/fr%u %s",
              m_frame_number < 100 ? m_frame_number : 100, "",
              m_thread.GetIndexID(), m_frame_number, logmsg.c_str());
  }
  va_end(args);
}

void RegisterContextUnwind::UnwindLogMsgVerbose(const char *fmt, ...) {
  Log *log = GetLog(LLDBLog::Unwind);
  if (!log || !log->GetVerbose())
    return;

  va_list args;
  va_start(args, fmt);

  llvm::SmallString<0> logmsg;
  if (VASprintf(logmsg, fmt, args)) {
    LLDB_LOGF(log, "%*sth%d/fr%u %s",
              m_frame_number < 100 ? m_frame_number : 100, "",
              m_thread.GetIndexID(), m_frame_number, logmsg.c_str());
  }
  va_end(args);
}
