Check in the native lldb unwinder.  

Not yet enabled as the default unwinder but there are no known
backtrace problems with the code at this point.

Added 'log enable lldb unwind' to help diagnose backtrace problems;
this output needs a little refining but it's a good first step.

eh_frame information is currently read unconditionally - the code
is structured to allow this to be delayed until it's actually needed.
There is a performance hit when you have to parse the eh_frame
information for any largeish executable/library so it's necessary
to avoid if possible.

It's confusing having both the UnwindPlan::RegisterLocation struct
and the RegisterConextLLDB::RegisterLocation struct, I need to rename
one of them.

The writing of registers isn't done in the RegisterConextLLDB subclass
yet; neither is the running of complex DWARF expressions from eh_frame
(e.g. used for _sigtramp on Mac OS X).



git-svn-id: https://llvm.org/svn/llvm-project/llvdb/trunk@117256 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/Plugins/Process/Utility/ArchDefaultUnwindPlan-x86.cpp b/source/Plugins/Process/Utility/ArchDefaultUnwindPlan-x86.cpp
index 1a4f18b..289429d 100644
--- a/source/Plugins/Process/Utility/ArchDefaultUnwindPlan-x86.cpp
+++ b/source/Plugins/Process/Utility/ArchDefaultUnwindPlan-x86.cpp
@@ -64,6 +64,7 @@
     row.SetRegisterInfo (LLDB_REGNUM_GENERIC_SP, regloc);
 
     m_32bit_default.AppendRow (row);
+    m_32bit_default.SetSourceName ("architectural default");
 
     row.Clear();
 
@@ -80,6 +81,7 @@
     row.SetRegisterInfo (LLDB_REGNUM_GENERIC_SP, regloc);
 
     m_64bit_default.AppendRow (row);
+    m_64bit_default.SetSourceName ("architectural default");
 }
 
 
diff --git a/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
new file mode 100644
index 0000000..7a65792
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
@@ -0,0 +1,986 @@
+//===-- RegisterContextLLDB.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/lldb-private.h"
+#include "RegisterContextLLDB.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/ArchDefaultUnwindPlan.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Utility/ArchVolatileRegs.h"
+#include "lldb/Core/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+RegisterContextLLDB::RegisterContextLLDB (Thread& thread, 
+                                          const RegisterContextSP &next_frame,
+                                          SymbolContext& sym_ctx,
+                                          int frame_number) :
+    RegisterContext (thread), m_thread(thread), m_next_frame(next_frame), 
+    m_zeroth_frame(false), m_sym_ctx(sym_ctx), m_all_registers_available(false), m_registers(),
+    m_cfa (LLDB_INVALID_ADDRESS), m_start_pc (), m_frame_number (frame_number)
+{
+    m_base_reg_ctx = m_thread.GetRegisterContext();
+    if (m_next_frame.get() == NULL)
+    {
+        InitializeZerothFrame ();
+    }
+    else
+    {
+        InitializeNonZerothFrame ();
+    }
+}
+
+// Initialize a RegisterContextLLDB which is the first frame of a stack -- the zeroth frame or currently
+// executing frame.
+
+void
+RegisterContextLLDB::InitializeZerothFrame()
+{
+    m_zeroth_frame = true;
+    StackFrameSP frame_sp (m_thread.GetStackFrameAtIndex (0));
+    if (m_base_reg_ctx == NULL)
+    {
+        m_frame_type = eNotAValidFrame;
+        return;
+    }
+    m_sym_ctx = frame_sp->GetSymbolContext (eSymbolContextEverything);
+    const AddressRange *addr_range_ptr;
+    if (m_sym_ctx.function)
+        addr_range_ptr = &m_sym_ctx.function->GetAddressRange();
+    else if (m_sym_ctx.symbol)
+        addr_range_ptr = m_sym_ctx.symbol->GetAddressRangePtr();
+
+    Address current_pc = frame_sp->GetFrameCodeAddress();
+
+    static ConstString sigtramp_name ("_sigtramp");
+    if ((m_sym_ctx.function && m_sym_ctx.function->GetMangled().GetMangledName() == sigtramp_name)
+        || (m_sym_ctx.symbol && m_sym_ctx.symbol->GetMangled().GetMangledName() == sigtramp_name))
+    {
+        m_frame_type = eSigtrampFrame;
+    }
+    else
+    {
+        // FIXME:  Detect eDebuggerFrame here.
+        m_frame_type = eNormalFrame;
+    }
+
+    // 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 (addr_range_ptr)
+    {
+        m_start_pc = addr_range_ptr->GetBaseAddress();
+        m_current_offset = frame_sp->GetFrameCodeAddress().GetOffset() - m_start_pc.GetOffset();
+    }
+    else
+    {
+        m_start_pc = current_pc;
+        m_current_offset = -1;
+    }
+
+    // We've set m_frame_type, m_zeroth_frame, and m_sym_ctx before this call.
+    // This call sets the m_all_registers_available, m_fast_unwind_plan, and m_full_unwind_plan member variables.
+    GetUnwindPlansForFrame (current_pc);
+
+    const UnwindPlan::Row *active_row = NULL;
+    int cfa_offset = 0;
+    int row_register_kind;
+    if (m_full_unwind_plan && m_full_unwind_plan->PlanValidAtAddress (current_pc))
+    {
+        active_row = m_full_unwind_plan->GetRowForFunctionOffset (m_current_offset);
+        row_register_kind = m_full_unwind_plan->GetRegisterKind ();
+    }
+
+    if (active_row == NULL)
+    {
+        m_frame_type = eNotAValidFrame;
+        return;
+    }
+
+    addr_t cfa_regval;
+    if (!ReadGPRValue (row_register_kind, active_row->GetCFARegister(), cfa_regval))
+    {
+        m_frame_type = eNotAValidFrame;
+        return;
+    }
+    else
+    {
+    }
+    cfa_offset = active_row->GetCFAOffset ();
+
+    m_cfa = cfa_regval + cfa_offset;
+
+    Log *log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND);
+    if (log)
+    {
+        log->Printf("%*sThread %u Frame %d initialized frame current pc is 0x%llx cfa is 0x%llx", 
+                    m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                    (uint64_t) m_cfa, (uint64_t) current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget()));
+    }
+}
+
+// Initialize a RegisterContextLLDB for the non-zeroth frame -- rely on the RegisterContextLLDB "below" it
+// to provide things like its current pc value.
+
+void
+RegisterContextLLDB::InitializeNonZerothFrame()
+{
+    Log *log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND);
+    if (m_next_frame.get() == NULL)
+    {
+        m_frame_type = eNotAValidFrame;
+        return;
+    }
+    if (!((RegisterContextLLDB*)m_next_frame.get())->IsValid())
+    {
+        m_frame_type = eNotAValidFrame;
+        return;
+    }
+    if (m_base_reg_ctx == NULL)
+    {
+        m_frame_type = eNotAValidFrame;
+        return;
+    }
+
+    m_zeroth_frame = false;
+    
+    addr_t pc;
+    if (!ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc))
+    {
+        if (log)
+        {
+            log->Printf("%*sThread %u Frame %d could not get pc value",
+                        m_frame_number, "", m_thread.GetIndexID(), m_frame_number);
+        }
+        m_frame_type = eNotAValidFrame;
+        return;
+    }
+    Address current_pc;
+    m_thread.GetProcess().GetTarget().GetSectionLoadList().ResolveLoadAddress (pc, current_pc);
+
+    // 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 (!current_pc.IsValid() || current_pc.GetModule() == NULL)
+    {
+        if (log)
+        {
+            log->Printf("%*sThread %u Frame %d using architectural default unwind method",
+                        m_frame_number, "", m_thread.GetIndexID(), m_frame_number);
+        }
+        ArchSpec arch = m_thread.GetProcess().GetTarget().GetArchitecture ();
+        ArchDefaultUnwindPlan *arch_default = ArchDefaultUnwindPlan::FindPlugin (arch);
+        if (arch_default)
+        {
+            m_fast_unwind_plan = NULL;
+            m_full_unwind_plan = arch_default->GetArchDefaultUnwindPlan (m_thread, current_pc);
+            m_frame_type = eNormalFrame;
+            m_all_registers_available = false;
+            m_current_offset = -1;
+            addr_t cfa_regval;
+            int row_register_kind = m_full_unwind_plan->GetRegisterKind ();
+            uint32_t cfa_regnum = m_full_unwind_plan->GetRowForFunctionOffset(0)->GetCFARegister();
+            int cfa_offset = m_full_unwind_plan->GetRowForFunctionOffset(0)->GetCFAOffset();
+            if (!ReadGPRValue (row_register_kind, cfa_regnum, cfa_regval))
+            {
+                if (log)
+                {
+                    log->Printf("%*sThread %u Frame %d failed to get cfa value",
+                                m_frame_number, "", m_thread.GetIndexID(), m_frame_number);
+                }
+                m_frame_type = eNormalFrame;
+                return;
+            }
+            m_cfa = cfa_regval + cfa_offset;
+            if (log)
+            {
+                log->Printf("%*sThread %u Frame %d initialized frame current pc is 0x%llx cfa is 0x%llx",
+                            m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                            (uint64_t) m_cfa, (uint64_t) current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget()));
+            }
+            return;
+        }
+        m_frame_type = eNotAValidFrame;
+        return;
+    }
+
+    // set up our m_sym_ctx SymbolContext
+    current_pc.GetModule()->ResolveSymbolContextForAddress (current_pc, eSymbolContextFunction | eSymbolContextSymbol, m_sym_ctx);
+
+    const AddressRange *addr_range_ptr;
+    if (m_sym_ctx.function)
+        addr_range_ptr = &m_sym_ctx.function->GetAddressRange();
+    else if (m_sym_ctx.symbol)
+        addr_range_ptr = m_sym_ctx.symbol->GetAddressRangePtr();
+
+    static ConstString sigtramp_name ("_sigtramp");
+    if ((m_sym_ctx.function && m_sym_ctx.function->GetMangled().GetMangledName() == sigtramp_name)
+        || (m_sym_ctx.symbol && m_sym_ctx.symbol->GetMangled().GetMangledName() == sigtramp_name))
+    {
+        m_frame_type = eSigtrampFrame;
+    }
+    else
+    {
+        // FIXME:  Detect eDebuggerFrame here.
+        m_frame_type = eNormalFrame;
+    }
+
+    // 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 (addr_range_ptr)
+    {
+        m_start_pc = addr_range_ptr->GetBaseAddress();
+        m_current_offset = current_pc.GetOffset() - m_start_pc.GetOffset();
+    }
+    else
+    {
+        m_start_pc = current_pc;
+        m_current_offset = -1;
+    }
+
+    // We've set m_frame_type, m_zeroth_frame, and m_sym_ctx before this call.
+    // This call sets the m_all_registers_available, m_fast_unwind_plan, and m_full_unwind_plan member variables.
+    GetUnwindPlansForFrame (current_pc);
+
+    const UnwindPlan::Row *active_row = NULL;
+    int cfa_offset = 0;
+    int row_register_kind;
+    if (m_fast_unwind_plan && m_fast_unwind_plan->PlanValidAtAddress (current_pc))
+    {
+        active_row = m_fast_unwind_plan->GetRowForFunctionOffset (m_current_offset);
+        row_register_kind = m_fast_unwind_plan->GetRegisterKind ();
+    }
+    else if (m_full_unwind_plan && m_full_unwind_plan->PlanValidAtAddress (current_pc))
+    {
+        active_row = m_full_unwind_plan->GetRowForFunctionOffset (m_current_offset);
+        row_register_kind = m_full_unwind_plan->GetRegisterKind ();
+    }
+
+    if (active_row == NULL)
+    {
+        m_frame_type = eNotAValidFrame;
+        return;
+    }
+
+    addr_t cfa_regval;
+    if (!ReadGPRValue (row_register_kind, active_row->GetCFARegister(), cfa_regval))
+    {
+        if (log)
+        {
+            log->Printf("%*sThread %u Frame %d failed to get cfa reg %d/%d",
+                        m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                        row_register_kind, active_row->GetCFARegister());
+        }
+        m_frame_type = eNotAValidFrame;
+        return;
+    }
+    cfa_offset = active_row->GetCFAOffset ();
+
+    m_cfa = cfa_regval + cfa_offset;
+
+    if (log)
+    {
+        log->Printf("%*sThread %u Frame %d initialized frame current pc is 0x%llx cfa is 0x%llx", 
+                    m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                    (uint64_t) m_cfa, (uint64_t) current_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget()));
+    }
+}
+
+
+
+
+// On entry to this method, 
+//
+//   1. m_frame_type should already be set to eSigtrampFrame/eDebuggerFrame 
+//      if either of those are correct, and
+//   2. m_zeroth_frame should be set to true if this is frame 0 and
+//   3. m_sym_ctx should already be filled in.
+//
+// On exit this function will have set
+//
+//   a. m_all_registers_available  (true if we can provide any requested register, false if only a subset are provided)
+//   b. m_fast_unwind_plan (fast unwind plan that walks the stack while filling in only minimal registers, may be NULL)
+//   c. m_full_unwind_plan (full unwind plan that can provide all registers possible, will *not* be NULL)
+//
+// The argument current_pc should be the current pc value in the function.  
+
+void
+RegisterContextLLDB::GetUnwindPlansForFrame (Address current_pc)
+{
+    UnwindPlan *arch_default_up = NULL;
+    ArchSpec arch = m_thread.GetProcess().GetTarget().GetArchitecture ();
+    ArchDefaultUnwindPlan *arch_default = ArchDefaultUnwindPlan::FindPlugin (arch);
+    if (arch_default)
+    {
+        arch_default_up = arch_default->GetArchDefaultUnwindPlan (m_thread, current_pc);
+    }
+
+    bool behaves_like_zeroth_frame = false;
+
+    if (m_zeroth_frame)
+    {
+        behaves_like_zeroth_frame = true;
+    }
+    if (m_next_frame.get() && ((RegisterContextLLDB*) m_next_frame.get())->m_frame_type == eSigtrampFrame)
+    {
+        behaves_like_zeroth_frame = true;
+    }
+    if (m_next_frame.get() && ((RegisterContextLLDB*) m_next_frame.get())->m_frame_type == eDebuggerFrame)
+    {
+        behaves_like_zeroth_frame = true;
+    }
+
+    if (behaves_like_zeroth_frame)
+    {
+        m_all_registers_available = true;
+    }
+    else
+    {
+//        If we need to implement gdb's decrement-pc-value-by-one-before-function-check macro, it would be here.
+//        current_pc.SetOffset (current_pc.GetOffset() - 1);
+        m_all_registers_available = false;
+    }
+
+    // No Module for the current pc, try using the architecture default unwind.
+    if (current_pc.GetModule() == NULL || current_pc.GetModule()->GetObjectFile() == NULL)
+    {
+        m_fast_unwind_plan = NULL;
+        m_full_unwind_plan = arch_default_up;
+        m_frame_type = eNormalFrame;
+        return;
+    }
+
+    FuncUnwindersSP fu;
+    if (current_pc.GetModule() && current_pc.GetModule()->GetObjectFile())
+    {
+       fu = current_pc.GetModule()->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (current_pc, m_sym_ctx);
+    }
+
+    // No FuncUnwinders available for this pc, try using architectural default unwind.
+    if (fu.get() == NULL)
+    {
+        m_fast_unwind_plan = NULL;
+        m_full_unwind_plan = arch_default_up;
+        m_frame_type = eNormalFrame;
+        return;
+    }
+
+    // If we're in _sigtramp(), unwinding past this frame requires special knowledge.  On Mac OS X this knowledge
+    // is properly encoded in the eh_frame section, so prefer that if available.
+    if (m_frame_type == eSigtrampFrame)
+    {
+        m_fast_unwind_plan = NULL;
+        UnwindPlan *up = fu->GetUnwindPlanAtCallSite ();
+        if (up->PlanValidAtAddress (current_pc))
+        {
+            m_fast_unwind_plan = NULL;
+            m_full_unwind_plan = up;
+            return;
+        }
+    }
+
+
+    UnwindPlan *fast, *callsite, *noncallsite;
+    fast = callsite = noncallsite = NULL;
+
+    if (fu->GetUnwindPlanFastUnwind (m_thread) 
+        && fu->GetUnwindPlanFastUnwind (m_thread)->PlanValidAtAddress (current_pc))
+    {
+        fast = fu->GetUnwindPlanFastUnwind (m_thread);
+    }
+
+    // Typically this is the unwind created by inspecting the assembly language instructions
+    if (fu->GetUnwindPlanAtNonCallSite (m_thread) 
+        && fu->GetUnwindPlanAtNonCallSite (m_thread)->PlanValidAtAddress (current_pc))
+    {
+        noncallsite = fu->GetUnwindPlanAtNonCallSite (m_thread);
+    }
+
+
+    // Typically this is unwind info from an eh_frame section intended for exception handling; only valid at call sites
+    if (fu->GetUnwindPlanAtCallSite () 
+        && fu->GetUnwindPlanAtCallSite ()->PlanValidAtAddress (current_pc))
+    {
+        callsite = fu->GetUnwindPlanAtCallSite ();
+    }
+
+    m_fast_unwind_plan = NULL;
+    m_full_unwind_plan = NULL;
+
+    if (fast)
+    {
+        m_fast_unwind_plan = fast;
+    }
+
+    if (behaves_like_zeroth_frame && noncallsite)
+    {
+        m_full_unwind_plan = noncallsite;
+    }
+    else 
+    {
+        if (callsite)
+        {
+            m_full_unwind_plan = callsite;
+        }
+        else
+        {
+            m_full_unwind_plan = noncallsite;
+        }
+    }
+
+    if (m_full_unwind_plan == NULL)
+    {
+        m_full_unwind_plan = arch_default_up;
+    }
+
+    Log *log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND);
+    if (log)
+    {
+        const char *has_fast = "";
+        if (m_fast_unwind_plan)
+            has_fast = ", and has a fast UnwindPlan";
+        log->Printf("%*sThread %u Frame %d frame uses %s for full UnwindPlan%s",
+                    m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                    m_full_unwind_plan->GetSourceName().GetCString(), has_fast);
+    }
+
+    return;
+}
+
+void
+RegisterContextLLDB::Invalidate ()
+{
+    m_frame_type = eNotAValidFrame;
+}
+
+size_t
+RegisterContextLLDB::GetRegisterCount ()
+{
+    return m_base_reg_ctx->GetRegisterCount();
+}
+
+const RegisterInfo *
+RegisterContextLLDB::GetRegisterInfoAtIndex (uint32_t reg)
+{
+    return m_base_reg_ctx->GetRegisterInfoAtIndex (reg);
+}
+
+size_t
+RegisterContextLLDB::GetRegisterSetCount ()
+{
+    return m_base_reg_ctx->GetRegisterSetCount ();
+}
+
+const RegisterSet *
+RegisterContextLLDB::GetRegisterSet (uint32_t reg_set)
+{
+    return m_base_reg_ctx->GetRegisterSet (reg_set);
+}
+
+uint32_t
+RegisterContextLLDB::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
+{
+    return m_base_reg_ctx->ConvertRegisterKindToRegisterNumber (kind, num);
+}
+
+bool
+RegisterContextLLDB::ReadRegisterBytesFromRegisterLocation (uint32_t regnum, RegisterLocation regloc, DataExtractor &data)
+{
+    if (!IsValid())
+        return false;
+
+    if (regloc.type == eRegisterInRegister)
+    {
+        data.SetAddressByteSize (m_thread.GetProcess().GetAddressByteSize());
+        data.SetByteOrder (m_thread.GetProcess().GetByteOrder());
+        if (m_next_frame.get() == NULL)
+        {
+            return m_base_reg_ctx->ReadRegisterBytes (regloc.location.register_number, data);
+        }
+        else
+        {
+            return m_next_frame->ReadRegisterBytes (regloc.location.register_number, data);
+        }
+    }
+    if (regloc.type == eRegisterNotSaved)
+    {
+        return false;
+    }
+    if (regloc.type == eRegisterSavedAtHostMemoryLocation)
+    {
+        assert ("FIXME debugger inferior function call unwind");
+    }
+    if (regloc.type != eRegisterSavedAtMemoryLocation)
+    {
+        assert ("Unknown RegisterLocation type.");
+    }
+
+    const RegisterInfo *reg_info = m_base_reg_ctx->GetRegisterInfoAtIndex (regnum);
+    DataBufferSP data_sp (new DataBufferHeap (reg_info->byte_size, 0));
+    data.SetData (data_sp, 0, reg_info->byte_size);
+    data.SetAddressByteSize (m_thread.GetProcess().GetAddressByteSize());
+
+    if (regloc.type == eRegisterValueInferred)
+    {
+        data.SetByteOrder (eByteOrderHost);
+        switch (reg_info->byte_size)
+        {
+            case 1:
+            {
+                uint8_t val = regloc.location.register_value;
+                memcpy (data_sp->GetBytes(), &val, sizeof (val));
+                data.SetByteOrder (eByteOrderHost);
+                return true;
+            }
+            case 2:
+            {
+                uint16_t val = regloc.location.register_value;
+                memcpy (data_sp->GetBytes(), &val, sizeof (val));
+                data.SetByteOrder (eByteOrderHost);
+                return true;
+            }
+            case 4:
+            {
+                uint32_t val = regloc.location.register_value;
+                memcpy (data_sp->GetBytes(), &val, sizeof (val));
+                data.SetByteOrder (eByteOrderHost);
+                return true;
+            }
+            case 8:
+            {
+                uint64_t val = regloc.location.register_value;
+                memcpy (data_sp->GetBytes(), &val, sizeof (val));
+                data.SetByteOrder (eByteOrderHost);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    assert (regloc.type == eRegisterSavedAtMemoryLocation);
+    Error error;
+    data.SetByteOrder (m_thread.GetProcess().GetByteOrder());
+    if (!m_thread.GetProcess().ReadMemory (regloc.location.target_memory_location, data_sp->GetBytes(), reg_info->byte_size, error))
+        return false;
+    return true;
+}
+
+bool
+RegisterContextLLDB::IsValid () const
+{
+    return m_frame_type != eNotAValidFrame;
+}
+
+// Answer the question: Where did THIS frame save the CALLER frame ("previous" frame)'s register value?
+
+bool
+RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, RegisterLocation &regloc)
+{
+    Log *log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND);
+
+    // Have we already found this register location?
+    std::map<uint32_t, RegisterLocation>::const_iterator iterator;
+    if (m_registers.size() > 0)
+    {
+        iterator = m_registers.find (lldb_regnum);
+        if (iterator != m_registers.end())
+        {
+            regloc = iterator->second;
+            return true;
+        }
+    }
+
+    // Are we looking for the CALLER's stack pointer?  The stack pointer is defined to be the same as THIS frame's
+    // CFA so just return the CFA value.  This is true on x86-32/x86-64 at least.
+    uint32_t sp_regnum;
+    if (m_base_reg_ctx->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, eRegisterKindLLDB, sp_regnum)
+        && sp_regnum == lldb_regnum)
+    {
+        // make sure we won't lose precision copying an addr_t (m_cfa) into a uint64_t (.register_value)
+        assert (sizeof (addr_t) <= sizeof (uint64_t));
+        regloc.type = eRegisterValueInferred;
+        regloc.location.register_value = m_cfa;
+        m_registers[lldb_regnum] = regloc;
+        return true;
+    }
+
+    // Look through the available UnwindPlans for the register location.
+
+    UnwindPlan::Row::RegisterLocation unwindplan_regloc;
+    bool have_unwindplan_regloc = false;
+    if (m_fast_unwind_plan)
+    {
+        const UnwindPlan::Row *active_row = m_fast_unwind_plan->GetRowForFunctionOffset (m_current_offset);
+        uint32_t row_regnum;
+        if (!m_base_reg_ctx->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, m_fast_unwind_plan->GetRegisterKind(), row_regnum))
+        {
+            if (log)
+            {
+                log->Printf("%*sThread %u Frame %d could not supply caller's reg %d location",
+                            m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                            lldb_regnum);
+            }
+            return false;
+        }
+        if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc))
+        {
+            if (log)
+            {
+                log->Printf("%*sThread %u Frame %d supplying caller's saved reg %d's location using FastUnwindPlan",
+                            m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                            lldb_regnum);
+            }
+            have_unwindplan_regloc = true;
+        }
+    }
+    else if (m_full_unwind_plan)
+    {
+        const UnwindPlan::Row *active_row = m_full_unwind_plan->GetRowForFunctionOffset (m_current_offset);
+        uint32_t row_regnum;
+        if (!m_base_reg_ctx->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, m_full_unwind_plan->GetRegisterKind(), row_regnum))
+        {
+            if (log)
+            {
+                log->Printf("%*sThread %u Frame %d could not supply caller's reg %d location",
+                            m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                            lldb_regnum);
+            }
+            return false;
+        }
+
+        if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc))
+        {
+            have_unwindplan_regloc = true;
+            if (log)
+            {                
+                log->Printf("%*sThread %u Frame %d supplying caller's saved reg %d's location using %s UnwindPlan",
+                            m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                            lldb_regnum, m_full_unwind_plan->GetSourceName().GetCString());
+            }
+        }
+    }
+    if (have_unwindplan_regloc == false)
+    {
+        // If a volatile register is being requested, we don't want to forward m_next_frame's register contents 
+        // up the stack -- the register is not retrievable at this frame.
+        ArchSpec arch = m_thread.GetProcess().GetTarget().GetArchitecture ();
+        ArchVolatileRegs *volatile_regs = ArchVolatileRegs::FindPlugin (arch);
+        if (volatile_regs && volatile_regs->RegisterIsVolatile (m_thread, lldb_regnum))
+        {
+            if (log)
+            {
+                log->Printf("%*sThread %u Frame %d did not supply reg location for %d because it is volatile",
+                            m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                            lldb_regnum);
+            }
+            return false;
+        }  
+
+        if (m_next_frame.get())
+        {
+            return ((RegisterContextLLDB*)m_next_frame.get())->SavedLocationForRegister (lldb_regnum, regloc);
+        }
+        else
+        {
+            // This is frame 0 - we should return the actual live register context value
+            RegisterLocation new_regloc;
+            new_regloc.type = eRegisterInRegister;
+            new_regloc.location.register_number = lldb_regnum;
+            m_registers[lldb_regnum] = new_regloc;
+            regloc = new_regloc;
+            if (log)
+            {
+                log->Printf("%*sThread %u Frame %d register %d is in the thread's live register context",
+                            m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                            lldb_regnum);
+            }
+            return true;
+        }
+        if (log)
+        {
+            log->Printf("%*sThread %u Frame %d could not supply caller's reg %d location",
+                        m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                        lldb_regnum);
+        }
+        return false;
+    }
+
+    // unwindplan_regloc has valid contents about where to retrieve the register
+    if (unwindplan_regloc.IsUnspecified())
+    {
+        RegisterLocation new_regloc;
+        new_regloc.type = eRegisterNotSaved;
+        m_registers[lldb_regnum] = new_regloc;
+        if (log)
+        {
+            log->Printf("%*sThread %u Frame %d could not supply caller's reg %d location",
+                        m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                        lldb_regnum);
+        }
+        return false;
+    }
+
+    if (unwindplan_regloc.IsSame())
+    {
+        if (m_next_frame.get())
+        {
+            return ((RegisterContextLLDB*)m_next_frame.get())->SavedLocationForRegister (lldb_regnum, regloc);
+        }
+        else
+        {
+            if (log)
+            {
+                log->Printf("%*sThread %u Frame %d could not supply caller's reg %d location",
+                            m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                            lldb_regnum);
+            }
+            return false;
+        }
+    }
+
+    if (unwindplan_regloc.IsCFAPlusOffset())
+    {
+        int offset = unwindplan_regloc.GetOffset();
+        regloc.type = eRegisterValueInferred;
+        regloc.location.register_value = m_cfa + offset;
+        m_registers[lldb_regnum] = regloc;
+        return true;
+    }
+
+    if (unwindplan_regloc.IsAtCFAPlusOffset())
+    {
+        int offset = unwindplan_regloc.GetOffset();
+        regloc.type = eRegisterSavedAtMemoryLocation;
+        regloc.location.target_memory_location = m_cfa + offset;
+        m_registers[lldb_regnum] = regloc;
+        return true;
+    }
+
+    if (unwindplan_regloc.IsInOtherRegister())
+    {
+        uint32_t unwindplan_regnum = unwindplan_regloc.GetRegisterNumber();
+        uint32_t row_regnum_in_lldb;
+        if (!m_base_reg_ctx->ConvertBetweenRegisterKinds (m_full_unwind_plan->GetRegisterKind(), unwindplan_regnum, eRegisterKindLLDB, row_regnum_in_lldb))
+        {
+            if (log)
+            {
+                log->Printf("%*sThread %u Frame %d could not supply caller's reg %d location",
+                            m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                            lldb_regnum);
+            }
+            return false;
+        }
+        regloc.type = eRegisterInRegister;
+        regloc.location.register_number = row_regnum_in_lldb;
+        m_registers[lldb_regnum] = regloc;
+        return true;
+    }
+
+    if (log)
+    {
+        log->Printf("%*sThread %u Frame %d could not supply caller's reg %d location",
+                    m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                    lldb_regnum);
+    }
+
+    assert ("UnwindPlan::Row types atDWARFExpression and isDWARFExpression are unsupported.");
+    return false;
+}
+
+// Retrieve a general purpose register value for THIS from, 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.
+
+// Assumes m_base_reg_ctx has been set
+bool
+RegisterContextLLDB::ReadGPRValue (int 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_base_reg_ctx->ConvertBetweenRegisterKinds (register_kind, regnum, eRegisterKindLLDB, lldb_regnum))
+    {
+        return false;
+    }
+
+    uint32_t offset = 0;
+    DataExtractor data;
+    data.SetAddressByteSize (m_thread.GetProcess().GetAddressByteSize());
+    data.SetByteOrder (m_thread.GetProcess().GetByteOrder());
+
+    // if this is frame 0 (currently executing frame), get the requested reg contents from the actual thread registers
+    if (m_next_frame.get() == NULL)
+    {
+        if (m_base_reg_ctx->ReadRegisterBytes (lldb_regnum, data))
+        {
+            data.SetAddressByteSize (m_thread.GetProcess().GetAddressByteSize());
+            value = data.GetAddress (&offset);
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+    RegisterLocation regloc;
+    if (!((RegisterContextLLDB*)m_next_frame.get())->SavedLocationForRegister (lldb_regnum, regloc))
+    {
+        return false;
+    }
+    if (!ReadRegisterBytesFromRegisterLocation (lldb_regnum, regloc, data))
+    {
+        return false;
+    }
+    data.SetAddressByteSize (m_thread.GetProcess().GetAddressByteSize());
+    value = data.GetAddress (&offset);
+    return true;
+}
+
+// Find the value of a register in THIS frame
+
+bool
+RegisterContextLLDB::ReadRegisterBytes (uint32_t lldb_reg, DataExtractor& data)
+{
+    Log *log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND);
+    if (!IsValid())
+        return false;
+
+    if (log)
+    {
+        log->Printf("%*sThread %u Frame %d looking for register saved location for reg %d",
+                    m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                    lldb_reg);
+    }
+
+    // If this is the 0th frame, hand this over to the live register context
+    if (m_next_frame.get() == NULL)
+    {
+        if (log)
+        {
+            log->Printf("%*sThread %u Frame %d passing along to the live register context for reg %d",
+                        m_frame_number, "", m_thread.GetIndexID(), m_frame_number,
+                        lldb_reg);
+        }
+        return m_base_reg_ctx->ReadRegisterBytes (lldb_reg, data);
+    }
+
+    RegisterLocation regloc;
+    // Find out where the NEXT frame saved THIS frame's register contents
+    if (!((RegisterContextLLDB*)m_next_frame.get())->SavedLocationForRegister (lldb_reg, regloc))
+        return false;
+
+    return ReadRegisterBytesFromRegisterLocation (lldb_reg, regloc, data);
+}
+
+bool
+RegisterContextLLDB::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+    assert ("not yet implemented");  // FIXME
+    return false;
+}
+
+bool
+RegisterContextLLDB::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset)
+{
+    assert ("not yet implemented");  // FIXME
+    return false;
+}
+
+bool
+RegisterContextLLDB::WriteAllRegisterValues (const lldb::DataBufferSP& data_sp)
+{
+    assert ("not yet implemented");  // FIXME
+    return false;
+}
+
+// Retrieve the pc value for THIS from
+
+bool
+RegisterContextLLDB::GetCFA (addr_t& cfa)
+{
+    if (!IsValid())
+    {
+        return false;
+    }
+    if (m_cfa == LLDB_INVALID_ADDRESS)
+    {
+        return false;
+    }
+    cfa = m_cfa;
+    return true;
+}
+
+// Retrieve the address of the start of the function of THIS frame
+
+bool
+RegisterContextLLDB::GetStartPC (addr_t& start_pc)
+{
+    if (!IsValid())
+        return false;
+    if (!m_start_pc.IsValid())
+    {
+        return GetPC (start_pc); 
+    }
+    start_pc = m_start_pc.GetLoadAddress (&m_thread.GetProcess().GetTarget());
+    return true;
+}
+
+// Retrieve the current pc value for THIS frame, as saved by the NEXT frame.
+
+bool
+RegisterContextLLDB::GetPC (addr_t& pc)
+{
+    if (!IsValid())
+        return false;
+    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 (m_all_registers_available == false 
+            && (pc == 0 || pc == 1))
+        {
+            return false;
+        }
+        else 
+        {
+            return true;
+        }
+    }
+    else
+    {
+        return false;
+    }
+}
diff --git a/source/Plugins/Process/Utility/RegisterContextLLDB.h b/source/Plugins/Process/Utility/RegisterContextLLDB.h
new file mode 100644
index 0000000..41e4353
--- /dev/null
+++ b/source/Plugins/Process/Utility/RegisterContextLLDB.h
@@ -0,0 +1,177 @@
+//===-- RegisterContextLLDB.h --------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterContextLLDB_h_
+#define lldb_RegisterContextLLDB_h_
+
+#include <vector>
+
+#include "lldb/lldb-private.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Symbol/SymbolContext.h"
+
+class RegisterContextLLDB : public lldb_private::RegisterContext
+{
+public:
+    RegisterContextLLDB (lldb_private::Thread &thread,
+                         const lldb::RegisterContextSP& next_frame,
+                         lldb_private::SymbolContext& sym_ctx,
+                         int frame_number);
+
+    ///
+    // pure virtual functions from the base class that we must implement
+    ///
+
+    virtual
+    ~RegisterContextLLDB () { }
+
+    virtual void
+    Invalidate ();
+
+    virtual size_t
+    GetRegisterCount ();
+
+    virtual const lldb::RegisterInfo *
+    GetRegisterInfoAtIndex (uint32_t reg);
+
+    virtual size_t
+    GetRegisterSetCount ();
+
+    virtual const lldb::RegisterSet *
+    GetRegisterSet (uint32_t reg_set);
+
+    virtual bool
+    ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data);
+
+    virtual bool
+    ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+    virtual bool
+    WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset = 0);
+
+    virtual bool
+    WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+    virtual uint32_t
+    ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+    bool
+    IsValid () const;
+
+    bool
+    GetCFA (lldb::addr_t& cfa);
+
+    bool
+    GetStartPC (lldb::addr_t& start_pc);
+
+    bool
+    GetPC (lldb::addr_t& start_pc);
+
+private:
+
+    typedef enum FrameType
+    {
+        eNormalFrame,
+        eSigtrampFrame,
+        eDebuggerFrame,  // a debugger inferior function call frame; we get caller's registers from debugger
+        eNotAValidFrame  // this frame is invalid for some reason - most likely it is past the top (end) of the stack
+    };
+
+    enum RegisterLocationTypes
+    {
+        eRegisterNotSaved = 0,          // register was not preserved by callee.  If volatile reg, is unavailable
+        eRegisterSavedAtMemoryLocation, // register is saved at a specific word of target mem (target_memory_location)
+        eRegisterInRegister,            // register is available in a (possible other) register (register_number)
+        eRegisterSavedAtHostMemoryLocation, // register is saved at a word in lldb's address space
+        eRegisterValueInferred          // register val was computed (and is in register_value)
+    };
+
+    struct RegisterLocation
+    {
+        int type;
+        union
+        {
+            lldb::addr_t target_memory_location;
+            uint32_t     register_number;       // in eRegisterKindLLDB register numbering system
+            void*        host_memory_location;
+            uint64_t     register_value;        // eRegisterValueInferred - e.g. stack pointer == cfa + offset
+        } location;
+    };
+
+
+    void 
+    InitializeZerothFrame ();
+
+    void 
+    InitializeNonZerothFrame();
+
+    // Provide a location for where THIS function saved the CALLER's register value
+    // Or a frame "below" this one savedit, i.e. a function called by this one, preserved a register that this
+    // function didn't modify/use.
+    //
+    // The RegisterLocation type may be set to eRegisterNotAvailable -- this will happen for a volatile register 
+    // bieng queried mid-stack.  Instead of floating frame 0's contents of that register up the stack (which may
+    // or may not be the value of that reg when the function was executing), we won't return any value.
+    //
+    // If a non-volatile register (a "preserved" register) is requested mid-stack and no frames "below" the requested
+    // stack have saved the register anywhere, it is safe to assume that frame 0's register values are still the same
+    // as the requesting frame's.
+    //
+    bool
+    SavedLocationForRegister (uint32_t lldb_regnum, RegisterLocation &regloc);
+
+    bool
+    ReadRegisterBytesFromRegisterLocation (uint32_t regnum, RegisterLocation regloc, lldb_private::DataExtractor &data);
+
+    bool
+    WriteRegisterBytesFromRegisterLocation (uint32_t regnum, RegisterLocation regloc, lldb_private::Scalar value);
+
+    // Get the contents of a general purpose (address-size) register for this frame 
+    // (usually retrieved from the m_next_frame)
+    // m_base_reg_ectx and m_next_frame should both be initialized appropriately before calling.
+    bool
+    ReadGPRValue (int register_kind, uint32_t regnum, lldb::addr_t &value);
+
+    void
+    GetUnwindPlansForFrame (lldb_private::Address current_pc);
+
+    lldb_private::Thread& m_thread;
+    lldb::RegisterContextSP m_next_frame;
+
+    lldb_private::RegisterContext *m_base_reg_ctx;     // RegisterContext of frame 0 (live register values only)
+
+    ///
+    // The following tell us how to retrieve the CALLER's register values (ie the "previous" frame, aka the frame above)
+    // i.e. where THIS frame saved them
+    ///
+
+    lldb_private::UnwindPlan *m_fast_unwind_plan;    // may be NULL
+    lldb_private::UnwindPlan *m_full_unwind_plan;
+    bool m_zeroth_frame;                             // Is this the bottom-most, i.e. currently executing, frame?
+    bool m_all_registers_available;                  // Can we retrieve all regs or just nonvolatile regs?
+    int m_frame_type;                                // enum FrameType
+    int m_current_offset;                            // how far into the function we've executed; -1 if unknown
+    lldb_private::SymbolContext& m_sym_ctx;
+
+    int m_frame_number;                              // What stack frame level this frame is - used for debug logging
+
+    lldb::addr_t m_cfa;
+    lldb_private::Address m_start_pc;
+
+    std::map<uint32_t, RegisterLocation> m_registers; // where to find reg values for this frame
+
+    //------------------------------------------------------------------
+    // For RegisterContextLLDB only
+    //------------------------------------------------------------------
+
+    DISALLOW_COPY_AND_ASSIGN (RegisterContextLLDB);
+};
+
+#endif  // lldb_RegisterContextLLDB_h_
diff --git a/source/Plugins/Process/Utility/UnwindAssemblyProfiler-x86.cpp b/source/Plugins/Process/Utility/UnwindAssemblyProfiler-x86.cpp
index 21a01f8..0e2a4a7 100644
--- a/source/Plugins/Process/Utility/UnwindAssemblyProfiler-x86.cpp
+++ b/source/Plugins/Process/Utility/UnwindAssemblyProfiler-x86.cpp
@@ -125,7 +125,7 @@
 
     bool get_non_call_site_unwind_plan (UnwindPlan &unwind_plan);
 
-    bool get_fast_unwind_plan (UnwindPlan &unwind_plan);
+    bool get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_plan);
 
     bool find_first_non_prologue_insn (Address &address);
 
@@ -166,7 +166,7 @@
 
 AssemblyParse_x86::AssemblyParse_x86 (Target& target, Thread* thread, int cpu, AddressRange func) :
                          m_target (target), m_thread (thread), m_cpu(cpu), m_func_bounds(func)
-{ 
+{
     int *initialized_flag = NULL;
     m_lldb_ip_regnum = m_lldb_sp_regnum = m_lldb_fp_regnum = -1;
     if (cpu == k_i386)
@@ -489,7 +489,7 @@
 
     if (EDGetDisassembler (&disasm, "i386-apple-darwin", kEDAssemblySyntaxX86ATT) != 0)
     {
-        false;
+        return false;
     }
 
     uint64_t addr_offset = addr.GetOffset();
@@ -498,7 +498,7 @@
     arg.target = &m_target;
     if (EDCreateInsts (&cur_insn, 1, disasm, read_byte_for_edis, addr_offset, &arg) != 1)
     {
-        false;
+        return false;
     }
     length = EDInstByteSize (cur_insn);
     EDReleaseInst (cur_insn);
@@ -566,12 +566,11 @@
             goto loopnext;
         }
         
+        // This is the start() function (or a pthread equivalent), it starts with a pushl $0x0 which puts the
+        // saved pc value of 0 on the stack.  In this case we want to pretend we didn't see a stack movement at all --
+        // normally the saved pc value is already on the stack by the time the function starts executing.
         if (push_0_pattern_p ())
         {
-            row.SetOffset (current_func_text_offset + insn_len);
-            current_sp_bytes_offset_from_cfa += m_wordsize;
-            row.SetCFAOffset (current_sp_bytes_offset_from_cfa);
-            unwind_plan.AppendRow (row);
             goto loopnext;
         }
 
@@ -648,14 +647,78 @@
         current_func_text_offset += insn_len;
     }
     
+    unwind_plan.SetSourceName ("assembly insn profiling");
+
     return true;
 }
 
+/* The "fast unwind plan" is valid for functions that follow the usual convention of 
+   using the frame pointer register (ebp, rbp), i.e. the function prologue looks like
+     push   %rbp      [0x55]
+     mov    %rsp,%rbp [0x48 0x89 0xe5]   (this is a 2-byte insn seq on i386)
+*/
+
 bool 
-AssemblyParse_x86::get_fast_unwind_plan (UnwindPlan &unwind_plan)
+AssemblyParse_x86::get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_plan)
 {
-    UnwindPlan up;
-    return false;
+    UnwindPlan::Row row;
+    UnwindPlan::Row::RegisterLocation pc_reginfo;
+    UnwindPlan::Row::RegisterLocation sp_reginfo;
+    UnwindPlan::Row::RegisterLocation fp_reginfo;
+    unwind_plan.SetRegisterKind (eRegisterKindLLDB);
+
+    if (!func.GetBaseAddress().IsValid())
+        return false;
+
+    uint8_t bytebuf[4];
+    Error error;
+    if (m_target.ReadMemory (func.GetBaseAddress(), bytebuf, sizeof (bytebuf), error) == -1)
+        return false;
+
+    uint8_t i386_prologue[] = {0x55, 0x89, 0xe5};
+    uint8_t x86_64_prologue[] = {0x55, 0x48, 0x89, 0xe5};
+    int prologue_size;
+
+    if (memcmp (bytebuf, i386_prologue, sizeof (i386_prologue)) == 0)
+    {
+        prologue_size = sizeof (i386_prologue);
+    }
+    else if (memcmp (bytebuf, x86_64_prologue, sizeof (x86_64_prologue)) == 0)
+    {
+        prologue_size = sizeof (x86_64_prologue);
+    }
+    else
+    {
+        return false;
+    }
+
+    pc_reginfo.SetAtCFAPlusOffset (-m_wordsize);
+    row.SetRegisterInfo (m_lldb_ip_regnum, pc_reginfo);
+
+    sp_reginfo.SetIsCFAPlusOffset (0);
+    row.SetRegisterInfo (m_lldb_sp_regnum, sp_reginfo);
+
+    // Zero instructions into the function
+    row.SetCFARegister (m_lldb_sp_regnum);
+    row.SetCFAOffset (m_wordsize);
+    row.SetOffset (0);
+    unwind_plan.AppendRow (row);
+
+    // push %rbp has executed - stack moved, rbp now saved
+    row.SetCFAOffset (2 * m_wordsize);
+    fp_reginfo.SetAtCFAPlusOffset (2 * -m_wordsize);
+    row.SetRegisterInfo (m_lldb_fp_regnum, fp_reginfo);
+    row.SetOffset (1);
+    unwind_plan.AppendRow (row);
+
+    // mov %rsp, %rbp has executed
+    row.SetCFARegister (m_lldb_fp_regnum);
+    row.SetCFAOffset (2 * m_wordsize);
+    row.SetOffset (prologue_size);     /// 3 or 4 bytes depending on arch
+    unwind_plan.AppendRow (row);
+
+    unwind_plan.SetPlanValidAddressRange (func);
+    return true;
 }
 
 bool 
@@ -712,7 +775,7 @@
 UnwindAssemblyProfiler_x86::GetFastUnwindPlan (AddressRange& func, Thread& thread, UnwindPlan &unwind_plan)
 {
     AssemblyParse_x86 asm_parse(thread.GetProcess().GetTarget(), &thread, m_cpu, func);
-    return asm_parse.get_fast_unwind_plan (unwind_plan);
+    return asm_parse.get_fast_unwind_plan (func, unwind_plan);
 }
 
 bool
diff --git a/source/Plugins/Process/Utility/UnwindLLDB.cpp b/source/Plugins/Process/Utility/UnwindLLDB.cpp
new file mode 100644
index 0000000..6d4c345
--- /dev/null
+++ b/source/Plugins/Process/Utility/UnwindLLDB.cpp
@@ -0,0 +1,141 @@
+//===-- UnwindLLDB.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/Thread.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Utility/ArchDefaultUnwindPlan.h"
+#include "UnwindLLDB.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Core/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+UnwindLLDB::UnwindLLDB (Thread &thread) :
+    Unwind (thread),
+    m_frames()
+{
+}
+
+uint32_t
+UnwindLLDB::GetFrameCount()
+{
+    Log *log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND);
+    if (m_frames.empty())
+    {
+        // First, set up the 0th (initial) frame
+        Cursor first_cursor;
+        RegisterContextSP no_frame; // an empty shared pointer
+        RegisterContextLLDB *first_register_ctx = new RegisterContextLLDB(m_thread, no_frame, first_cursor.sctx, 0);
+        if (!first_register_ctx->IsValid())
+        {
+            delete first_register_ctx;
+            return 0;
+        }
+        if (!first_register_ctx->GetCFA (first_cursor.cfa))
+        {
+            delete first_register_ctx;
+            return 0;
+        }
+        if (!first_register_ctx->GetPC (first_cursor.start_pc))
+        {
+            delete first_register_ctx;
+            return 0;
+        }
+        // Reuse the StackFrame provided by the processor native machine context for the first frame
+        first_register_ctx->SetStackFrame (m_thread.GetStackFrameAtIndex(0).get());
+        RegisterContextSP temp_rcs(first_register_ctx);
+        first_cursor.reg_ctx = temp_rcs;
+        m_frames.push_back (first_cursor);
+
+        // Now walk up the rest of the stack
+        while (1)
+        {
+            Cursor cursor;
+            RegisterContextLLDB *register_ctx;
+            int cur_idx = m_frames.size ();
+            register_ctx = new RegisterContextLLDB (m_thread, m_frames[cur_idx - 1].reg_ctx, cursor.sctx, cur_idx);
+            if (!register_ctx->IsValid())
+            {
+                delete register_ctx;
+                if (log)
+                {
+                    log->Printf("%*sThread %u Frame %d invalid RegisterContext for this frame, stopping stack walk", 
+                                cur_idx, "", m_thread.GetIndexID(), cur_idx);
+                }
+                break;
+            }
+            if (!register_ctx->GetCFA (cursor.cfa))
+            {
+                delete register_ctx;
+                if (log)
+                {
+                    log->Printf("%*sThread %u Frame %d did not get CFA for this frame, stopping stack walk",
+                                cur_idx, "", m_thread.GetIndexID(), cur_idx);
+                }
+                break;
+            }
+            if (!register_ctx->GetPC (cursor.start_pc))
+            {
+                delete register_ctx;
+                if (log)
+                {
+                    log->Printf("%*sThread %u Frame %d did not get PC for this frame, stopping stack walk",
+                                cur_idx, "", m_thread.GetIndexID(), cur_idx);
+                }
+                break;
+            }
+            RegisterContextSP temp_rcs(register_ctx);
+            StackFrame *frame = new StackFrame(cur_idx, cur_idx, m_thread, temp_rcs, cursor.cfa, cursor.start_pc, &cursor.sctx);
+            register_ctx->SetStackFrame (frame);
+            cursor.reg_ctx = temp_rcs;
+            m_frames.push_back (cursor);
+        }
+    }
+    return m_frames.size ();
+}
+
+bool
+UnwindLLDB::GetFrameInfoAtIndex (uint32_t idx, addr_t& cfa, addr_t& pc)
+{
+    // FIXME don't get the entire stack if it isn't needed.
+    if (m_frames.size() == 0)
+        GetFrameCount();
+
+    if (idx < m_frames.size ())
+    {
+        cfa = m_frames[idx].cfa;
+        pc = m_frames[idx].start_pc;
+        return true;
+    }
+    return false;
+}
+
+RegisterContext *
+UnwindLLDB::CreateRegisterContextForFrame (StackFrame *frame)
+{
+    uint32_t idx = frame->GetFrameIndex ();
+    
+    // FIXME don't get the entire stack if it isn't needed.
+    if (m_frames.size() == 0)
+        GetFrameCount();
+    
+    if (idx == 0)
+    {
+        return m_thread.GetRegisterContext();
+    }
+    if (idx < m_frames.size ())
+        return m_frames[idx].reg_ctx.get();
+    return NULL;
+}
diff --git a/source/Plugins/Process/Utility/UnwindLLDB.h b/source/Plugins/Process/Utility/UnwindLLDB.h
new file mode 100644
index 0000000..5c12117
--- /dev/null
+++ b/source/Plugins/Process/Utility/UnwindLLDB.h
@@ -0,0 +1,70 @@
+//===-- UnwindLLDB.h --------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_UnwindLLDB_h_
+#define lldb_UnwindLLDB_h_
+
+#include "lldb/lldb-private.h"
+#include "lldb/Target/Unwind.h"
+#include "lldb/Symbol/FuncUnwinders.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "RegisterContextLLDB.h"
+#include "lldb/Target/RegisterContext.h"
+#include <vector>
+
+
+namespace lldb_private {
+
+class UnwindLLDB : public lldb_private::Unwind
+{
+public: 
+    UnwindLLDB (lldb_private::Thread &thread);
+    
+    virtual
+    ~UnwindLLDB() { }
+    
+    void
+    Clear()
+    {
+        m_frames.clear();
+    }
+
+    virtual uint32_t
+    GetFrameCount();
+
+    bool
+    GetFrameInfoAtIndex (uint32_t frame_idx,
+                         lldb::addr_t& cfa, 
+                         lldb::addr_t& start_pc);
+    
+    lldb_private::RegisterContext *
+    CreateRegisterContextForFrame (lldb_private::StackFrame *frame);
+
+private:
+    struct Cursor
+    {
+        lldb::addr_t start_pc;  // The start address of the function/symbol for this frame - current pc if unknown
+        lldb::addr_t cfa;       // The canonical frame address for this stack frame
+        lldb_private::SymbolContext sctx;  // A symbol context we'll contribute to & provide to the StackFrame creation
+        lldb::RegisterContextSP reg_ctx; // These are all RegisterContextLLDB's
+
+        Cursor () : start_pc (LLDB_INVALID_ADDRESS), cfa (LLDB_INVALID_ADDRESS), sctx(), reg_ctx() { }
+    };
+
+    std::vector<Cursor> m_frames;
+
+    //------------------------------------------------------------------
+    // For UnwindLLDB only
+    //------------------------------------------------------------------
+    DISALLOW_COPY_AND_ASSIGN (UnwindLLDB);
+};
+
+}
+
+#endif  // lldb_UnwindLLDB_h_