forked from OSchip/llvm-project
536 lines
19 KiB
C++
536 lines
19 KiB
C++
//===-- 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/Core/StreamFile.h"
|
|
#include "lldb/Symbol/Block.h"
|
|
#include "lldb/Symbol/Function.h"
|
|
#include "lldb/Symbol/Symbol.h"
|
|
#include "lldb/Target/RegisterContext.h"
|
|
#include "lldb/Target/StackFrame.h"
|
|
#include "lldb/Target/Thread.h"
|
|
#include "lldb/Target/Unwind.h"
|
|
|
|
//#define DEBUG_STACK_FRAMES 1
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
//----------------------------------------------------------------------
|
|
// StackFrameList constructor
|
|
//----------------------------------------------------------------------
|
|
StackFrameList::StackFrameList
|
|
(
|
|
Thread &thread,
|
|
const lldb::StackFrameListSP &prev_frames_sp,
|
|
bool show_inline_frames
|
|
) :
|
|
m_thread (thread),
|
|
m_prev_frames_sp (prev_frames_sp),
|
|
m_show_inlined_frames (show_inline_frames),
|
|
m_mutex (Mutex::eMutexTypeRecursive),
|
|
m_frames (),
|
|
m_selected_frame_idx (0)
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Destructor
|
|
//----------------------------------------------------------------------
|
|
StackFrameList::~StackFrameList()
|
|
{
|
|
}
|
|
|
|
|
|
uint32_t
|
|
StackFrameList::GetNumFrames (bool can_create)
|
|
{
|
|
Mutex::Locker locker (m_mutex);
|
|
|
|
if (can_create && m_frames.size() <= 1)
|
|
{
|
|
if (m_show_inlined_frames)
|
|
{
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
StreamFile s(stdout);
|
|
#endif
|
|
Unwind *unwinder = m_thread.GetUnwinder ();
|
|
addr_t pc = LLDB_INVALID_ADDRESS;
|
|
addr_t cfa = LLDB_INVALID_ADDRESS;
|
|
|
|
// 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.
|
|
const uint32_t unwind_frame_count = unwinder->GetFrameCount();
|
|
|
|
StackFrameSP unwind_frame_sp;
|
|
for (uint32_t idx=0; idx<unwind_frame_count; ++idx)
|
|
{
|
|
if (idx == 0)
|
|
{
|
|
// We might have already created frame zero, only create it
|
|
// if we need to
|
|
if (m_frames.empty())
|
|
{
|
|
cfa = m_thread.m_reg_context_sp->GetSP();
|
|
m_thread.GetRegisterContext();
|
|
unwind_frame_sp.reset (new StackFrame (m_frames.size(),
|
|
idx,
|
|
m_thread,
|
|
m_thread.m_reg_context_sp,
|
|
cfa,
|
|
m_thread.m_reg_context_sp->GetPC(),
|
|
NULL));
|
|
m_frames.push_back (unwind_frame_sp);
|
|
}
|
|
else
|
|
{
|
|
unwind_frame_sp = m_frames.front();
|
|
cfa = unwind_frame_sp->m_id.GetCallFrameAddress();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const bool success = unwinder->GetFrameInfoAtIndex(idx, cfa, pc);
|
|
assert (success);
|
|
unwind_frame_sp.reset (new StackFrame (m_frames.size(), idx, m_thread, cfa, pc, NULL));
|
|
m_frames.push_back (unwind_frame_sp);
|
|
}
|
|
|
|
Block *unwind_block = unwind_frame_sp->GetSymbolContext (eSymbolContextBlock).block;
|
|
|
|
if (unwind_block)
|
|
{
|
|
Block *inlined_block = unwind_block->GetContainingInlinedBlock();
|
|
if (inlined_block)
|
|
{
|
|
for (; inlined_block != NULL; inlined_block = inlined_block->GetInlinedParent ())
|
|
{
|
|
SymbolContext inline_sc;
|
|
Block *parent_block = inlined_block->GetInlinedParent();
|
|
|
|
const bool is_inlined_frame = parent_block != NULL;
|
|
|
|
if (parent_block == NULL)
|
|
parent_block = inlined_block->GetParent();
|
|
|
|
parent_block->CalculateSymbolContext (&inline_sc);
|
|
|
|
Address previous_frame_lookup_addr (m_frames.back()->GetFrameCodeAddress());
|
|
if (unwind_frame_sp->GetFrameIndex() > 0 && m_frames.back().get() == unwind_frame_sp.get())
|
|
previous_frame_lookup_addr.Slide (-1);
|
|
|
|
AddressRange range;
|
|
inlined_block->GetRangeContainingAddress (previous_frame_lookup_addr, range);
|
|
|
|
const InlineFunctionInfo* inline_info = inlined_block->GetInlinedFunctionInfo();
|
|
assert (inline_info);
|
|
inline_sc.line_entry.range.GetBaseAddress() = m_frames.back()->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 frame_sp(new StackFrame (m_frames.size(),
|
|
idx,
|
|
m_thread,
|
|
unwind_frame_sp->GetRegisterContextSP (),
|
|
cfa,
|
|
range.GetBaseAddress(),
|
|
&inline_sc)); // The symbol context for this inline frame
|
|
|
|
if (is_inlined_frame)
|
|
{
|
|
// Use the block with the inlined function info
|
|
// as the symbol context since we want this frame
|
|
// to have only the variables for the inlined function
|
|
frame_sp->SetSymbolContextScope (parent_block);
|
|
}
|
|
else
|
|
{
|
|
// This block is not inlined with means it has no
|
|
// inlined parents either, so we want to use the top
|
|
// most function block.
|
|
frame_sp->SetSymbolContextScope (&unwind_frame_sp->GetSymbolContext (eSymbolContextFunction).function->GetBlock(false));
|
|
}
|
|
|
|
m_frames.push_back (frame_sp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_prev_frames_sp)
|
|
{
|
|
StackFrameList *prev_frames = m_prev_frames_sp.get();
|
|
StackFrameList *curr_frames = this;
|
|
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
s.PutCString("\nprev_frames:\n");
|
|
prev_frames->Dump (&s);
|
|
s.PutCString("\ncurr_frames:\n");
|
|
curr_frames->Dump (&s);
|
|
s.EOL();
|
|
#endif
|
|
size_t curr_frame_num, prev_frame_num;
|
|
|
|
for (curr_frame_num = curr_frames->m_frames.size(), prev_frame_num = prev_frames->m_frames.size();
|
|
curr_frame_num > 0 && prev_frame_num > 0;
|
|
--curr_frame_num, --prev_frame_num)
|
|
{
|
|
const size_t curr_frame_idx = curr_frame_num-1;
|
|
const size_t prev_frame_idx = prev_frame_num-1;
|
|
StackFrameSP curr_frame_sp (curr_frames->m_frames[curr_frame_idx]);
|
|
StackFrameSP prev_frame_sp (prev_frames->m_frames[prev_frame_idx]);
|
|
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
s.Printf("\n\nCurr frame #%u ", curr_frame_idx);
|
|
if (curr_frame_sp)
|
|
curr_frame_sp->Dump (&s, true, false);
|
|
else
|
|
s.PutCString("NULL");
|
|
s.Printf("\nPrev frame #%u ", prev_frame_idx);
|
|
if (prev_frame_sp)
|
|
prev_frame_sp->Dump (&s, true, false);
|
|
else
|
|
s.PutCString("NULL");
|
|
#endif
|
|
|
|
StackFrame *curr_frame = curr_frame_sp.get();
|
|
StackFrame *prev_frame = prev_frame_sp.get();
|
|
|
|
if (curr_frame == NULL || prev_frame == NULL)
|
|
break;
|
|
|
|
// Check the stack ID to make sure they are equal
|
|
if (curr_frame->GetStackID() != prev_frame->GetStackID())
|
|
break;
|
|
|
|
prev_frame->UpdatePreviousFrameFromCurrentFrame (*curr_frame);
|
|
// Now copy the fixed up previous frame into the current frames
|
|
// so the pointer doesn't change
|
|
m_frames[curr_frame_idx] = prev_frame_sp;
|
|
//curr_frame->UpdateCurrentFrameFromPreviousFrame (*prev_frame);
|
|
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
s.Printf("\n Copying previous frame to current frame");
|
|
#endif
|
|
}
|
|
// We are done with the old stack frame list, we can release it now
|
|
m_prev_frames_sp.reset();
|
|
}
|
|
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
s.PutCString("\n\nNew frames:\n");
|
|
Dump (&s);
|
|
s.EOL();
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
m_frames.resize(m_thread.GetUnwinder()->GetFrameCount());
|
|
}
|
|
}
|
|
return m_frames.size();
|
|
}
|
|
|
|
void
|
|
StackFrameList::Dump (Stream *s)
|
|
{
|
|
if (s == NULL)
|
|
return;
|
|
Mutex::Locker locker (m_mutex);
|
|
|
|
const_iterator pos, begin = m_frames.begin(), end = m_frames.end();
|
|
for (pos = begin; pos != end; ++pos)
|
|
{
|
|
StackFrame *frame = (*pos).get();
|
|
s->Printf("%p: ", frame);
|
|
if (frame)
|
|
{
|
|
frame->GetStackID().Dump (s);
|
|
frame->DumpUsingSettingsFormat (s);
|
|
}
|
|
else
|
|
s->Printf("frame #%u", std::distance (begin, pos));
|
|
s->EOL();
|
|
}
|
|
s->EOL();
|
|
}
|
|
|
|
StackFrameSP
|
|
StackFrameList::GetFrameAtIndex (uint32_t idx)
|
|
{
|
|
StackFrameSP frame_sp;
|
|
Mutex::Locker locker (m_mutex);
|
|
if (idx < m_frames.size())
|
|
frame_sp = m_frames[idx];
|
|
|
|
if (frame_sp)
|
|
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));
|
|
|
|
SetFrameAtIndex(idx, frame_sp);
|
|
}
|
|
else if (idx < GetNumFrames())
|
|
{
|
|
if (m_show_inlined_frames)
|
|
{
|
|
// When inline frames are enabled we cache up all frames in GetNumFrames()
|
|
frame_sp = m_frames[idx];
|
|
}
|
|
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));
|
|
|
|
Function *function = frame_sp->GetSymbolContext (eSymbolContextFunction).function;
|
|
if (function)
|
|
{
|
|
// When we aren't showing inline functions we always use
|
|
// the top most function block as the scope.
|
|
frame_sp->SetSymbolContextScope (&function->GetBlock(false));
|
|
}
|
|
else
|
|
{
|
|
// Set the symbol scope from the symbol regardless if it is NULL or valid.
|
|
frame_sp->SetSymbolContextScope (frame_sp->GetSymbolContext (eSymbolContextSymbol).symbol);
|
|
}
|
|
SetFrameAtIndex(idx, frame_sp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return frame_sp;
|
|
}
|
|
|
|
bool
|
|
StackFrameList::SetFrameAtIndex (uint32_t idx, StackFrameSP &frame_sp)
|
|
{
|
|
if (idx >= m_frames.size())
|
|
m_frames.resize(idx + 1);
|
|
// Make sure allocation succeeded by checking bounds again
|
|
if (idx < m_frames.size())
|
|
{
|
|
m_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_frames.begin();
|
|
const_iterator end = m_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_frames.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_frames.size();
|
|
while (start_idx < num_frames)
|
|
{
|
|
m_frames[start_idx].reset();
|
|
++start_idx;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
StackFrameList::Merge (std::auto_ptr<StackFrameList>& curr_ap, lldb::StackFrameListSP& prev_sp)
|
|
{
|
|
Mutex::Locker curr_locker (curr_ap.get() ? curr_ap->m_mutex.GetMutex() : NULL);
|
|
Mutex::Locker prev_locker (prev_sp.get() ? prev_sp->m_mutex.GetMutex() : NULL);
|
|
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
StreamFile s(stdout);
|
|
s.PutCString("\n\nStackFrameList::Merge():\nPrev:\n");
|
|
if (prev_sp.get())
|
|
prev_sp->Dump (&s);
|
|
else
|
|
s.PutCString ("NULL");
|
|
s.PutCString("\nCurr:\n");
|
|
if (curr_ap.get())
|
|
curr_ap->Dump (&s);
|
|
else
|
|
s.PutCString ("NULL");
|
|
s.EOL();
|
|
#endif
|
|
|
|
if (curr_ap.get() == NULL || curr_ap->GetNumFrames (false) == 0)
|
|
{
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
s.PutCString("No current frames, leave previous frames alone...\n");
|
|
#endif
|
|
curr_ap.release();
|
|
return;
|
|
}
|
|
|
|
if (prev_sp.get() == NULL || prev_sp->GetNumFrames (false) == 0)
|
|
{
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
s.PutCString("No previous frames, so use current frames...\n");
|
|
#endif
|
|
// We either don't have any previous frames, or since we have more than
|
|
// one current frames it means we have all the frames and can safely
|
|
// replace our previous frames.
|
|
prev_sp.reset (curr_ap.release());
|
|
return;
|
|
}
|
|
|
|
const uint32_t num_curr_frames = curr_ap->GetNumFrames (false);
|
|
|
|
if (num_curr_frames > 1)
|
|
{
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
s.PutCString("We have more than one current frame, so use current frames...\n");
|
|
#endif
|
|
// We have more than one current frames it means we have all the frames
|
|
// and can safely replace our previous frames.
|
|
prev_sp.reset (curr_ap.release());
|
|
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
s.PutCString("\nMerged:\n");
|
|
prev_sp->Dump (&s);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
StackFrameSP prev_frame_zero_sp(prev_sp->GetFrameAtIndex (0));
|
|
StackFrameSP curr_frame_zero_sp(curr_ap->GetFrameAtIndex (0));
|
|
StackID curr_stack_id (curr_frame_zero_sp->GetStackID());
|
|
StackID prev_stack_id (prev_frame_zero_sp->GetStackID());
|
|
|
|
//const uint32_t num_prev_frames = prev_sp->GetNumFrames (false);
|
|
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
s.Printf("\n%u previous frames with one current frame\n", num_prev_frames);
|
|
#endif
|
|
|
|
// We have only a single current frame
|
|
// Our previous stack frames only had a single frame as well...
|
|
if (curr_stack_id == prev_stack_id)
|
|
{
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
s.Printf("\nPrevious frame #0 is same as current frame #0, merge the cached data\n");
|
|
#endif
|
|
|
|
curr_frame_zero_sp->UpdateCurrentFrameFromPreviousFrame (*prev_frame_zero_sp);
|
|
// prev_frame_zero_sp->UpdatePreviousFrameFromCurrentFrame (*curr_frame_zero_sp);
|
|
// prev_sp->SetFrameAtIndex (0, prev_frame_zero_sp);
|
|
}
|
|
else if (curr_stack_id < prev_stack_id)
|
|
{
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
s.Printf("\nCurrent frame #0 has a stack ID that is less than the previous frame #0, insert current frame zero in front of previous\n");
|
|
#endif
|
|
prev_sp->m_frames.insert (prev_sp->m_frames.begin(), curr_frame_zero_sp);
|
|
}
|
|
|
|
curr_ap.release();
|
|
|
|
#if defined (DEBUG_STACK_FRAMES)
|
|
s.PutCString("\nMerged:\n");
|
|
prev_sp->Dump (&s);
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
lldb::StackFrameSP
|
|
StackFrameList::GetStackFrameSPForStackFramePtr (StackFrame *stack_frame_ptr)
|
|
{
|
|
const_iterator pos;
|
|
const_iterator begin = m_frames.begin();
|
|
const_iterator end = m_frames.end();
|
|
lldb::StackFrameSP ret_sp;
|
|
|
|
for (pos = begin; pos != end; ++pos)
|
|
{
|
|
if (pos->get() == stack_frame_ptr)
|
|
{
|
|
ret_sp = (*pos);
|
|
break;
|
|
}
|
|
}
|
|
return ret_sp;
|
|
}
|
|
|