blob: b82e756310e986709edec791a399f0c301530357 [file] [log] [blame]
//===-- StackFrameList.cpp --------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/Target/StackFrameList.h"
// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Symbol/Block.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/Unwind.h"
using namespace lldb;
using namespace lldb_private;
//----------------------------------------------------------------------
// StackFrameList constructor
//----------------------------------------------------------------------
StackFrameList::StackFrameList(Thread &thread, StackFrameList *prev_frames, bool show_inline_frames) :
m_thread (thread),
m_prev_frames_ap (prev_frames),
m_show_inlined_frames (show_inline_frames),
m_mutex (Mutex::eMutexTypeRecursive),
m_unwind_frames (),
m_inline_frames (),
m_selected_frame_idx (0)
{
}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
StackFrameList::~StackFrameList()
{
}
uint32_t
StackFrameList::GetNumFrames()
{
Mutex::Locker locker (m_mutex);
if (m_show_inlined_frames)
{
if (m_inlined_info.empty())
{
Unwind *unwinder = m_thread.GetUnwinder ();
// If we are going to show inlined stack frames as actual frames,
// we need to calculate all concrete frames first, then iterate
// through all of them and count up how many inlined functions are
// in each frame. We can then fill in m_inlined_info with
// the concrete frame index and inlined depth
const uint32_t concrete_frame_count = unwinder->GetFrameCount();
addr_t pc, cfa;
InlinedFrameInfo inlined_frame_info;
StackFrameSP frame_sp;
for (uint32_t idx=0; idx<concrete_frame_count; ++idx)
{
if (idx == 0)
{
m_thread.GetRegisterContext();
frame_sp.reset (new StackFrame (0,
0,
m_thread,
m_thread.m_reg_context_sp,
m_thread.m_reg_context_sp->GetSP(),
m_thread.m_reg_context_sp->GetPC(),
NULL));
}
else
{
const bool success = unwinder->GetFrameInfoAtIndex(idx, cfa, pc);
assert (success);
frame_sp.reset (new StackFrame (m_inlined_info.size(), idx, m_thread, cfa, pc, NULL));
}
SetUnwindFrameAtIndex (idx, frame_sp);
Block *block = frame_sp->GetSymbolContext (eSymbolContextBlock).block;
inlined_frame_info.unwind_frame_index = idx;
inlined_frame_info.block = NULL;
m_inlined_info.push_back (inlined_frame_info);
if (block)
{
Block *inlined_block = block->GetContainingInlinedBlock();
if (inlined_block)
{
frame_sp->SetInlineBlockID (inlined_block->GetID());
while (inlined_block)
{
inlined_frame_info.block = inlined_block;
m_inlined_info.push_back (inlined_frame_info);
inlined_block = inlined_block->GetInlinedParent ();
}
}
}
}
}
return m_inlined_info.size();
}
else
{
if (m_unwind_frames.empty())
m_unwind_frames.resize(m_thread.GetUnwinder()->GetFrameCount());
return m_unwind_frames.size();
}
return 0;
}
lldb::StackFrameSP
StackFrameList::GetUnwindFrameAtIndex (uint32_t idx) const
{
StackFrameSP frame_sp;
if (idx < m_unwind_frames.size())
frame_sp = m_unwind_frames[idx];
return frame_sp;
}
lldb::StackFrameSP
StackFrameList::GetInlineFrameAtIndex (uint32_t idx) const
{
StackFrameSP frame_sp;
if (idx < m_inline_frames.size())
frame_sp = m_inline_frames[idx];
return frame_sp;
}
StackFrameSP
StackFrameList::GetFrameAtIndex (uint32_t idx)
{
StackFrameSP frame_sp;
{
Mutex::Locker locker (m_mutex);
if (m_show_inlined_frames)
{
frame_sp = GetInlineFrameAtIndex (idx);
}
else
{
frame_sp = GetUnwindFrameAtIndex (idx);
}
if (frame_sp.get())
return frame_sp;
// Special case the first frame (idx == 0) so that we don't need to
// know how many stack frames there are to get it. If we need any other
// frames, then we do need to know if "idx" is a valid index.
if (idx == 0)
{
// If this is the first frame, we want to share the thread register
// context with the stack frame at index zero.
m_thread.GetRegisterContext();
assert (m_thread.m_reg_context_sp.get());
frame_sp.reset (new StackFrame (0,
0,
m_thread,
m_thread.m_reg_context_sp,
m_thread.m_reg_context_sp->GetSP(),
m_thread.m_reg_context_sp->GetPC(),
NULL));
if (m_show_inlined_frames && idx + 1 < m_inlined_info.size())
{
if (m_inlined_info[idx].unwind_frame_index == m_inlined_info[idx+1].unwind_frame_index)
frame_sp->SetInlineBlockID (frame_sp->GetSymbolContext (eSymbolContextBlock).block->GetID());
}
}
else if (idx < GetNumFrames())
{
if (m_show_inlined_frames)
{
if (m_inlined_info[idx].block == NULL)
{
// Same as the concrete stack frame if block is NULL
assert (m_inlined_info[idx].unwind_frame_index < m_unwind_frames.size());
frame_sp = GetUnwindFrameAtIndex (m_inlined_info[idx].unwind_frame_index);
if (idx + 1 < m_inlined_info.size())
{
if (m_inlined_info[idx].unwind_frame_index == m_inlined_info[idx+1].unwind_frame_index)
frame_sp->SetInlineBlockID (frame_sp->GetSymbolContext (eSymbolContextBlock).block->GetID());
}
}
else
{
// We have blocks that were above an inlined function. Inlined
// functions are represented as blocks with non-NULL inline
// function info. Here we must reconstruct a frame by looking
// at the block
StackFrameSP previous_frame_sp (m_thread.GetStackFrameAtIndex (idx-1));
SymbolContext inline_sc;
Block *inlined_parent_block = m_inlined_info[idx].block->GetInlinedParent();
if (inlined_parent_block)
inlined_parent_block->CalculateSymbolContext (&inline_sc);
else
{
Block *parent_block = m_inlined_info[idx].block->GetParent();
parent_block->CalculateSymbolContext(&inline_sc);
}
Address previous_frame_lookup_addr (previous_frame_sp->GetFrameCodeAddress());
if (previous_frame_sp->GetFrameIndex() > 0 && m_inlined_info[idx-1].block == NULL)
previous_frame_lookup_addr.Slide (-1);
AddressRange range;
m_inlined_info[idx].block->GetRangeContainingAddress (previous_frame_lookup_addr, range);
const InlineFunctionInfo* inline_info = m_inlined_info[idx].block->InlinedFunctionInfo();
assert (inline_info);
inline_sc.line_entry.range.GetBaseAddress() = previous_frame_sp->GetFrameCodeAddress();
inline_sc.line_entry.file = inline_info->GetCallSite().GetFile();
inline_sc.line_entry.line = inline_info->GetCallSite().GetLine();
inline_sc.line_entry.column = inline_info->GetCallSite().GetColumn();
StackFrameSP concrete_frame_sp (GetUnwindFrameAtIndex (m_inlined_info[idx].unwind_frame_index));
assert (previous_frame_sp.get());
frame_sp.reset (new StackFrame (idx,
m_inlined_info[idx].unwind_frame_index,
m_thread,
concrete_frame_sp->GetRegisterContextSP (),
concrete_frame_sp->GetStackID().GetCallFrameAddress(), // CFA
range.GetBaseAddress(),
&inline_sc)); // The symbol context for this inline frame
if (idx + 1 < m_inlined_info.size())
{
if (m_inlined_info[idx].unwind_frame_index == m_inlined_info[idx+1].unwind_frame_index)
frame_sp->SetInlineBlockID (m_inlined_info[idx].block->GetID());
}
}
}
else
{
Unwind *unwinder = m_thread.GetUnwinder ();
if (unwinder)
{
addr_t pc, cfa;
if (unwinder->GetFrameInfoAtIndex(idx, cfa, pc))
frame_sp.reset (new StackFrame (idx, idx, m_thread, cfa, pc, NULL));
}
}
}
if (m_show_inlined_frames)
{
SetInlineFrameAtIndex(idx, frame_sp);
}
else
{
SetUnwindFrameAtIndex(idx, frame_sp);
}
return frame_sp;
}
return frame_sp;
}
bool
StackFrameList::SetUnwindFrameAtIndex (uint32_t idx, StackFrameSP &frame_sp)
{
if (idx >= m_unwind_frames.size())
m_unwind_frames.resize(idx + 1);
// Make sure allocation succeeded by checking bounds again
if (idx < m_unwind_frames.size())
{
m_unwind_frames[idx] = frame_sp;
return true;
}
return false; // resize failed, out of memory?
}
bool
StackFrameList::SetInlineFrameAtIndex (uint32_t idx, StackFrameSP &frame_sp)
{
if (idx >= m_inline_frames.size())
m_inline_frames.resize(idx + 1);
// Make sure allocation succeeded by checking bounds again
if (idx < m_inline_frames.size())
{
m_inline_frames[idx] = frame_sp;
return true;
}
return false; // resize failed, out of memory?
}
uint32_t
StackFrameList::GetSelectedFrameIndex () const
{
Mutex::Locker locker (m_mutex);
return m_selected_frame_idx;
}
uint32_t
StackFrameList::SetSelectedFrame (lldb_private::StackFrame *frame)
{
Mutex::Locker locker (m_mutex);
const_iterator pos;
const_iterator begin = m_show_inlined_frames ? m_inline_frames.begin() : m_unwind_frames.begin();
const_iterator end = m_show_inlined_frames ? m_inline_frames.end() : m_unwind_frames.end();
for (pos = begin; pos != end; ++pos)
{
if (pos->get() == frame)
{
m_selected_frame_idx = std::distance (begin, pos);
return m_selected_frame_idx;
}
}
m_selected_frame_idx = 0;
return m_selected_frame_idx;
}
// Mark a stack frame as the current frame using the frame index
void
StackFrameList::SetSelectedFrameByIndex (uint32_t idx)
{
Mutex::Locker locker (m_mutex);
m_selected_frame_idx = idx;
}
// The thread has been run, reset the number stack frames to zero so we can
// determine how many frames we have lazily.
void
StackFrameList::Clear ()
{
Mutex::Locker locker (m_mutex);
m_unwind_frames.clear();
m_inline_frames.clear();
m_inlined_info.clear();
}
void
StackFrameList::InvalidateFrames (uint32_t start_idx)
{
Mutex::Locker locker (m_mutex);
if (m_show_inlined_frames)
{
Clear();
}
else
{
const size_t num_frames = m_unwind_frames.size();
while (start_idx < num_frames)
{
m_unwind_frames[start_idx].reset();
++start_idx;
}
}
}