
instance: settings set frame-format <string> settings set thread-format <string> This allows users to control the information that is seen when dumping threads and frames. The default values are set such that they do what they used to do prior to changing over the the user defined formats. This allows users with terminals that can display color to make different items different colors using the escape control codes. A few alias examples that will colorize your thread and frame prompts are: settings set frame-format 'frame #${frame.index}: \033[0;33m${frame.pc}\033[0m{ \033[1;4;36m${module.file.basename}\033[0;36m ${function.name}{${function.pc-offset}}\033[0m}{ \033[0;35mat \033[1;35m${line.file.basename}:${line.number}}\033[0m\n' settings set thread-format 'thread #${thread.index}: \033[1;33mtid\033[0;33m = ${thread.id}\033[0m{, \033[0;33m${frame.pc}\033[0m}{ \033[1;4;36m${module.file.basename}\033[0;36m ${function.name}{${function.pc-offset}}\033[0m}{, \033[1;35mstop reason\033[0;35m = ${thread.stop-reason}\033[0m}{, \033[1;36mname = \033[0;36m${thread.name}\033[0m}{, \033[1;32mqueue = \033[0;32m${thread.queue}}\033[0m\n' A quick web search for "colorize terminal output" should allow you to see what you can do to make your output look like you want it. The "settings set" commands above can of course be added to your ~/.lldbinit file for permanent use. Changed the pure virtual void ExecutionContextScope::Calculate (ExecutionContext&); To: void ExecutionContextScope::CalculateExecutionContext (ExecutionContext&); I did this because this is a class that anything in the execution context heirarchy inherits from and "target->Calculate (exe_ctx)" didn't always tell you what it was really trying to do unless you look at the parameter. llvm-svn: 115485
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;
|
|
}
|
|
|