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("\nprev_frames:\n");
155                 prev_frames->Dump (&s);
156                 s.PutCString("\ncurr_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                         }
207                     }
208                     else if (curr_sc.symbol && curr_sc.symbol == prev_sc.symbol)
209                     {
210                         // Same symbol
211                     }
212                     else if (curr_frame->GetFrameCodeAddress() != prev_frame->GetFrameCodeAddress())
213                     {
214                         // No symbols for this frame and the PC was different
215                         break;
216                     }
217 
218                     curr_frame->UpdateCurrentFrameFromPreviousFrame (*prev_frame);
219 
220 #if defined (DEBUG_STACK_FRAMES)
221                     s.Printf("\n    Copying previous frame to current frame");
222 #endif
223                 }
224                 // We are done with the old stack frame list, we can release it now
225                 m_prev_frames_ap.release();
226                 prev_frames = NULL;
227             }
228 
229 #if defined (DEBUG_STACK_FRAMES)
230                 s.PutCString("\n\nNew frames:\n");
231                 Dump (&s);
232                 s.EOL();
233 #endif
234         }
235         else
236         {
237             m_frames.resize(m_thread.GetUnwinder()->GetFrameCount());
238         }
239     }
240     return m_frames.size();
241 }
242 
243 void
244 StackFrameList::Dump (Stream *s)
245 {
246     if (s == NULL)
247         return;
248     Mutex::Locker locker (m_mutex);
249 
250     const_iterator pos, begin = m_frames.begin(), end = m_frames.end();
251     for (pos = begin; pos != end; ++pos)
252     {
253         StackFrame *frame = (*pos).get();
254         s->Printf("%p: ", frame);
255         if (frame)
256             frame->Dump(s, true);
257         else
258             s->Printf("frame #%u", std::distance (begin, pos));
259         s->EOL();
260     }
261     s->EOL();
262 }
263 
264 StackFrameSP
265 StackFrameList::GetFrameAtIndex (uint32_t idx)
266 {
267     StackFrameSP frame_sp;
268     Mutex::Locker locker (m_mutex);
269     if (idx < m_frames.size())
270         frame_sp = m_frames[idx];
271 
272     if (frame_sp)
273         return frame_sp;
274 
275     // Special case the first frame (idx == 0) so that we don't need to
276     // know how many stack frames there are to get it. If we need any other
277     // frames, then we do need to know if "idx" is a valid index.
278     if (idx == 0)
279     {
280         // If this is the first frame, we want to share the thread register
281         // context with the stack frame at index zero.
282         m_thread.GetRegisterContext();
283         assert (m_thread.m_reg_context_sp.get());
284         frame_sp.reset (new StackFrame (0,
285                                         0,
286                                         m_thread,
287                                         m_thread.m_reg_context_sp,
288                                         m_thread.m_reg_context_sp->GetSP(),
289                                         m_thread.m_reg_context_sp->GetPC(),
290                                         NULL));
291 
292         if (m_show_inlined_frames)
293         {
294             Block *block = frame_sp->GetSymbolContext (eSymbolContextBlock).block;
295 
296             if (block)
297             {
298                 Block *inline_block = block->GetContainingInlinedBlock();
299                 if (inline_block)
300                     frame_sp->SetInlineBlockID (inline_block->GetID());
301             }
302         }
303         SetFrameAtIndex(idx, frame_sp);
304     }
305     else if (idx < GetNumFrames())
306     {
307         if (m_show_inlined_frames)
308         {
309             // When inline frames are enabled we cache up all frames in GetNumFrames()
310             frame_sp = m_frames[idx];
311         }
312         else
313         {
314             Unwind *unwinder = m_thread.GetUnwinder ();
315             if (unwinder)
316             {
317                 addr_t pc, cfa;
318                 if (unwinder->GetFrameInfoAtIndex(idx, cfa, pc))
319                 {
320                     frame_sp.reset (new StackFrame (idx, idx, m_thread, cfa, pc, NULL));
321                     SetFrameAtIndex(idx, frame_sp);
322                 }
323             }
324         }
325 
326     }
327     return frame_sp;
328 }
329 
330 
331 bool
332 StackFrameList::SetFrameAtIndex (uint32_t idx, StackFrameSP &frame_sp)
333 {
334     if (idx >= m_frames.size())
335         m_frames.resize(idx + 1);
336     // Make sure allocation succeeded by checking bounds again
337     if (idx < m_frames.size())
338     {
339         m_frames[idx] = frame_sp;
340         return true;
341     }
342     return false;   // resize failed, out of memory?
343 }
344 
345 uint32_t
346 StackFrameList::GetSelectedFrameIndex () const
347 {
348     Mutex::Locker locker (m_mutex);
349     return m_selected_frame_idx;
350 }
351 
352 
353 uint32_t
354 StackFrameList::SetSelectedFrame (lldb_private::StackFrame *frame)
355 {
356     Mutex::Locker locker (m_mutex);
357     const_iterator pos;
358     const_iterator begin = m_frames.begin();
359     const_iterator end = m_frames.end();
360     for (pos = begin; pos != end; ++pos)
361     {
362         if (pos->get() == frame)
363         {
364             m_selected_frame_idx = std::distance (begin, pos);
365             return m_selected_frame_idx;
366         }
367     }
368     m_selected_frame_idx = 0;
369     return m_selected_frame_idx;
370 }
371 
372 // Mark a stack frame as the current frame using the frame index
373 void
374 StackFrameList::SetSelectedFrameByIndex (uint32_t idx)
375 {
376     Mutex::Locker locker (m_mutex);
377     m_selected_frame_idx = idx;
378 }
379 
380 // The thread has been run, reset the number stack frames to zero so we can
381 // determine how many frames we have lazily.
382 void
383 StackFrameList::Clear ()
384 {
385     Mutex::Locker locker (m_mutex);
386     m_frames.clear();
387 }
388 
389 void
390 StackFrameList::InvalidateFrames (uint32_t start_idx)
391 {
392     Mutex::Locker locker (m_mutex);
393     if (m_show_inlined_frames)
394     {
395         Clear();
396     }
397     else
398     {
399         const size_t num_frames = m_frames.size();
400         while (start_idx < num_frames)
401         {
402             m_frames[start_idx].reset();
403             ++start_idx;
404         }
405     }
406 }
407