Infrastructure for SW CallTrees and group operations
[dyninst.git] / stackwalk / src / dbginfo-stepper.C
1 /*
2  * Copyright (c) 1996-2011 Barton P. Miller
3  * 
4  * We provide the Paradyn Parallel Performance Tools (below
5  * described as "Paradyn") on an AS IS basis, and do not warrant its
6  * validity or performance.  We reserve the right to update, modify,
7  * or discontinue this software at any time.  We shall have no
8  * obligation to supply such updates or modifications or any other
9  * form of support to you.
10  * 
11  * By your use of Paradyn, you understand and agree that we (or any
12  * other person or entity with proprietary rights in Paradyn) are
13  * under no obligation to provide either maintenance services,
14  * update services, notices of latent defects, or correction of
15  * defects for Paradyn.
16  * 
17  * This library is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU Lesser General Public
19  * License as published by the Free Software Foundation; either
20  * version 2.1 of the License, or (at your option) any later version.
21  * 
22  * This library is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  * Lesser General Public License for more details.
26  * 
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30  */
31
32 #include "stackwalk/h/framestepper.h"
33 #include "stackwalk/h/frame.h"
34 #include "stackwalk/h/procstate.h"
35 #include "stackwalk/h/swk_errors.h"
36 #include "stackwalk/h/steppergroup.h"
37 #include "stackwalk/h/walker.h"
38 #include "stackwalk/src/dbgstepper-impl.h"
39 #include "stackwalk/src/linuxbsd-swk.h"
40 #include "dynutil/h/dyntypes.h"
41 #include "common/h/Types.h"
42
43 #include "symtabAPI/h/Symtab.h"
44
45 using namespace Dyninst;
46 using namespace Stackwalker;
47
48 static std::map<std::string, DwarfSW *> dwarf_info;
49
50 typedef enum {
51    storageAddr,
52    storageReg,
53    storageRegOffset
54 } storageClass;
55
56 typedef enum {
57    storageRef,
58    storageNoRef
59 } storageRefClass;
60
61 class VariableLocation {
62 public:
63    storageClass stClass;
64    storageRefClass refClass;
65    int reg;
66    MachRegister mr_reg;
67    long frameOffset;
68    Address lowPC;
69    Address hiPC;
70 };
71
72 #include <stdarg.h>
73 #include "dwarf.h"
74 #include "libdwarf.h"
75 #include "common/h/dwarfExpr.h"
76 #include "common/h/dwarfSW.h"
77 #include "common/h/Elf_X.h"
78
79 DwarfSW *getDwarfInfo(std::string s, unsigned addr_width)
80 {
81    Dwarf_Debug dbg;
82    DwarfSW *ret = NULL;
83    static std::map<std::string, DwarfSW *> dwarf_info;
84    std::map<std::string, DwarfSW *>::iterator i = dwarf_info.find(s);
85    if (i != dwarf_info.end())
86       return i->second;
87
88    bool result = getDwarfDebug(s, &dbg);
89    if (!result) goto done;
90
91    ret = new DwarfSW(dbg, addr_width);
92
93   done:
94    dwarf_info[s] = ret;
95    return ret;
96 }
97
98 DebugStepperImpl::DebugStepperImpl(Walker *w, DebugStepper *parent) :
99    FrameStepper(w),
100    parent_stepper(parent),
101    cur_frame(NULL),
102    depth_frame(NULL)
103 {
104 }
105
106 bool DebugStepperImpl::ReadMem(Address addr, void *buffer, unsigned size)
107 {
108    return getProcessState()->readMem(buffer, addr, size);
109 }
110
111 bool DebugStepperImpl::GetReg(MachRegister reg, MachRegisterVal &val)
112 {
113    using namespace SymtabAPI;
114    
115    const Frame *prevDepthFrame = depth_frame;
116   
117    if (reg.isFramePointer()) {
118       val = static_cast<MachRegisterVal>(depth_frame->getFP());
119       return true;
120    }
121
122    if (reg.isStackPointer()) {
123       val = static_cast<MachRegisterVal>(depth_frame->getSP());
124       return true;
125    }
126    
127    if (reg.isPC()) {
128       val = static_cast<MachRegisterVal>(depth_frame->getRA());
129       return true;
130    }
131
132    depth_frame = depth_frame->getPrevFrame();
133    if (!depth_frame)
134    {
135       bool bres =  getProcessState()->getRegValue(reg, cur_frame->getThread(), val);
136       depth_frame = prevDepthFrame;
137       return bres;
138    }
139
140    Offset offset;
141    void *symtab_v;
142    std::string lib;
143    depth_frame->getLibOffset(lib, offset, symtab_v);
144    Symtab *symtab = (Symtab*) symtab_v;
145    if (!symtab)
146    {
147      depth_frame = prevDepthFrame;
148      return false;
149    }
150
151    bool result = symtab->getRegValueAtFrame(offset, reg, val, this);
152    depth_frame = prevDepthFrame;
153    return result;
154 }
155
156 gcframe_ret_t DebugStepperImpl::getCallerFrame(const Frame &in, Frame &out)
157 {
158    LibAddrPair lib;
159    bool result;
160
161    result = getProcessState()->getLibraryTracker()->getLibraryAtAddr(in.getRA(), lib);
162    if (!result) {
163       sw_printf("[%s:%u] - Stackwalking through an invalid PC at %lx\n",
164                 __FILE__, __LINE__, in.getRA());
165       return gcf_stackbottom;
166    }
167    
168    DwarfSW *dinfo = getDwarfInfo(lib.first, walker->getProcessState()->getAddressWidth());
169    if (!dinfo) {
170       sw_printf("[%s:%u] - Could not open file %s for DWARF info\n",
171                 __FILE__, __LINE__, lib.first.c_str());
172       setLastError(err_nofile, "Could not open file for Debugging stackwalker\n");
173       return gcf_error;
174    }
175    if (!dinfo->hasFrameDebugInfo())
176    {
177       sw_printf("[%s:%u] - Library %s does not have stackwalking debug info\n",
178                  __FILE__, __LINE__, lib.first.c_str());
179       return gcf_not_me;
180    }   
181    Address pc = in.getRA() - lib.second;
182
183    bool isVsyscallPage = false;
184 #if defined(os_linux)
185    isVsyscallPage = (strstr(lib.first.c_str(), "[vsyscall-") != NULL);
186 #endif
187
188    cur_frame = &in;
189    gcframe_ret_t gcresult = getCallerFrameArch(pc, in, out, dinfo, isVsyscallPage);
190    cur_frame = NULL;
191    return gcresult;
192 }
193
194 void DebugStepperImpl::registerStepperGroup(StepperGroup *group)
195 {
196    unsigned addr_width = group->getWalker()->getProcessState()->getAddressWidth();
197    if (addr_width == 4)
198       group->addStepper(parent_stepper, 0, 0xffffffff);
199 #if defined(arch_64bit)
200    else if (addr_width == 8)
201       group->addStepper(parent_stepper, 0, 0xffffffffffffffff);
202 #endif
203    else
204       assert(0 && "Unknown architecture word size");
205 }
206
207 unsigned DebugStepperImpl::getPriority() const
208 {
209    return debugstepper_priority;
210 }
211
212 DebugStepperImpl::~DebugStepperImpl()
213 {
214 }
215
216 #if defined(arch_x86) || defined(arch_x86_64)
217 gcframe_ret_t DebugStepperImpl::getCallerFrameArch(Address pc, const Frame &in, 
218                                                    Frame &out, DwarfSW *dinfo,
219                                                    bool isVsyscallPage)
220 {
221    MachRegisterVal frame_value, stack_value, ret_value;
222    bool result;
223    FrameErrors_t frame_error = FE_No_Error;
224
225    Dyninst::Architecture arch;
226    unsigned addr_width = getProcessState()->getAddressWidth();
227    if (addr_width == 4)
228       arch = Dyninst::Arch_x86;
229    else
230       arch = Dyninst::Arch_x86_64;
231
232    depth_frame = cur_frame;
233
234    result = dinfo->getRegValueAtFrame(pc, Dyninst::ReturnAddr,
235                                       ret_value, arch, this, frame_error);
236
237    if (!result && frame_error == FE_No_Frame_Entry && isVsyscallPage) {
238       //Work-around kernel bug.  The vsyscall page location was randomized, but
239       // the debug info still has addresses from the old, pre-randomized days.
240       // See if we get any hits by assuming the address corresponds to the 
241       // old PC.
242       pc += 0xffffe000;
243       result = dinfo->getRegValueAtFrame(pc, Dyninst::ReturnAddr,
244                                          ret_value, arch, this, frame_error);
245
246    }
247    if (!result) {
248       sw_printf("[%s:%u] - Couldn't get return debug info at %lx, error: %u\n",
249                 __FILE__, __LINE__, in.getRA(), frame_error);
250       return gcf_not_me;
251    }
252
253  
254    
255    Dyninst::MachRegister frame_reg;
256    if (addr_width == 4)
257       frame_reg = x86::ebp;
258    else
259       frame_reg = x86_64::rbp;
260
261    result = dinfo->getRegValueAtFrame(pc, frame_reg,
262                                       frame_value, arch, this, frame_error);
263    if (!result) {
264       sw_printf("[%s:%u] - Couldn't get frame debug info at %lx\n",
265                  __FILE__, __LINE__, in.getRA());
266       return gcf_not_me;
267    }
268
269    result = dinfo->getRegValueAtFrame(pc, Dyninst::FrameBase,
270                                       stack_value, arch, this, frame_error);
271    if (!result) {
272       sw_printf("[%s:%u] - Couldn't get stack debug info at %lx\n",
273                  __FILE__, __LINE__, in.getRA());
274       return gcf_not_me;
275    }
276
277    out.setRA(ret_value);
278    out.setFP(frame_value);
279    out.setSP(stack_value);
280
281    return gcf_success;
282 }
283 #endif
284