1
/*
2
 * $Id$
3
 *
4
 * Copyright (C) 2003-2009 JNode.org
5
 *
6
 * This library is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation; either version 2.1 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful, but 
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
14
 * License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this library; If not, write to the Free Software Foundation, Inc., 
18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
 */
20
 
21
package org.jnode.vm;
22
23
import org.jnode.annotation.Internal;
24
import org.jnode.annotation.KernelSpace;
25
import org.jnode.annotation.MagicPermission;
26
import org.jnode.vm.classmgr.VmCompiledCode;
27
import org.jnode.vm.classmgr.VmMethod;
28
import org.jnode.vm.classmgr.VmType;
29
import org.jnode.vm.scheduler.VmThread;
30
import org.vmmagic.unboxed.Address;
31
import org.vmmagic.unboxed.Offset;
32
33
/**
34
 * Abstract class for reading information from stack frames.
35
 *
36
 * @author Ewout Prangsma (epr@users.sourceforge.net)
37
 */
38
@MagicPermission
39
public abstract class VmStackReader extends VmSystemObject {
40
41
    /**
42
     * Gets the previous frame (if any)
43
     *
44
     * @param sf The stackframe to get the previous frame from.
45
     * @return The previous frame or null.
46
     */
47
    @KernelSpace
48
    @Internal
49
    public final Address getPrevious(Address sf) {
50
        if (isValid(sf)) {
51
            return sf.loadAddress(getPreviousOffset(sf));
52
        } else {
53
            return null;
54
        }
55
    }
56
57
    /**
58
     * Gets the method of a given stackframe.
59
     *
60
     * @param sf Stackframe pointer
61
     * @return The method
62
     */
63
    @KernelSpace
64
    final VmMethod getMethod(Address sf) {
65
        final int ccid = sf.loadInt(getMethodIdOffset(sf));
66
        if (ccid == 0) {
67
            return null;
68
        } else {
69
            final VmCompiledCode cc = Vm.getCompiledMethods().get(ccid);
70
            if (cc == null) {
71
                // (This can happen if an exception is thrown while a frame
72
                // for a 'native' method call is on the stack.  A panic is not a 
73
                // good idea.  It is better to generate a stack trace with a missing
74
                // method name.)
75
                
76
                // Unsafe.die("Unknown ccid found on stack");
77
                return null;
78
            } else {
79
                return cc.getMethod();
80
            }
81
        }
82
    }
83
84
    /**
85
     * Gets the compiled code of a given stackframe.
86
     *
87
     * @param sf Stackframe pointer
88
     * @return The compiled code
89
     */
90
    final VmCompiledCode getCompiledCode(Address sf) {
91
        final int ccid = sf.loadInt(getMethodIdOffset(sf));
92
        if (ccid == 0) {
93
            return null;
94
        } else {
95
            return Vm.getCompiledMethods().get(ccid);
96
        }
97
    }
98
99
    /**
100
     * Gets the return address of a given stackframe.
101
     *
102
     * @param sf Stackframe pointer
103
     * @return The address
104
     */
105
    @Internal
106
    public final Address getReturnAddress(Address sf) {
107
        return sf.loadAddress(getReturnAddressOffset(sf));
108
    }
109
110
    /**
111
     * Is a given stackframe valid?
112
     *
113
     * @param sf
114
     * @return boolean
115
     */
116
    @KernelSpace
117
    final boolean isValid(Address sf) {
118
        if (sf == null) {
119
            return false;
120
        }
121
        if (getMethod(sf) == null) {
122
            return false;
123
        }
124
        return true;
125
    }
126
127
    /**
128
     * Is the given frame a bottom of stack marker?
129
     *
130
     * @param sf
131
     * @return boolean
132
     */
133
    final boolean isStackBottom(Address sf) {
134
        if (sf == null) {
135
            return true;
136
        }
137
        return (getMethod(sf) == null)
138
            && (getPrevious(sf) == null);
139
    }
140
141
    /**
142
     * Gets the stacktrace for a given current frame.
143
     *
144
     * @param frame    The address of the current frame.
145
     * @param ip       The instruction pointer of the given frame
146
     * @param limit    Maximum length of returned array.
147
     * @return VmStackFrame[]
148
     */
149
    @Internal
150
    public final VmStackFrame[] getVmStackTrace(Address frame, Address ip, int limit) {
151
152
        final VmStackFrameEnumerator sfEnum = new VmStackFrameEnumerator(this, frame, ip);
153
        int count = 0;
154
        while (sfEnum.isValid() && (count < limit)) {
155
            count++;
156
            sfEnum.next();
157
        }
158
//      if ((f != null) && !isStackBottom(f) && (count < limit)) {
159
//          Unsafe.debug("Corrupted stack!, st.length=");
160
//          Unsafe.debug(count);
161
//          Unsafe.debug(" f.magic=");
162
//          //Unsafe.die();
163
//      }
164
165
        final VmStackFrame[] stack = new VmStackFrame[count];
166
        sfEnum.reset(frame, ip);
167
        for (int i = 0; i < count; i++) {
168
            stack[i] = new VmStackFrame(sfEnum.getMethod(), sfEnum.getProgramCounter());
169
            sfEnum.next();
170
        }
171
172
        return stack;
173
    }
174
175
    /**
176
     * Count the number of stackframe from a given frame.
177
     *
178
     * @param sf
179
     * @return int
180
     */
181
    @Internal
182
    public final int countStackFrames(Address sf) {
183
        int count = 0;
184
        while (isValid(sf)) {
185
            count++;
186
            sf = getPrevious(sf);
187
        }
188
        return count;
189
    }
190
191
    /**
192
     * Show the current stacktrace using Screen.debug.
193
     */
194
    @KernelSpace
195
    public final void debugStackTrace() {
196
        debugStackTrace(25);
197
    }
198
199
    /**
200
     * Show the current stacktrace using Screen.debug.
201
     */
202
    @KernelSpace
203
    public final void debugStackTrace(int max) {
204
        Address f = VmMagic.getCurrentFrame();
205
        Unsafe.debug("\nDebug stacktrace: ");
206
        boolean first = true;
207
        while (isValid(f) && (max > 0)) {
208
            if (first) {
209
                first = false;
210
            } else {
211
                Unsafe.debug(", ");
212
            }
213
            final VmMethod method = getMethod(f);
214
            final VmType<?> vmClass = method.getDeclaringClass();
215
            Unsafe.debug(vmClass.getName());
216
            Unsafe.debug("::");
217
            Unsafe.debug(method.getName());
218
            Unsafe.debug('\n');
219
            f = getPrevious(f);
220
            max--;
221
        }
222
        if (isValid(f)) {
223
            Unsafe.debug("...");
224
        }
225
    }
226
227
    /**
228
     * Show the current stacktrace using Screen.debug.
229
     * TODO that method only exist to have line numbers : find a way to add line numbers to debugStackTrace(max)
230
     */
231
    @KernelSpace
232
    public final void debugStackTraceWithLineNumbers(int max) {
233
        final VmThread current = VmThread.currentThread();
234
        final VmStackFrame[] frames = (VmStackFrame[]) VmThread.getStackTrace(current);
235
        if (frames == null) {
236
            Unsafe.debug("Debug stacktrace:<no stack trace>\n");
237
        } else {
238
            Unsafe.debug("Debug stacktrace: ");
239
            for (int i = 0; i < frames.length; i++) {
240
                final VmStackFrame s = (VmStackFrame) frames[i];
241
                Unsafe.debug(s.getMethod().getDeclaringClass().getName());
242
                Unsafe.debug("::");
243
                Unsafe.debug(s.getMethod().getName());
244
                Unsafe.debug(":");
245
                Unsafe.debug(s.getLocationInfo());
246
                Unsafe.debug('\n');
247
            }
248
        }
249
    }
250
251
    /**
252
     * Show the stacktrace of the given thread using Screen.debug.
253
     */
254
    @KernelSpace
255
    public final void debugStackTrace(VmThread thread) {
256
        Address f = thread.getStackFrame();
257
        Unsafe.debug("Debug stacktrace: ");
258
        boolean first = true;
259
        int max = 20;
260
        while (isValid(f) && (max > 0)) {
261
            if (first) {
262
                first = false;
263
            } else {
264
                Unsafe.debug(", ");
265
            }
266
            final VmMethod method = getMethod(f);
267
            final VmType vmClass = method.getDeclaringClass();
268
            Unsafe.debug(vmClass.getName());
269
            Unsafe.debug("::");
270
            Unsafe.debug(method.getName());
271
            f = getPrevious(f);
272
            max--;
273
        }
274
        if (isValid(f)) {
275
            Unsafe.debug("...");
276
        }
277
    }
278
279
    /**
280
     * Gets the offset within the frame of previous frame (if any)
281
     *
282
     * @param sf The stackframe to get the previous frame from.
283
     * @return The previous frame or null.
284
     */
285
    @KernelSpace
286
    protected abstract Offset getPreviousOffset(Address sf);
287
288
    /**
289
     * Gets the offset within the stackframe of compiled method id.
290
     *
291
     * @param sf Stackframe pointer
292
     * @return The method id offset
293
     */
294
    @KernelSpace
295
    protected abstract Offset getMethodIdOffset(Address sf);
296
297
    /**
298
     * Gets the offset within the stackframe of the return address.
299
     *
300
     * @param sf Stackframe pointer
301
     * @return The return address offset
302
     */
303
    @KernelSpace
304
    protected abstract Offset getReturnAddressOffset(Address sf);
305
}