1 //===-- StackFrameList.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/StackFrameList.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Core/StreamFile.h"
17 #include "lldb/Symbol/Block.h"
18 #include "lldb/Symbol/Function.h"
19 #include "lldb/Target/RegisterContext.h"
20 #include "lldb/Target/StackFrame.h"
21 #include "lldb/Target/Thread.h"
22 #include "lldb/Target/Unwind.h"
23 
24 //#define DEBUG_STACK_FRAMES 1
25 
26 using namespace lldb;
27 using namespace lldb_private;
28 
29 //----------------------------------------------------------------------
30 // StackFrameList constructor
31 //----------------------------------------------------------------------
32 StackFrameList::StackFrameList(Thread &thread, StackFrameList *prev_frames, bool show_inline_frames) :
33     m_thread (thread),
34     m_prev_frames_ap (prev_frames),
35     m_show_inlined_frames (show_inline_frames),
36     m_mutex (Mutex::eMutexTypeRecursive),
37     m_frames (),
38     m_selected_frame_idx (0)
39 {
40 }
41 
42 //----------------------------------------------------------------------
43 // Destructor
44 //----------------------------------------------------------------------
45 StackFrameList::~StackFrameList()
46 {
47 }
48 
49 
50 uint32_t
51 StackFrameList::GetNumFrames()
52 {
53     Mutex::Locker locker (m_mutex);
54 
55     if (m_frames.size() <= 1)
56     {
57         if (m_show_inlined_frames)
58         {
59 #if defined (DEBUG_STACK_FRAMES)
60             StreamFile s(stdout);
61 #endif
62             Unwind *unwinder = m_thread.GetUnwinder ();
63             addr_t pc, cfa;
64 
65             // If we are going to show inlined stack frames as actual frames,
66             // we need to calculate all concrete frames first, then iterate
67             // through all of them and count up how many inlined functions are
68             // in each frame.
69             const uint32_t unwind_frame_count = unwinder->GetFrameCount();
70 
71             StackFrameSP unwind_frame_sp;
72             for (uint32_t idx=0; idx<unwind_frame_count; ++idx)
73             {
74                 if (idx == 0)
75                 {
76                     // We might have already created frame zero, only create it
77                     // if we need to
78                     if (m_frames.empty())
79                     {
80                         m_thread.GetRegisterContext();
81                         unwind_frame_sp.reset (new StackFrame (m_frames.size(),
82                                                                idx,
83                                                                m_thread,
84                                                                m_thread.m_reg_context_sp,
85                                                                m_thread.m_reg_context_sp->GetSP(),
86                                                                m_thread.m_reg_context_sp->GetPC(),
87                                                                NULL));
88                         m_frames.push_back (unwind_frame_sp);
89                     }
90                     else
91                     {
92                         unwind_frame_sp = m_frames.front();
93                     }
94                 }
95                 else
96                 {
97                     const bool success = unwinder->GetFrameInfoAtIndex(idx, cfa, pc);
98                     assert (success);
99                     unwind_frame_sp.reset (new StackFrame (m_frames.size(), idx, m_thread, cfa, pc, NULL));
100                     m_frames.push_back (unwind_frame_sp);
101                 }
102 
103                 Block *block = unwind_frame_sp->GetSymbolContext (eSymbolContextBlock).block;
104 
105                 if (block)
106                 {
107                     for (block = block->GetContainingInlinedBlock(); block != NULL; block = block->GetInlinedParent ())
108                     {
109                         SymbolContext inline_sc;
110                         Block *parent_block = block->GetInlinedParent();
111 
112                         const bool is_inlined_frame = parent_block != NULL;
113 
114                         if (parent_block == NULL)
115                             parent_block = block->GetParent();
116 
117                         parent_block->CalculateSymbolContext (&inline_sc);
118 
119                         Address previous_frame_lookup_addr (m_frames.back()->GetFrameCodeAddress());
120                         if (unwind_frame_sp->GetFrameIndex() > 0 && m_frames.back().get() == unwind_frame_sp.get())
121                             previous_frame_lookup_addr.Slide (-1);
122 
123                         AddressRange range;
124                         block->GetRangeContainingAddress (previous_frame_lookup_addr, range);
125 
126                         const InlineFunctionInfo* inline_info = block->InlinedFunctionInfo();
127                         assert (inline_info);
128                         inline_sc.line_entry.range.GetBaseAddress() = m_frames.back()->GetFrameCodeAddress();
129                         inline_sc.line_entry.file = inline_info->GetCallSite().GetFile();
130                         inline_sc.line_entry.line = inline_info->GetCallSite().GetLine();
131                         inline_sc.line_entry.column = inline_info->GetCallSite().GetColumn();
132 
133                         StackFrameSP frame_sp(new StackFrame (m_frames.size(),
134                                                               idx,
135                                                               m_thread,
136                                                               unwind_frame_sp->GetRegisterContextSP (),
137                                                               unwind_frame_sp->GetStackID().GetCallFrameAddress(),  // CFA
138                                                               range.GetBaseAddress(),
139                                                               &inline_sc));                                           // The symbol context for this inline frame
140 
141                         if (is_inlined_frame)
142                             frame_sp->SetInlineBlockID (block->GetID());
143 
144                         m_frames.push_back (frame_sp);
145                     }
146                 }
147             }
148             StackFrameList *prev_frames = m_prev_frames_ap.get();
149             if (prev_frames)
150             {
151                 StackFrameList *curr_frames = this;
152 
153 #if defined (DEBUG_STACK_FRAMES)
154                 s.PutCString("prev_frames:\n");
155                 prev_frames->Dump (&s);
156                 s.PutCString("curr_frames:\n");
157                 curr_frames->Dump (&s);
158                 s.EOL();
159 #endif
160                 size_t curr_frame_num, prev_frame_num;
161 
162                 for (curr_frame_num = curr_frames->m_frames.size(), prev_frame_num = prev_frames->m_frames.size();
163                      curr_frame_num > 0 && prev_frame_num > 0;
164                      --curr_frame_num, --prev_frame_num)
165                 {
166                     const size_t curr_frame_idx = curr_frame_num-1;
167                     const size_t prev_frame_idx = prev_frame_num-1;
168                     StackFrameSP curr_frame_sp (curr_frames->m_frames[curr_frame_idx]);
169                     StackFrameSP prev_frame_sp (prev_frames->m_frames[prev_frame_idx]);
170 
171 #if defined (DEBUG_STACK_FRAMES)
172                     s.Printf("\nCurrent frame #%u ", curr_frame_idx);
173                     if (curr_frame_sp)
174                         curr_frame_sp->Dump (&s, true);
175                     else
176                         s.PutCString("NULL");
177                     s.Printf("\nPrevious frame #%u ", prev_frame_idx);
178                     if (prev_frame_sp)
179                         prev_frame_sp->Dump (&s, true);
180                     else
181                         s.PutCString("NULL");
182                     s.EOL();
183 #endif
184 
185                     StackFrame *curr_frame = curr_frame_sp.get();
186                     StackFrame *prev_frame = prev_frame_sp.get();
187 
188                     if (curr_frame == NULL || prev_frame == NULL)
189                         break;
190 
191                     // Do a quick sanity check to see if the CFA values are the same.
192                     if (curr_frame->m_id.GetCallFrameAddress() != prev_frame->m_id.GetCallFrameAddress())
193                         break;
194 
195                     // Now check our function or symbol
196                     SymbolContext curr_sc (curr_frame->GetSymbolContext (eSymbolContextFunction | eSymbolContextBlock | eSymbolContextSymbol));
197                     SymbolContext prev_sc (prev_frame->GetSymbolContext (eSymbolContextFunction | eSymbolContextBlock | eSymbolContextSymbol));
198                     if (curr_sc.function && curr_sc.function == prev_sc.function)
199                     {
200                         // Same function
201                         if (curr_sc.block != prev_sc.block)
202                         {
203                             // Same function different block
204                             if (m_show_inlined_frames)
205                                 break;
206                             else
207                                 prev_frame->SetSymbolContext (curr_frame->m_sc);
208                         }
209                     }
210                     else if (curr_sc.symbol && curr_sc.symbol == prev_sc.symbol)
211                     {
212                         // Same symbol
213                     }
214                     else if (curr_frame->GetFrameCodeAddress() != prev_frame->GetFrameCodeAddress())
215                     {
216                         // No symbols for this frame and the PC was different
217                         break;
218                     }
219 
220                     if (curr_frame->GetFrameCodeAddress() != prev_frame->GetFrameCodeAddress())
221                     {
222 #if defined (DEBUG_STACK_FRAMES)
223                         s.Printf("\nUpdating frame code address and symbol context in previous frame #%u to current frame #%u", prev_frame_idx, curr_frame_idx);
224 #endif
225                         // We have a different code frame address, we might need to copy
226                         // some stuff in prev_frame, yet update the code address...
227                         prev_frame->SetFrameCodeAddress (curr_frame->GetFrameCodeAddress());
228                         prev_frame->SetSymbolContext (curr_frame->m_sc);
229                     }
230 
231                     curr_frames->m_frames[curr_frame_idx] = prev_frames->m_frames[prev_frame_idx];
232 
233 #if defined (DEBUG_STACK_FRAMES)
234                     s.Printf("\nCopying previous frame #%u to current frame #%u", prev_frame_idx, curr_frame_idx);
235 #endif
236                 }
237                 // We are done with the old stack frame list, we can release it now
238                 m_prev_frames_ap.release();
239                 prev_frames = NULL;
240             }
241         }
242         else
243         {
244             m_frames.resize(m_thread.GetUnwinder()->GetFrameCount());
245         }
246     }
247     return m_frames.size();
248 }
249 
250 void
251 StackFrameList::Dump (Stream *s)
252 {
253     if (s == NULL)
254         return;
255     Mutex::Locker locker (m_mutex);
256 
257     const_iterator pos, begin = m_frames.begin(), end = m_frames.end();
258     for (pos = begin; pos != end; ++pos)
259     {
260         StackFrame *frame = (*pos).get();
261         s->Printf("%p: ", frame);
262         if (frame)
263             frame->Dump(s, true);
264         else
265             s->Printf("frame #%u", std::distance (begin, pos));
266         s->EOL();
267     }
268     s->EOL();
269 }
270 
271 StackFrameSP
272 StackFrameList::GetFrameAtIndex (uint32_t idx)
273 {
274     StackFrameSP frame_sp;
275     Mutex::Locker locker (m_mutex);
276     if (idx < m_frames.size())
277         frame_sp = m_frames[idx];
278 
279     if (frame_sp)
280         return frame_sp;
281 
282     // Special case the first frame (idx == 0) so that we don't need to
283     // know how many stack frames there are to get it. If we need any other
284     // frames, then we do need to know if "idx" is a valid index.
285     if (idx == 0)
286     {
287         // If this is the first frame, we want to share the thread register
288         // context with the stack frame at index zero.
289         m_thread.GetRegisterContext();
290         assert (m_thread.m_reg_context_sp.get());
291         frame_sp.reset (new StackFrame (0,
292                                         0,
293                                         m_thread,
294                                         m_thread.m_reg_context_sp,
295                                         m_thread.m_reg_context_sp->GetSP(),
296                                         m_thread.m_reg_context_sp->GetPC(),
297                                         NULL));
298 
299         if (m_show_inlined_frames)
300         {
301             Block *block = frame_sp->GetSymbolContext (eSymbolContextBlock).block;
302 
303             if (block)
304             {
305                 Block *inline_block = block->GetContainingInlinedBlock();
306                 if (inline_block)
307                     frame_sp->SetInlineBlockID (inline_block->GetID());
308             }
309         }
310         SetFrameAtIndex(idx, frame_sp);
311     }
312     else if (idx < GetNumFrames())
313     {
314         if (m_show_inlined_frames)
315         {
316             // When inline frames are enabled we cache up all frames in GetNumFrames()
317             frame_sp = m_frames[idx];
318         }
319         else
320         {
321             Unwind *unwinder = m_thread.GetUnwinder ();
322             if (unwinder)
323             {
324                 addr_t pc, cfa;
325                 if (unwinder->GetFrameInfoAtIndex(idx, cfa, pc))
326                 {
327                     frame_sp.reset (new StackFrame (idx, idx, m_thread, cfa, pc, NULL));
328                     SetFrameAtIndex(idx, frame_sp);
329                 }
330             }
331         }
332 
333     }
334     return frame_sp;
335 }
336 
337 
338 bool
339 StackFrameList::SetFrameAtIndex (uint32_t idx, StackFrameSP &frame_sp)
340 {
341     if (idx >= m_frames.size())
342         m_frames.resize(idx + 1);
343     // Make sure allocation succeeded by checking bounds again
344     if (idx < m_frames.size())
345     {
346         m_frames[idx] = frame_sp;
347         return true;
348     }
349     return false;   // resize failed, out of memory?
350 }
351 
352 uint32_t
353 StackFrameList::GetSelectedFrameIndex () const
354 {
355     Mutex::Locker locker (m_mutex);
356     return m_selected_frame_idx;
357 }
358 
359 
360 uint32_t
361 StackFrameList::SetSelectedFrame (lldb_private::StackFrame *frame)
362 {
363     Mutex::Locker locker (m_mutex);
364     const_iterator pos;
365     const_iterator begin = m_frames.begin();
366     const_iterator end = m_frames.end();
367     for (pos = begin; pos != end; ++pos)
368     {
369         if (pos->get() == frame)
370         {
371             m_selected_frame_idx = std::distance (begin, pos);
372             return m_selected_frame_idx;
373         }
374     }
375     m_selected_frame_idx = 0;
376     return m_selected_frame_idx;
377 }
378 
379 // Mark a stack frame as the current frame using the frame index
380 void
381 StackFrameList::SetSelectedFrameByIndex (uint32_t idx)
382 {
383     Mutex::Locker locker (m_mutex);
384     m_selected_frame_idx = idx;
385 }
386 
387 // The thread has been run, reset the number stack frames to zero so we can
388 // determine how many frames we have lazily.
389 void
390 StackFrameList::Clear ()
391 {
392     Mutex::Locker locker (m_mutex);
393     m_frames.clear();
394 }
395 
396 void
397 StackFrameList::InvalidateFrames (uint32_t start_idx)
398 {
399     Mutex::Locker locker (m_mutex);
400     if (m_show_inlined_frames)
401     {
402         Clear();
403     }
404     else
405     {
406         const size_t num_frames = m_frames.size();
407         while (start_idx < num_frames)
408         {
409             m_frames[start_idx].reset();
410             ++start_idx;
411         }
412     }
413 }
414