1//===-- UnwindPlan.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/Symbol/UnwindPlan.h"
11
12#include "lldb/Core/ConstString.h"
13#include "lldb/Target/Process.h"
14#include "lldb/Target/RegisterContext.h"
15#include "lldb/Target/Thread.h"
16
17using namespace lldb;
18using namespace lldb_private;
19
20bool
21UnwindPlan::Row::RegisterLocation::operator == (const UnwindPlan::Row::RegisterLocation& rhs) const
22{
23    if (m_type == rhs.m_type)
24    {
25        switch (m_type)
26        {
27            case unspecified:
28            case undefined:
29            case same:
30                return true;
31
32            case atCFAPlusOffset:
33            case isCFAPlusOffset:
34                return m_location.offset == rhs.m_location.offset;
35
36            case inOtherRegister:
37                return m_location.reg_num == rhs.m_location.reg_num;
38
39            case atDWARFExpression:
40            case isDWARFExpression:
41                if (m_location.expr.length == rhs.m_location.expr.length)
42                    return !memcmp (m_location.expr.opcodes, rhs.m_location.expr.opcodes, m_location.expr.length);
43                break;
44        }
45    }
46    return false;
47}
48
49// This function doesn't copy the dwarf expression bytes; they must remain in allocated
50// memory for the lifespan of this UnwindPlan object.
51void
52UnwindPlan::Row::RegisterLocation::SetAtDWARFExpression (const uint8_t *opcodes, uint32_t len)
53{
54    m_type = atDWARFExpression;
55    m_location.expr.opcodes = opcodes;
56    m_location.expr.length = len;
57}
58
59// This function doesn't copy the dwarf expression bytes; they must remain in allocated
60// memory for the lifespan of this UnwindPlan object.
61void
62UnwindPlan::Row::RegisterLocation::SetIsDWARFExpression (const uint8_t *opcodes, uint32_t len)
63{
64    m_type = isDWARFExpression;
65    m_location.expr.opcodes = opcodes;
66    m_location.expr.length = len;
67}
68
69void
70UnwindPlan::Row::RegisterLocation::Dump (Stream &s, const UnwindPlan* unwind_plan, const UnwindPlan::Row* row, Thread* thread, bool verbose) const
71{
72    switch (m_type)
73    {
74        case unspecified:
75            if (verbose)
76                s.PutCString ("=<unspec>");
77            else
78                s.PutCString ("=!");
79            break;
80        case undefined:
81            if (verbose)
82                s.PutCString ("=<undef>");
83            else
84                s.PutCString ("=?");
85            break;
86        case same:
87            s.PutCString ("= <same>");
88            break;
89
90        case atCFAPlusOffset:
91        case isCFAPlusOffset:
92            {
93                s.PutChar('=');
94                if (m_type == atCFAPlusOffset)
95                    s.PutChar('[');
96                if (verbose)
97                    s.Printf ("CFA%+d", m_location.offset);
98
99                if (unwind_plan && row)
100                {
101                    const uint32_t cfa_reg = row->GetCFARegister();
102                    const RegisterInfo *cfa_reg_info = unwind_plan->GetRegisterInfo (thread, cfa_reg);
103                    const int32_t offset = row->GetCFAOffset() + m_location.offset;
104                    if (verbose)
105                    {
106                        if (cfa_reg_info)
107                            s.Printf (" (%s%+d)",  cfa_reg_info->name, offset);
108                        else
109                            s.Printf (" (reg(%u)%+d)",  cfa_reg, offset);
110                    }
111                    else
112                    {
113                        if (cfa_reg_info)
114                            s.Printf ("%s",  cfa_reg_info->name);
115                        else
116                            s.Printf ("reg(%u)",  cfa_reg);
117                        if (offset != 0)
118                            s.Printf ("%+d", offset);
119                    }
120                }
121                if (m_type == atCFAPlusOffset)
122                    s.PutChar(']');
123            }
124            break;
125
126        case inOtherRegister:
127            {
128                const RegisterInfo *other_reg_info = NULL;
129                if (unwind_plan)
130                    other_reg_info = unwind_plan->GetRegisterInfo (thread, m_location.reg_num);
131                if (other_reg_info)
132                    s.Printf ("=%s", other_reg_info->name);
133                else
134                    s.Printf ("=reg(%u)", m_location.reg_num);
135            }
136            break;
137
138        case atDWARFExpression:
139        case isDWARFExpression:
140            {
141                s.PutChar('=');
142                if (m_type == atDWARFExpression)
143                    s.PutCString("[dwarf-expr]");
144                else
145                    s.PutCString("dwarf-expr");
146            }
147            break;
148
149    }
150}
151
152void
153UnwindPlan::Row::Clear ()
154{
155    m_offset = 0;
156    m_cfa_reg_num = LLDB_INVALID_REGNUM;
157    m_cfa_offset = 0;
158    m_register_locations.clear();
159}
160
161void
162UnwindPlan::Row::Dump (Stream& s, const UnwindPlan* unwind_plan, Thread* thread, addr_t base_addr) const
163{
164    const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo (thread, GetCFARegister());
165
166    if (base_addr != LLDB_INVALID_ADDRESS)
167        s.Printf ("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset());
168    else
169        s.Printf ("0x%8.8" PRIx64 ": CFA=", GetOffset());
170
171    if (reg_info)
172        s.Printf ("%s", reg_info->name);
173    else
174        s.Printf ("reg(%u)", GetCFARegister());
175    s.Printf ("%+3d => ", GetCFAOffset ());
176    for (collection::const_iterator idx = m_register_locations.begin (); idx != m_register_locations.end (); ++idx)
177    {
178        reg_info = unwind_plan->GetRegisterInfo (thread, idx->first);
179        if (reg_info)
180            s.Printf ("%s", reg_info->name);
181        else
182            s.Printf ("reg(%u)", idx->first);
183        const bool verbose = false;
184        idx->second.Dump(s, unwind_plan, this, thread, verbose);
185        s.PutChar (' ');
186    }
187    s.EOL();
188}
189
190UnwindPlan::Row::Row() :
191    m_offset(0),
192    m_cfa_reg_num(LLDB_INVALID_REGNUM),
193    m_cfa_offset(0),
194    m_register_locations()
195{
196}
197
198bool
199UnwindPlan::Row::GetRegisterInfo (uint32_t reg_num, UnwindPlan::Row::RegisterLocation& register_location) const
200{
201    collection::const_iterator pos = m_register_locations.find(reg_num);
202    if (pos != m_register_locations.end())
203    {
204        register_location = pos->second;
205        return true;
206    }
207    return false;
208}
209
210void
211UnwindPlan::Row::SetRegisterInfo (uint32_t reg_num, const UnwindPlan::Row::RegisterLocation register_location)
212{
213    m_register_locations[reg_num] = register_location;
214}
215
216bool
217UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset (uint32_t reg_num, int32_t offset, bool can_replace)
218{
219    if (!can_replace && m_register_locations.find(reg_num) != m_register_locations.end())
220        return false;
221    RegisterLocation reg_loc;
222    reg_loc.SetAtCFAPlusOffset(offset);
223    m_register_locations[reg_num] = reg_loc;
224    return true;
225}
226
227bool
228UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset (uint32_t reg_num, int32_t offset, bool can_replace)
229{
230    if (!can_replace && m_register_locations.find(reg_num) != m_register_locations.end())
231        return false;
232    RegisterLocation reg_loc;
233    reg_loc.SetIsCFAPlusOffset(offset);
234    m_register_locations[reg_num] = reg_loc;
235    return true;
236}
237
238bool
239UnwindPlan::Row::SetRegisterLocationToUndefined (uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified)
240{
241    collection::iterator pos = m_register_locations.find(reg_num);
242    collection::iterator end = m_register_locations.end();
243
244    if (pos != end)
245    {
246        if (!can_replace)
247            return false;
248        if (can_replace_only_if_unspecified && !pos->second.IsUnspecified())
249            return false;
250    }
251    RegisterLocation reg_loc;
252    reg_loc.SetUndefined();
253    m_register_locations[reg_num] = reg_loc;
254    return true;
255}
256
257bool
258UnwindPlan::Row::SetRegisterLocationToUnspecified (uint32_t reg_num, bool can_replace)
259{
260    if (!can_replace && m_register_locations.find(reg_num) != m_register_locations.end())
261        return false;
262    RegisterLocation reg_loc;
263    reg_loc.SetUnspecified();
264    m_register_locations[reg_num] = reg_loc;
265    return true;
266}
267
268bool
269UnwindPlan::Row::SetRegisterLocationToRegister (uint32_t reg_num,
270                                                uint32_t other_reg_num,
271                                                bool can_replace)
272{
273    if (!can_replace && m_register_locations.find(reg_num) != m_register_locations.end())
274        return false;
275    RegisterLocation reg_loc;
276    reg_loc.SetInRegister(other_reg_num);
277    m_register_locations[reg_num] = reg_loc;
278    return true;
279}
280
281bool
282UnwindPlan::Row::SetRegisterLocationToSame (uint32_t reg_num, bool must_replace)
283{
284    if (must_replace && m_register_locations.find(reg_num) == m_register_locations.end())
285        return false;
286    RegisterLocation reg_loc;
287    reg_loc.SetSame();
288    m_register_locations[reg_num] = reg_loc;
289    return true;
290}
291
292void
293UnwindPlan::Row::SetCFARegister (uint32_t reg_num)
294{
295    m_cfa_reg_num = reg_num;
296}
297
298bool
299UnwindPlan::Row::operator == (const UnwindPlan::Row& rhs) const
300{
301    if (m_offset != rhs.m_offset || m_cfa_reg_num != rhs.m_cfa_reg_num || m_cfa_offset != rhs.m_cfa_offset)
302        return false;
303    return m_register_locations == rhs.m_register_locations;
304}
305
306void
307UnwindPlan::AppendRow (const UnwindPlan::RowSP &row_sp)
308{
309    if (m_row_list.empty() || m_row_list.back()->GetOffset() != row_sp->GetOffset())
310        m_row_list.push_back(row_sp);
311    else
312        m_row_list.back() = row_sp;
313}
314
315UnwindPlan::RowSP
316UnwindPlan::GetRowForFunctionOffset (int offset) const
317{
318    RowSP row;
319    if (!m_row_list.empty())
320    {
321        if (offset == -1)
322            row = m_row_list.back();
323        else
324        {
325            collection::const_iterator pos, end = m_row_list.end();
326            for (pos = m_row_list.begin(); pos != end; ++pos)
327            {
328                if ((*pos)->GetOffset() <= offset)
329                    row = *pos;
330                else
331                    break;
332            }
333        }
334    }
335    return row;
336}
337
338bool
339UnwindPlan::IsValidRowIndex (uint32_t idx) const
340{
341    return idx < m_row_list.size();
342}
343
344const UnwindPlan::RowSP
345UnwindPlan::GetRowAtIndex (uint32_t idx) const
346{
347    // You must call IsValidRowIndex(idx) first before calling this!!!
348    assert (idx < m_row_list.size());
349    return m_row_list[idx];
350}
351
352const UnwindPlan::RowSP
353UnwindPlan::GetLastRow () const
354{
355    // You must call GetRowCount() first to make sure there is at least one row
356    assert (!m_row_list.empty());
357    return m_row_list.back();
358}
359
360int
361UnwindPlan::GetRowCount () const
362{
363    return m_row_list.size ();
364}
365
366void
367UnwindPlan::SetPlanValidAddressRange (const AddressRange& range)
368{
369   if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0)
370       m_plan_valid_address_range = range;
371}
372
373bool
374UnwindPlan::PlanValidAtAddress (Address addr)
375{
376    if (!m_plan_valid_address_range.GetBaseAddress().IsValid() || m_plan_valid_address_range.GetByteSize() == 0)
377        return true;
378
379    if (!addr.IsValid())
380        return true;
381
382    if (m_plan_valid_address_range.ContainsFileAddress (addr))
383        return true;
384
385    return false;
386}
387
388void
389UnwindPlan::Dump (Stream& s, Thread *thread, lldb::addr_t base_addr) const
390{
391    if (!m_source_name.IsEmpty())
392    {
393        s.Printf ("This UnwindPlan originally sourced from %s\n", m_source_name.GetCString());
394    }
395    if (m_plan_valid_address_range.GetBaseAddress().IsValid() && m_plan_valid_address_range.GetByteSize() > 0)
396    {
397        s.PutCString ("Address range of this UnwindPlan: ");
398        TargetSP target_sp(thread->CalculateTarget());
399        m_plan_valid_address_range.Dump (&s, target_sp.get(), Address::DumpStyleSectionNameOffset);
400        s.EOL();
401    }
402    collection::const_iterator pos, begin = m_row_list.begin(), end = m_row_list.end();
403    for (pos = begin; pos != end; ++pos)
404    {
405        s.Printf ("row[%u]: ", (uint32_t)std::distance (begin, pos));
406        (*pos)->Dump(s, this, thread, base_addr);
407    }
408}
409
410void
411UnwindPlan::SetSourceName (const char *source)
412{
413    m_source_name = ConstString (source);
414}
415
416ConstString
417UnwindPlan::GetSourceName () const
418{
419    return m_source_name;
420}
421
422const RegisterInfo *
423UnwindPlan::GetRegisterInfo (Thread* thread, uint32_t unwind_reg) const
424{
425    if (thread)
426    {
427        RegisterContext *reg_ctx = thread->GetRegisterContext().get();
428        if (reg_ctx)
429        {
430            uint32_t reg;
431            if (m_register_kind == eRegisterKindLLDB)
432                reg = unwind_reg;
433            else
434                reg = reg_ctx->ConvertRegisterKindToRegisterNumber (m_register_kind, unwind_reg);
435            if (reg != LLDB_INVALID_REGNUM)
436                return reg_ctx->GetRegisterInfoAtIndex (reg);
437        }
438    }
439    return NULL;
440}
441
442