AppleThreadPlanStepThroughObjCTrampoline.cpp revision 309124
1293116Semaste//===-- AppleThreadPlanStepThroughObjCTrampoline.cpp --------------------------*- C++ -*-===//
2293116Semaste//
3293116Semaste//                     The LLVM Compiler Infrastructure
4293116Semaste//
5293116Semaste// This file is distributed under the University of Illinois Open Source
6293116Semaste// License. See LICENSE.TXT for details.
7293116Semaste//
8293116Semaste//===----------------------------------------------------------------------===//
9293116Semaste
10293116Semaste// C Includes
11293116Semaste// C++ Includes
12293116Semaste// Other libraries and framework includes
13293116Semaste// Project includes
14293116Semaste#include "AppleThreadPlanStepThroughObjCTrampoline.h"
15293116Semaste#include "AppleObjCTrampolineHandler.h"
16309124Sdim#include "lldb/Core/Log.h"
17309124Sdim#include "lldb/Expression/DiagnosticManager.h"
18293116Semaste#include "lldb/Expression/FunctionCaller.h"
19293116Semaste#include "lldb/Expression/UtilityFunction.h"
20293116Semaste#include "lldb/Target/ExecutionContext.h"
21293116Semaste#include "lldb/Target/ObjCLanguageRuntime.h"
22309124Sdim#include "lldb/Target/Process.h"
23309124Sdim#include "lldb/Target/Thread.h"
24293116Semaste#include "lldb/Target/ThreadPlanRunToAddress.h"
25293116Semaste#include "lldb/Target/ThreadPlanStepOut.h"
26293116Semaste
27293116Semasteusing namespace lldb;
28293116Semasteusing namespace lldb_private;
29293116Semaste
30293116Semaste//----------------------------------------------------------------------
31293116Semaste// ThreadPlanStepThroughObjCTrampoline constructor
32293116Semaste//----------------------------------------------------------------------
33293116SemasteAppleThreadPlanStepThroughObjCTrampoline::AppleThreadPlanStepThroughObjCTrampoline
34293116Semaste(
35293116Semaste    Thread &thread,
36293116Semaste    AppleObjCTrampolineHandler *trampoline_handler,
37293116Semaste    ValueList &input_values,
38293116Semaste    lldb::addr_t isa_addr,
39293116Semaste    lldb::addr_t sel_addr,
40293116Semaste    bool stop_others
41293116Semaste) :
42293116Semaste    ThreadPlan (ThreadPlan::eKindGeneric,
43293116Semaste                "MacOSX Step through ObjC Trampoline",
44293116Semaste                thread,
45293116Semaste                eVoteNoOpinion,
46293116Semaste                eVoteNoOpinion),
47293116Semaste    m_trampoline_handler (trampoline_handler),
48293116Semaste    m_args_addr (LLDB_INVALID_ADDRESS),
49293116Semaste    m_input_values (input_values),
50293116Semaste    m_isa_addr(isa_addr),
51293116Semaste    m_sel_addr(sel_addr),
52293116Semaste    m_impl_function (NULL),
53293116Semaste    m_stop_others (stop_others)
54293116Semaste{
55293116Semaste
56293116Semaste}
57293116Semaste
58293116Semaste//----------------------------------------------------------------------
59293116Semaste// Destructor
60293116Semaste//----------------------------------------------------------------------
61293116SemasteAppleThreadPlanStepThroughObjCTrampoline::~AppleThreadPlanStepThroughObjCTrampoline()
62293116Semaste{
63293116Semaste}
64293116Semaste
65293116Semastevoid
66293116SemasteAppleThreadPlanStepThroughObjCTrampoline::DidPush ()
67293116Semaste{
68293116Semaste    // Setting up the memory space for the called function text might require allocations,
69293116Semaste    // i.e. a nested function call.  This needs to be done as a PreResumeAction.
70293116Semaste    m_thread.GetProcess()->AddPreResumeAction (PreResumeInitializeFunctionCaller, (void *) this);
71293116Semaste}
72293116Semaste
73293116Semastebool
74293116SemasteAppleThreadPlanStepThroughObjCTrampoline::InitializeFunctionCaller ()
75293116Semaste{
76293116Semaste    if (!m_func_sp)
77293116Semaste    {
78309124Sdim        DiagnosticManager diagnostics;
79293116Semaste        m_args_addr = m_trampoline_handler->SetupDispatchFunction(m_thread, m_input_values);
80309124Sdim
81293116Semaste        if (m_args_addr == LLDB_INVALID_ADDRESS)
82293116Semaste        {
83293116Semaste            return false;
84293116Semaste        }
85293116Semaste        m_impl_function = m_trampoline_handler->GetLookupImplementationFunctionCaller();
86293116Semaste        ExecutionContext exc_ctx;
87293116Semaste        EvaluateExpressionOptions options;
88293116Semaste        options.SetUnwindOnError(true);
89293116Semaste        options.SetIgnoreBreakpoints(true);
90293116Semaste        options.SetStopOthers(m_stop_others);
91293116Semaste        m_thread.CalculateExecutionContext(exc_ctx);
92309124Sdim        m_func_sp = m_impl_function->GetThreadPlanToCallFunction(exc_ctx, m_args_addr, options, diagnostics);
93293116Semaste        m_func_sp->SetOkayToDiscard(true);
94309124Sdim        m_thread.QueueThreadPlan(m_func_sp, false);
95293116Semaste    }
96293116Semaste    return true;
97293116Semaste}
98293116Semaste
99293116Semastebool
100293116SemasteAppleThreadPlanStepThroughObjCTrampoline::PreResumeInitializeFunctionCaller(void *void_myself)
101293116Semaste{
102293116Semaste    AppleThreadPlanStepThroughObjCTrampoline *myself = static_cast<AppleThreadPlanStepThroughObjCTrampoline *>(void_myself);
103293116Semaste    return myself->InitializeFunctionCaller();
104293116Semaste}
105293116Semaste
106293116Semastevoid
107293116SemasteAppleThreadPlanStepThroughObjCTrampoline::GetDescription (Stream *s,
108293116Semaste                                                          lldb::DescriptionLevel level)
109293116Semaste{
110293116Semaste    if (level == lldb::eDescriptionLevelBrief)
111293116Semaste        s->Printf("Step through ObjC trampoline");
112293116Semaste    else
113293116Semaste    {
114293116Semaste        s->Printf ("Stepping to implementation of ObjC method - obj: 0x%llx, isa: 0x%" PRIx64 ", sel: 0x%" PRIx64,
115293116Semaste                   m_input_values.GetValueAtIndex(0)->GetScalar().ULongLong(), m_isa_addr, m_sel_addr);
116293116Semaste    }
117293116Semaste}
118293116Semaste
119293116Semastebool
120293116SemasteAppleThreadPlanStepThroughObjCTrampoline::ValidatePlan (Stream *error)
121293116Semaste{
122293116Semaste    return true;
123293116Semaste}
124293116Semaste
125293116Semastebool
126293116SemasteAppleThreadPlanStepThroughObjCTrampoline::DoPlanExplainsStop (Event *event_ptr)
127293116Semaste{
128293116Semaste    // If we get asked to explain the stop it will be because something went
129293116Semaste    // wrong (like the implementation for selector function crashed...  We're going
130293116Semaste    // to figure out what to do about that, so we do explain the stop.
131293116Semaste    return true;
132293116Semaste}
133293116Semaste
134293116Semastelldb::StateType
135293116SemasteAppleThreadPlanStepThroughObjCTrampoline::GetPlanRunState ()
136293116Semaste{
137293116Semaste    return eStateRunning;
138293116Semaste}
139293116Semaste
140293116Semastebool
141293116SemasteAppleThreadPlanStepThroughObjCTrampoline::ShouldStop (Event *event_ptr)
142293116Semaste{
143293116Semaste    // First stage: we are still handling the "call a function to get the target of the dispatch"
144293116Semaste    if (m_func_sp)
145293116Semaste    {
146293116Semaste        if (!m_func_sp->IsPlanComplete())
147293116Semaste        {
148293116Semaste            return false;
149293116Semaste        }
150293116Semaste        else
151293116Semaste        {
152293116Semaste            if (!m_func_sp->PlanSucceeded())
153293116Semaste            {
154293116Semaste                SetPlanComplete(false);
155293116Semaste                return true;
156293116Semaste            }
157293116Semaste            m_func_sp.reset();
158293116Semaste        }
159293116Semaste    }
160293116Semaste
161293116Semaste    // Second stage, if all went well with the function calling, then fetch the target address, and
162293116Semaste    // queue up a "run to that address" plan.
163293116Semaste    if (!m_run_to_sp)
164293116Semaste    {
165293116Semaste        Value target_addr_value;
166293116Semaste        ExecutionContext exc_ctx;
167293116Semaste        m_thread.CalculateExecutionContext(exc_ctx);
168293116Semaste        m_impl_function->FetchFunctionResults (exc_ctx, m_args_addr, target_addr_value);
169293116Semaste        m_impl_function->DeallocateFunctionResults(exc_ctx, m_args_addr);
170293116Semaste        lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong();
171293116Semaste        Address target_so_addr;
172293116Semaste        target_so_addr.SetOpcodeLoadAddress(target_addr, exc_ctx.GetTargetPtr());
173293116Semaste        Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
174293116Semaste        if (target_addr == 0)
175293116Semaste        {
176293116Semaste            if (log)
177293116Semaste                log->Printf("Got target implementation of 0x0, stopping.");
178293116Semaste            SetPlanComplete();
179293116Semaste            return true;
180293116Semaste        }
181293116Semaste        if (m_trampoline_handler->AddrIsMsgForward(target_addr))
182293116Semaste        {
183293116Semaste            if (log)
184293116Semaste                log->Printf ("Implementation lookup returned msgForward function: 0x%" PRIx64 ", stopping.", target_addr);
185293116Semaste
186293116Semaste            SymbolContext sc = m_thread.GetStackFrameAtIndex(0)->GetSymbolContext(eSymbolContextEverything);
187293116Semaste            const bool abort_other_plans = false;
188293116Semaste            const bool first_insn = true;
189293116Semaste            const uint32_t frame_idx = 0;
190293116Semaste            m_run_to_sp = m_thread.QueueThreadPlanForStepOutNoShouldStop (abort_other_plans,
191293116Semaste                                           &sc,
192293116Semaste                                           first_insn,
193293116Semaste                                           m_stop_others,
194293116Semaste                                           eVoteNoOpinion,
195293116Semaste                                           eVoteNoOpinion,
196293116Semaste                                           frame_idx);
197293116Semaste            m_run_to_sp->SetPrivate(true);
198293116Semaste            return false;
199293116Semaste        }
200293116Semaste
201293116Semaste        if (log)
202293116Semaste            log->Printf("Running to ObjC method implementation: 0x%" PRIx64, target_addr);
203293116Semaste
204293116Semaste        ObjCLanguageRuntime *objc_runtime = GetThread().GetProcess()->GetObjCLanguageRuntime();
205293116Semaste        assert (objc_runtime != NULL);
206293116Semaste        objc_runtime->AddToMethodCache (m_isa_addr, m_sel_addr, target_addr);
207293116Semaste        if (log)
208293116Semaste            log->Printf("Adding {isa-addr=0x%" PRIx64 ", sel-addr=0x%" PRIx64 "} = addr=0x%" PRIx64 " to cache.", m_isa_addr, m_sel_addr, target_addr);
209293116Semaste
210293116Semaste        // Extract the target address from the value:
211293116Semaste
212293116Semaste        m_run_to_sp.reset(new ThreadPlanRunToAddress(m_thread, target_so_addr, m_stop_others));
213293116Semaste        m_thread.QueueThreadPlan(m_run_to_sp, false);
214293116Semaste        m_run_to_sp->SetPrivate(true);
215293116Semaste        return false;
216293116Semaste    }
217293116Semaste    else if (m_thread.IsThreadPlanDone(m_run_to_sp.get()))
218293116Semaste    {
219293116Semaste        // Third stage, work the run to target plan.
220293116Semaste        SetPlanComplete();
221293116Semaste        return true;
222293116Semaste    }
223293116Semaste    return false;
224293116Semaste}
225293116Semaste
226293116Semaste// The base class MischiefManaged does some cleanup - so you have to call it
227293116Semaste// in your MischiefManaged derived class.
228293116Semastebool
229293116SemasteAppleThreadPlanStepThroughObjCTrampoline::MischiefManaged ()
230293116Semaste{
231293116Semaste    if (IsPlanComplete())
232293116Semaste        return true;
233293116Semaste    else
234293116Semaste        return false;
235293116Semaste}
236293116Semaste
237293116Semastebool
238293116SemasteAppleThreadPlanStepThroughObjCTrampoline::WillStop()
239293116Semaste{
240293116Semaste    return true;
241293116Semaste}
242