1 //===-- ThreadPlanStepRange.cpp ---------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/Target/ThreadPlanStepRange.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 
17 #include "lldb/lldb-private-log.h"
18 #include "lldb/Core/Log.h"
19 #include "lldb/Core/Stream.h"
20 #include "lldb/Symbol/Function.h"
21 #include "lldb/Symbol/Symbol.h"
22 #include "lldb/Target/Process.h"
23 #include "lldb/Target/RegisterContext.h"
24 #include "lldb/Target/StopInfo.h"
25 #include "lldb/Target/Thread.h"
26 
27 using namespace lldb;
28 using namespace lldb_private;
29 
30 
31 //----------------------------------------------------------------------
32 // ThreadPlanStepRange: Step through a stack range, either stepping over or into
33 // based on the value of \a type.
34 //----------------------------------------------------------------------
35 
36 ThreadPlanStepRange::ThreadPlanStepRange (ThreadPlanKind kind,
37                                           const char *name,
38                                           Thread &thread,
39                                           const AddressRange &range,
40                                           const SymbolContext &addr_context,
41                                           lldb::RunMode stop_others) :
42     ThreadPlan (kind, name, thread, eVoteNoOpinion, eVoteNoOpinion),
43     m_addr_context (addr_context),
44     m_address_ranges (),
45     m_stop_others (stop_others),
46     m_stack_id (),
47     m_no_more_plans (false),
48     m_first_run_event (true)
49 {
50     AddRange(range);
51     m_stack_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
52 }
53 
54 ThreadPlanStepRange::~ThreadPlanStepRange ()
55 {
56 }
57 
58 bool
59 ThreadPlanStepRange::ValidatePlan (Stream *error)
60 {
61     return true;
62 }
63 
64 Vote
65 ThreadPlanStepRange::ShouldReportStop (Event *event_ptr)
66 {
67     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
68 
69     const Vote vote = IsPlanComplete() ? eVoteYes : eVoteNo;
70     if (log)
71         log->Printf ("ThreadPlanStepRange::ShouldReportStop() returning vote %i\n", vote);
72     return vote;
73 }
74 
75 void
76 ThreadPlanStepRange::AddRange(const AddressRange &new_range)
77 {
78     // For now I'm just adding the ranges.  At some point we may want to
79     // condense the ranges if they overlap, though I don't think it is likely
80     // to be very important.
81     m_address_ranges.push_back (new_range);
82 }
83 
84 void
85 ThreadPlanStepRange::DumpRanges(Stream *s)
86 {
87     size_t num_ranges = m_address_ranges.size();
88     if (num_ranges == 1)
89     {
90         m_address_ranges[0].Dump (s, m_thread.CalculateTarget().get(), Address::DumpStyleLoadAddress);
91     }
92     else
93     {
94         for (size_t i = 0; i < num_ranges; i++)
95         {
96             s->PutCString("%d: ");
97             m_address_ranges[i].Dump (s, m_thread.CalculateTarget().get(), Address::DumpStyleLoadAddress);
98         }
99     }
100 }
101 
102 bool
103 ThreadPlanStepRange::InRange ()
104 {
105     LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
106     bool ret_value = false;
107 
108     lldb::addr_t pc_load_addr = m_thread.GetRegisterContext()->GetPC();
109 
110     size_t num_ranges = m_address_ranges.size();
111     for (size_t i = 0; i < num_ranges; i++)
112     {
113         ret_value = m_address_ranges[i].ContainsLoadAddress(pc_load_addr, m_thread.CalculateTarget().get());
114         if (ret_value)
115             break;
116     }
117 
118     if (!ret_value)
119     {
120         // See if we've just stepped to another part of the same line number...
121         StackFrame *frame = m_thread.GetStackFrameAtIndex(0).get();
122 
123         SymbolContext new_context(frame->GetSymbolContext(eSymbolContextEverything));
124         if (m_addr_context.line_entry.IsValid() && new_context.line_entry.IsValid())
125         {
126             if (m_addr_context.line_entry.file == new_context.line_entry.file)
127             {
128                 if (m_addr_context.line_entry.line == new_context.line_entry.line)
129                 {
130                     m_addr_context = new_context;
131                     AddRange(m_addr_context.line_entry.range);
132                     ret_value = true;
133                     if (log)
134                     {
135                         StreamString s;
136                         m_addr_context.line_entry.range.Dump (&s,
137                                                               m_thread.CalculateTarget().get(),
138                                                               Address::DumpStyleLoadAddress);
139 
140                         log->Printf ("Step range plan stepped to another range of same line: %s", s.GetData());
141                     }
142                 }
143                 else if (new_context.line_entry.range.GetBaseAddress().GetLoadAddress(m_thread.CalculateTarget().get())
144                          != pc_load_addr)
145                 {
146                     // Another thing that sometimes happens here is that we step out of one line into the MIDDLE of another
147                     // line.  So far I mostly see this due to bugs in the debug information.
148                     // But we probably don't want to be in the middle of a line range, so in that case reset the stepping
149                     // range to the line we've stepped into the middle of and continue.
150                     m_addr_context = new_context;
151                     m_address_ranges.clear();
152                     AddRange(m_addr_context.line_entry.range);
153                     ret_value = true;
154                     if (log)
155                     {
156                         StreamString s;
157                         m_addr_context.line_entry.range.Dump (&s,
158                                                               m_thread.CalculateTarget().get(),
159                                                               Address::DumpStyleLoadAddress);
160 
161                         log->Printf ("Step range plan stepped to the middle of new line(%d): %s, continuing to clear this line.",
162                                      new_context.line_entry.line,
163                                      s.GetData());
164                     }
165 
166                 }
167             }
168 
169         }
170 
171     }
172 
173     if (!ret_value && log)
174         log->Printf ("Step range plan out of range to 0x%llx", pc_load_addr);
175 
176     return ret_value;
177 }
178 
179 bool
180 ThreadPlanStepRange::InSymbol()
181 {
182     lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC();
183     if (m_addr_context.function != NULL)
184     {
185         return m_addr_context.function->GetAddressRange().ContainsLoadAddress (cur_pc, m_thread.CalculateTarget().get());
186     }
187     else if (m_addr_context.symbol)
188     {
189         AddressRange range(m_addr_context.symbol->GetAddress(), m_addr_context.symbol->GetByteSize());
190         return range.ContainsLoadAddress (cur_pc, m_thread.CalculateTarget().get());
191     }
192     return false;
193 }
194 
195 // FIXME: This should also handle inlining if we aren't going to do inlining in the
196 // main stack.
197 //
198 // Ideally we should remember the whole stack frame list, and then compare that
199 // to the current list.
200 
201 lldb::FrameComparison
202 ThreadPlanStepRange::CompareCurrentFrameToStartFrame()
203 {
204     FrameComparison frame_order;
205 
206     StackID cur_frame_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
207 
208     if (cur_frame_id == m_stack_id)
209     {
210         frame_order = eFrameCompareEqual;
211     }
212     else if (cur_frame_id < m_stack_id)
213     {
214         frame_order = eFrameCompareYounger;
215     }
216     else
217     {
218         frame_order = eFrameCompareOlder;
219     }
220     return frame_order;
221 }
222 
223 bool
224 ThreadPlanStepRange::StopOthers ()
225 {
226     if (m_stop_others == lldb::eOnlyThisThread
227         || m_stop_others == lldb::eOnlyDuringStepping)
228         return true;
229     else
230         return false;
231 }
232 
233 bool
234 ThreadPlanStepRange::WillStop ()
235 {
236     return true;
237 }
238 
239 StateType
240 ThreadPlanStepRange::GetPlanRunState ()
241 {
242     return eStateStepping;
243 }
244 
245 bool
246 ThreadPlanStepRange::MischiefManaged ()
247 {
248     bool done = true;
249     if (!IsPlanComplete())
250     {
251         if (InRange())
252         {
253             done = false;
254         }
255         else
256         {
257             FrameComparison frame_order = CompareCurrentFrameToStartFrame();
258             if (frame_order != eFrameCompareOlder)
259             {
260                 if (m_no_more_plans)
261                     done = true;
262                 else
263                     done = false;
264             }
265             else
266                 done = true;
267         }
268     }
269 
270     if (done)
271     {
272         LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
273         if (log)
274             log->Printf("Completed step through range plan.");
275         ThreadPlan::MischiefManaged ();
276         return true;
277     }
278     else
279     {
280         return false;
281     }
282 
283 }
284