1 // stacktrace.cc - Functions for unwinding & inspecting the call stack.
3 /* Copyright (C) 2005 Free Software Foundation
5 This file is part of libgcj.
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
15 #include <java-interp.h>
16 #include <java-stack.h>
24 #include <java/lang/Class.h>
25 #include <java/lang/Long.h>
26 #include <java/util/ArrayList.h>
27 #include <java/util/IdentityHashMap.h>
28 #include <gnu/java/lang/MainThread.h>
29 #include <gnu/gcj/runtime/NameFinder.h>
31 #include <sysdep/backtrace.h>
33 using namespace java::lang
;
34 using namespace java::lang::reflect
;
35 using namespace java::util
;
36 using namespace gnu::gcj::runtime
;
38 // Maps ncode values to their containing native class.
39 // NOTE: Currently this Map contradicts class GC for native classes. This map
40 // (and the "new class stack") will need to use WeakReferences in order to
41 // enable native class GC.
42 static java::util::IdentityHashMap
*ncodeMap
;
44 // Check the "class stack" for any classes initialized since we were last
45 // called, and add them to ncodeMap.
47 _Jv_StackTrace::UpdateNCodeMap ()
49 // The Map should be large enough so that a typical Java app doesn't cause
50 // it to rehash, without using too much memory. ~5000 entries should be
53 ncodeMap
= new java::util::IdentityHashMap (5087);
56 while ((klass
= _Jv_PopClass ()))
58 //printf ("got %s\n", klass->name->data);
60 JvAssert (! _Jv_IsInterpretedClass (klass
));
62 for (int i
=0; i
< klass
->method_count
; i
++)
64 _Jv_Method
*method
= &klass
->methods
[i
];
65 // Add non-abstract methods to ncodeMap.
68 //printf("map->put 0x%x / %s.%s\n", method->ncode, klass->name->data,
69 // method->name->data);
70 ncodeMap
->put ((java::lang::Object
*) method
->ncode
, klass
);
76 // Given a native frame, return the class which this code belongs
77 // to. Returns NULL if this IP is not associated with a native Java class.
78 // If NCODE is supplied, it will be set with the ip for the entry point of the
81 _Jv_StackTrace::ClassForFrame (_Jv_StackFrame
*frame
)
83 JvAssert (frame
->type
== frame_native
);
85 // use _Unwind_FindEnclosingFunction to find start of method
86 //void *entryPoint = _Unwind_FindEnclosingFunction (ip);
88 // look it up in ncodeMap
90 klass
= (jclass
) ncodeMap
->get ((jobject
) frame
->start_ip
);
96 _Jv_StackTrace::UnwindTraceFn (struct _Unwind_Context
*context
, void *state_ptr
)
98 _Jv_UnwindState
*state
= (_Jv_UnwindState
*) state_ptr
;
99 jint pos
= state
->pos
;
101 // Check if the trace buffer needs to be extended.
102 if (pos
== state
->length
)
104 int newLength
= state
->length
*= 2;
105 void *newFrames
= _Jv_AllocBytes (newLength
* sizeof(_Jv_StackFrame
));
106 memcpy (newFrames
, state
->frames
, state
->length
* sizeof(_Jv_StackFrame
));
107 state
->frames
= (_Jv_StackFrame
*) newFrames
;
108 state
->length
= newLength
;
111 _Unwind_Ptr func_addr
= _Unwind_GetRegionStart (context
);
113 // If we see the interpreter's main function, "pop" an entry off the
114 // interpreter stack and use that instead, so that the trace goes through
115 // the java code and not the interpreter itself. This assumes a 1:1
116 // correspondance between call frames in the interpreted stack and occurances
117 // of _Jv_InterpMethod::run() on the native stack.
119 if (func_addr
== (_Unwind_Ptr
) &_Jv_InterpMethod::run
)
121 state
->frames
[pos
].type
= frame_interpreter
;
122 state
->frames
[pos
].interp
.meth
= state
->interp_frame
->self
;
123 state
->frames
[pos
].interp
.pc
= state
->interp_frame
->pc
;
124 state
->interp_frame
= state
->interp_frame
->next
;
129 state
->frames
[pos
].type
= frame_native
;
130 state
->frames
[pos
].ip
= (void *) _Unwind_GetIP (context
);
131 state
->frames
[pos
].start_ip
= (void *) func_addr
;
134 //printf ("unwind ip: %p\n", _Unwind_GetIP (context));
136 _Unwind_Reason_Code result
= _URC_NO_REASON
;
137 if (state
->trace_function
!= NULL
)
138 result
= (state
->trace_function
) (state
);
143 // Return a raw stack trace from the current point of execution. The raw
144 // trace will include all functions that have unwind info.
146 _Jv_StackTrace::GetStackTrace(void)
148 int trace_size
= 100;
149 _Jv_StackFrame frames
[trace_size
];
150 _Jv_UnwindState
state (trace_size
);
151 state
.frames
= (_Jv_StackFrame
*) &frames
;
153 #ifdef SJLJ_EXCEPTIONS
154 // The Unwind interface doesn't work with the SJLJ exception model.
155 // Fall back to a platform-specific unwinder.
156 fallback_backtrace (&state
);
157 #else /* SJLJ_EXCEPTIONS */
158 _Unwind_Backtrace (UnwindTraceFn
, &state
);
159 #endif /* SJLJ_EXCEPTIONS */
161 // Copy the trace and return it.
162 int traceSize
= sizeof (_Jv_StackTrace
) +
163 (sizeof (_Jv_StackFrame
) * state
.pos
);
164 _Jv_StackTrace
*trace
= (_Jv_StackTrace
*) _Jv_AllocBytes (traceSize
);
165 trace
->length
= state
.pos
;
166 memcpy (trace
->frames
, state
.frames
, sizeof (_Jv_StackFrame
) * state
.pos
);
171 _Jv_StackTrace::getLineNumberForFrame(_Jv_StackFrame
*frame
, NameFinder
*finder
,
172 jstring
*sourceFileName
, jint
*lineNum
)
175 if (frame
->type
== frame_interpreter
)
177 _Jv_InterpMethod
*interp_meth
= frame
->interp
.meth
;
178 _Jv_InterpClass
*interp_class
=
179 (_Jv_InterpClass
*) interp_meth
->defining_class
->aux_info
;
180 *sourceFileName
= interp_class
->source_file_name
;
181 *lineNum
= interp_meth
->get_source_line(frame
->interp
.pc
);
185 // Use dladdr() to determine in which binary the address IP resides.
186 #if defined (HAVE_DLFCN_H) && defined (HAVE_DLADDR)
187 extern char **_Jv_argv
;
189 jstring binaryName
= NULL
;
191 void *ip
= frame
->ip
;
192 _Unwind_Ptr offset
= 0;
194 if (dladdr (ip
, &info
))
197 binaryName
= JvNewStringUTF (info
.dli_fname
);
201 // addr2line expects relative addresses for shared libraries.
202 if (strcmp (info
.dli_fname
, _Jv_argv
[0]) == 0)
203 offset
= (_Unwind_Ptr
) ip
;
205 offset
= (_Unwind_Ptr
) ip
- (_Unwind_Ptr
) info
.dli_fbase
;
207 //printf ("linenum ip: %p\n", ip);
208 //printf ("%s: 0x%x\n", info.dli_fname, offset);
209 //offset -= sizeof(void *);
211 // The unwinder gives us the return address. In order to get the right
212 // line number for the stack trace, roll it back a little.
215 // printf ("%s: 0x%x\n", info.dli_fname, offset);
217 finder
->lookup (binaryName
, (jlong
) offset
);
218 *sourceFileName
= finder
->getSourceFile();
219 *lineNum
= finder
->getLineNum();
224 // Look up class and method info for the given stack frame, setting
225 // frame->klass and frame->meth if they are known.
227 _Jv_StackTrace::FillInFrameInfo (_Jv_StackFrame
*frame
)
230 _Jv_Method
*meth
= NULL
;
232 if (frame
->type
== frame_native
)
234 klass
= _Jv_StackTrace::ClassForFrame (frame
);
237 // Find method in class
238 for (int j
= 0; j
< klass
->method_count
; j
++)
240 if (klass
->methods
[j
].ncode
== frame
->start_ip
)
242 meth
= &klass
->methods
[j
];
248 else if (frame
->type
== frame_interpreter
)
250 _Jv_InterpMethod
*interp_meth
= frame
->interp
.meth
;
251 klass
= interp_meth
->defining_class
;
252 meth
= interp_meth
->self
;
256 JvFail ("Unknown frame type");
258 frame
->klass
= klass
;
262 // Convert raw stack frames to a Java array of StackTraceElement objects.
263 JArray
< ::java::lang::StackTraceElement
*>*
264 _Jv_StackTrace::GetStackTraceElements (_Jv_StackTrace
*trace
,
265 Throwable
*throwable
__attribute__((unused
)))
267 ArrayList
*list
= new ArrayList ();
269 #ifdef SJLJ_EXCEPTIONS
270 // We can't use the nCodeMap without unwinder support. Instead,
271 // fake the method name by giving the IP in hex - better than nothing.
272 jstring hex
= JvNewStringUTF ("0x");
274 for (int i
= 0; i
< trace
->length
; i
++)
276 jstring sourceFileName
= NULL
;
278 _Jv_StackFrame
*frame
= &trace
->frames
[i
];
280 jstring className
= NULL
;
281 jstring methodName
= hex
->concat (Long::toHexString ((jlong
) frame
->ip
));
283 StackTraceElement
*element
= new StackTraceElement (sourceFileName
,
284 lineNum
, className
, methodName
, 0);
288 #else /* SJLJ_EXCEPTIONS */
290 //JvSynchronized (ncodeMap);
293 NameFinder
*finder
= new NameFinder();
295 int end_idx
= trace
->length
- 1;
297 // First pass: strip superfluous frames from beginning and end of the trace.
298 for (int i
= 0; i
< trace
->length
; i
++)
300 _Jv_StackFrame
*frame
= &trace
->frames
[i
];
301 FillInFrameInfo (frame
);
303 if (!frame
->klass
|| !frame
->meth
)
307 // Throw away the top of the stack till we see:
308 // - the constructor(s) of this Throwable, or
309 // - the Throwable.fillInStackTrace call.
310 if (frame
->klass
== throwable
->getClass()
311 && strcmp (frame
->meth
->name
->chars(), "<init>") == 0)
314 if (frame
->klass
== &Throwable::class$
315 && strcmp (frame
->meth
->name
->chars(), "fillInStackTrace") == 0)
318 // End the trace at the application's main() method if we see call_main.
319 if (frame
->klass
== &gnu::java::lang::MainThread::class$
320 && strcmp (frame
->meth
->name
->chars(), "call_main") == 0)
324 // Second pass: Look up line-number info for remaining frames.
325 for (int i
= start_idx
; i
<= end_idx
; i
++)
327 _Jv_StackFrame
*frame
= &trace
->frames
[i
];
329 if (frame
->klass
== NULL
)
333 jstring className
= frame
->klass
->getName ();
334 jstring methodName
= NULL
;
336 methodName
= JvNewStringUTF (frame
->meth
->name
->chars());
338 jstring sourceFileName
= NULL
;
341 getLineNumberForFrame(frame
, finder
, &sourceFileName
, &lineNum
);
343 StackTraceElement
*element
= new StackTraceElement (sourceFileName
, lineNum
,
344 className
, methodName
, 0);
349 #endif /* SJLJ_EXCEPTIONS */
351 JArray
<Object
*> *array
= JvNewObjectArray (list
->size (),
352 &StackTraceElement::class$
, NULL
);
354 return (JArray
<StackTraceElement
*>*) list
->toArray (array
);
357 struct CallingClassTraceData
361 _Jv_Method
*foundMeth
;
362 bool seen_checkClass
;
366 _Jv_StackTrace::calling_class_trace_fn (_Jv_UnwindState
*state
)
368 CallingClassTraceData
*trace_data
= (CallingClassTraceData
*)
370 _Jv_StackFrame
*frame
= &state
->frames
[state
->pos
];
371 FillInFrameInfo (frame
);
373 if (trace_data
->seen_checkClass
375 && frame
->klass
!= trace_data
->checkClass
)
377 trace_data
->foundClass
= frame
->klass
;
378 trace_data
->foundMeth
= frame
->meth
;
379 return _URC_NORMAL_STOP
;
382 if (frame
->klass
== trace_data
->checkClass
)
383 trace_data
->seen_checkClass
= true;
385 return _URC_NO_REASON
;
388 // Find the class immediately above the given class on the call stack. Any
389 // intermediate non-Java
390 // frames are ignored. If the calling class could not be determined (eg because
391 // the unwinder is not supported on this platform), NULL is returned.
392 // This function is used to implement calling-classloader checks and reflection
393 // accessibility checks.
394 // CHECKCLASS is typically the class calling GetCallingClass. The first class
395 // above CHECKCLASS on the call stack will be returned.
397 _Jv_StackTrace::GetCallingClass (jclass checkClass
)
399 jclass result
= NULL
;
400 GetCallerInfo (checkClass
, &result
, NULL
);
405 _Jv_StackTrace::GetCallerInfo (jclass checkClass
, jclass
*caller_class
,
406 _Jv_Method
**caller_meth
)
408 #ifndef SJLJ_EXCEPTIONS
410 _Jv_StackFrame frames
[trace_size
];
411 _Jv_UnwindState
state (trace_size
);
412 state
.frames
= (_Jv_StackFrame
*) &frames
;
414 CallingClassTraceData trace_data
;
415 trace_data
.checkClass
= checkClass
;
416 trace_data
.seen_checkClass
= false;
417 trace_data
.foundClass
= NULL
;
418 trace_data
.foundMeth
= NULL
;
420 state
.trace_function
= calling_class_trace_fn
;
421 state
.trace_data
= (void *) &trace_data
;
423 //JvSynchronized (ncodeMap);
426 _Unwind_Backtrace (UnwindTraceFn
, &state
);
429 *caller_class
= trace_data
.foundClass
;
431 *caller_meth
= trace_data
.foundMeth
;
437 // Return a java array containing the Java classes on the stack above CHECKCLASS.
439 _Jv_StackTrace::GetClassContext (jclass checkClass
)
441 JArray
<jclass
> *result
= NULL
;
443 int trace_size
= 100;
444 _Jv_StackFrame frames
[trace_size
];
445 _Jv_UnwindState
state (trace_size
);
446 state
.frames
= (_Jv_StackFrame
*) &frames
;
448 //JvSynchronized (ncodeMap);
451 _Unwind_Backtrace (UnwindTraceFn
, &state
);
453 // Count the number of Java frames on the stack.
454 int jframe_count
= 0;
455 bool seen_checkClass
= false;
457 for (int i
= 0; i
< state
.pos
; i
++)
459 _Jv_StackFrame
*frame
= &state
.frames
[i
];
460 FillInFrameInfo (frame
);
472 seen_checkClass
= frame
->klass
== checkClass
;
474 result
= (JArray
<jclass
> *) _Jv_NewObjectArray (jframe_count
, &Class::class$
, NULL
);
477 for (int i
= start_pos
; i
< state
.pos
; i
++)
479 _Jv_StackFrame
*frame
= &state
.frames
[i
];
481 elements(result
)[pos
++] = frame
->klass
;
487 _Jv_StackTrace::non_system_trace_fn (_Jv_UnwindState
*state
)
489 _Jv_StackFrame
*frame
= &state
->frames
[state
->pos
];
490 FillInFrameInfo (frame
);
492 ClassLoader
*classLoader
= NULL
;
496 classLoader
= frame
->klass
->getClassLoaderInternal();
498 if (classLoader
!= NULL
)
500 state
->trace_data
= (void *) classLoader
;
501 return _URC_NORMAL_STOP
;
506 return _URC_NO_REASON
;
510 _Jv_StackTrace::GetFirstNonSystemClassLoader ()
513 _Jv_StackFrame frames
[trace_size
];
514 _Jv_UnwindState
state (trace_size
);
515 state
.frames
= (_Jv_StackFrame
*) &frames
;
516 state
.trace_function
= non_system_trace_fn
;
517 state
.trace_data
= NULL
;
519 //JvSynchronized (ncodeMap);
522 _Unwind_Backtrace (UnwindTraceFn
, &state
);
524 if (state
.trace_data
)
525 return (ClassLoader
*) state
.trace_data
;