More fixes for local variable support for StackwalkerAPI
[dyninst.git] / stackwalk / h / local_var.h
1 #if !defined (local_var_h_)
2 #define local_var_h_
3
4 #include <vector>
5 #include "Symbol.h"
6 #include "Symtab.h"
7 #include "Type.h"
8 #include "Function.h"
9
10 #include "frame.h"
11 #include "procstate.h"
12 #include "walker.h"
13
14 using namespace Dyninst;
15 using namespace SymtabAPI;
16 using namespace Stackwalker;
17
18 class LVReader : public MemRegReader
19 {
20  private:
21    ProcessState *proc;
22    int current_depth;
23    std::vector<Frame> *swalk;
24    Dyninst::THR_ID thrd;
25
26    bool isFrameRegister(MachRegister reg)
27    {
28       return (reg == x86::EBP || reg == MachRegFrameBase);
29    }
30
31    bool isStackRegister(MachRegister reg)
32    {
33       return (reg == x86::ESP);
34    }
35  public:
36
37    LVReader(ProcessState *p, int f, std::vector<Frame> *s, Dyninst::THR_ID t) :
38       proc(p),
39       current_depth(f),
40       swalk(s),
41       thrd(t)
42    {
43    }
44
45    virtual bool ReadMem(Address addr, void *buffer, unsigned size)
46    {
47       return proc->readMem(buffer, addr, size);
48    }
49
50
51    virtual bool GetReg(MachRegister reg, MachRegisterVal &val)
52    {
53       Frame &f = (*swalk)[current_depth];
54       if (isFrameRegister(reg)) {
55          val = static_cast<MachRegisterVal>(f.getFP());
56          return true;
57       }
58       if (isStackRegister(reg)) {
59          val = static_cast<MachRegisterVal>(f.getSP());
60          return true;
61       }
62       if (reg == MachRegReturn) {
63          val = static_cast<MachRegisterVal>(f.getRA());
64          return true;
65       }
66
67       if (!current_depth) {
68          return proc->getRegValue(reg, thrd, val);
69       }
70
71       current_depth--;
72       Frame &g = (*swalk)[current_depth];
73       Offset offset;
74       void *symtab_v;
75       std::string lib;
76       g.getLibOffset(lib, offset, symtab_v);
77       Symtab *symtab = (Symtab *)(symtab_v);
78       if (!symtab)
79          return false;
80       
81       bool result = symtab->getRegValueAtFrame(offset, reg, val, this);
82       current_depth++;
83       return result;
84    }
85
86    virtual ~LVReader() {}
87 };
88
89
90 /**
91  * Given a StackwalkerAPI frame, return the SymtabAPI function
92  * that created the frame.
93  **/
94 static Dyninst::SymtabAPI::Function *getFunctionForFrame(Frame f)
95 {
96    Offset offset;
97    void *symtab_v;
98    std::string lib_name;
99    bool result = f.getLibOffset(lib_name, offset, symtab_v);
100    if (!result || !symtab_v)
101       return NULL;
102    Symtab *symtab = (Symtab *) symtab_v;
103    
104    Function *func;
105    result = symtab->getContainingFunction(offset, func);
106    if (!result)
107       return NULL;
108    return func;
109 }
110
111 /**
112  * Given a frame in a stackwalk, and a local variable, get the value
113  * of that local variable in the frame.
114  *
115  * 'localVar' is the variable that we're getting the value of.
116  * 'swalk' is a stackwalk from StackwalkerAPI
117  * 'frame' is an index into swalk and notes the frame that we'll be reading
118  *   the variable from.  localVar should be part of the frame defined by
119  *   swalk[frame]
120  * 'out_buffer' is a buffer where we will write the value of the local variable.
121  * out_buffer_size should be the size of out_buffer, used to prevent buffer overflows
122  *
123  * getLocalVariableValue will return one of the following on success or error
124  **/
125 static int glvv_Success = 0;
126 static int glvv_EParam = -1;
127 static int glvv_EOutOfScope = -2;
128 static int glvv_EBufferSize = -3;
129 static int glvv_EUnknown = -4;
130
131 static int getLocalVariableValue(localVar *var, 
132                                  std::vector<Frame> &swalk, unsigned frame,
133                                  void *out_buffer, unsigned out_buffer_size)
134 {
135    bool result;
136
137    if (!var || frame < 0 || frame >= swalk.size() || !out_buffer) {
138       return glvv_EParam;
139    }
140
141    /**
142     * Find the SymtabAPI object for this frame
143     * Find the offset for this frame's RA()
144     **/
145    std::string lib_name;
146    Offset offset;
147    void *symtab_v;
148    swalk[frame].getLibOffset(lib_name, offset, symtab_v);
149    THR_ID thrd = swalk[frame].getThread();
150    ProcessState *proc = swalk[frame].getWalker()->getProcessState();
151
152    /**
153     * Find the variable location that is valid at this point.
154     **/
155    bool deref;
156    std::vector<VariableLocation> &locs = var->getLocationLists();
157    std::vector<VariableLocation>::iterator i;
158    for (i = locs.begin(); i != locs.end(); i++) {
159       if (i->lowPC <= offset && offset < i->hiPC) {
160          break;
161       }
162    }
163    if (i == locs.end()) {
164       return glvv_EOutOfScope;
165    }
166    VariableLocation &loc = *i;
167    
168    /**
169     * Interpret the variable location
170     **/
171    Address var_addr = 0;
172    deref = (loc.refClass == storageRef);
173    switch (loc.stClass) { 
174       case storageAddr:
175          var_addr = loc.frameOffset;
176          break;
177       case storageReg:
178       case storageRegOffset: {
179          MachRegisterVal reg_value;
180          MachRegister reg = loc.reg;
181          if (loc.stClass == storageRegOffset && loc.reg == -1) {
182             reg = MachRegFrameBase;
183          }
184          
185          LVReader r(proc, frame, &swalk, thrd);
186          result = r.GetReg(reg, reg_value);
187          
188          if (loc.stClass == storageReg) {
189             var_addr = reg_value;
190          }
191          else {
192             deref = true;
193             var_addr = reg_value + loc.frameOffset;
194          }
195          break;
196       }
197    }
198
199    /** 
200     * Get the size of the variable
201     **/
202    unsigned size = out_buffer_size;
203    Type *var_type = var->getType();
204    if (var_type) {
205       size = var_type->getSize();
206    }
207    if (size > out_buffer_size) {
208       return glvv_EBufferSize;
209    }
210
211    /**
212     * Read the resulting value
213     **/
214    if (deref) {
215       result = proc->readMem(out_buffer, var_addr, size);
216       if (!result) {
217          return glvv_EUnknown;
218       }
219       return glvv_Success;
220    }
221
222    if (size > sizeof(var_addr)) {
223       //Value stored in register, but larger than a register?
224       return glvv_EBufferSize;
225    }
226    memcpy(out_buffer, &var_addr, size);
227
228    return glvv_Success;
229 }
230
231 #endif