PR c++/29886
[official-gcc.git] / libjava / stacktrace.cc
blob7f967baccf08baa113a6b8e86891087a14594eb3
1 // stacktrace.cc - Functions for unwinding & inspecting the call stack.
3 /* Copyright (C) 2005, 2006 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
9 details. */
11 #include <config.h>
12 #include <platform.h>
14 #include <jvm.h>
15 #include <gcj/cni.h>
16 #include <java-interp.h>
17 #include <java-stack.h>
19 #include <stdio.h>
21 #include <java/lang/Boolean.h>
22 #include <java/lang/Class.h>
23 #include <java/lang/Long.h>
24 #include <java/security/AccessController.h>
25 #include <java/util/ArrayList.h>
26 #include <java/util/IdentityHashMap.h>
27 #include <gnu/classpath/jdwp/Jdwp.h>
28 #include <gnu/java/lang/MainThread.h>
29 #include <gnu/gcj/runtime/NameFinder.h>
30 #include <gnu/gcj/runtime/StringBuffer.h>
32 #include <sysdep/backtrace.h>
33 #include <sysdep/descriptor.h>
35 using namespace java::lang;
36 using namespace java::lang::reflect;
37 using namespace java::util;
38 using namespace gnu::gcj::runtime;
40 // Maps ncode values to their containing native class.
41 // NOTE: Currently this Map contradicts class GC for native classes. This map
42 // (and the "new class stack") will need to use WeakReferences in order to
43 // enable native class GC.
44 static java::util::IdentityHashMap *ncodeMap;
46 // Check the "class stack" for any classes initialized since we were last
47 // called, and add them to ncodeMap.
48 void
49 _Jv_StackTrace::UpdateNCodeMap ()
51 // The Map should be large enough so that a typical Java app doesn't cause
52 // it to rehash, without using too much memory. ~5000 entries should be
53 // enough.
54 if (ncodeMap == NULL)
55 ncodeMap = new java::util::IdentityHashMap (5087);
57 jclass klass;
58 while ((klass = _Jv_PopClass ()))
59 if (!_Jv_IsInterpretedClass (klass))
61 //printf ("got %s\n", klass->name->data);
62 for (int i = 0; i < klass->method_count; i++)
64 _Jv_Method *method = &klass->methods[i];
65 void *ncode = method->ncode;
66 // Add non-abstract methods to ncodeMap.
67 if (ncode)
69 ncode = UNWRAP_FUNCTION_DESCRIPTOR (ncode);
70 ncodeMap->put ((java::lang::Object *) 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
79 // enclosing method.
80 jclass
81 _Jv_StackTrace::ClassForFrame (_Jv_StackFrame *frame)
83 JvAssert (frame->type == frame_native);
84 jclass klass = NULL;
86 // look it up in ncodeMap
87 if (frame->start_ip)
88 klass = (jclass) ncodeMap->get ((jobject) frame->start_ip);
90 return klass;
93 _Unwind_Reason_Code
94 _Jv_StackTrace::UnwindTraceFn (struct _Unwind_Context *context, void *state_ptr)
96 _Jv_UnwindState *state = (_Jv_UnwindState *) state_ptr;
97 jint pos = state->pos;
99 // Check if the trace buffer needs to be extended.
100 if (pos == state->length)
102 int newLength = state->length * 2;
103 void *newFrames = _Jv_AllocBytes (newLength * sizeof(_Jv_StackFrame));
104 memcpy (newFrames, state->frames, state->length * sizeof(_Jv_StackFrame));
105 state->frames = (_Jv_StackFrame *) newFrames;
106 state->length = newLength;
109 void *func_addr = (void *) _Unwind_GetRegionStart (context);
111 // If we see the interpreter's main function, "pop" an entry off the
112 // interpreter stack and use that instead, so that the trace goes through
113 // the java code and not the interpreter itself. This assumes a 1:1
114 // correspondance between call frames in the interpreted stack and occurances
115 // of _Jv_InterpMethod::run() on the native stack.
116 #ifdef INTERPRETER
117 void *interp_run = NULL;
119 if (::gnu::classpath::jdwp::Jdwp::isDebugging)
120 interp_run = (void *) &_Jv_InterpMethod::run_debug;
121 else
122 interp_run = (void *) &_Jv_InterpMethod::run;
124 if (func_addr == UNWRAP_FUNCTION_DESCRIPTOR (interp_run))
126 state->frames[pos].type = frame_interpreter;
127 state->frames[pos].interp.meth = state->interp_frame->self;
128 state->frames[pos].interp.pc = state->interp_frame->pc;
129 state->interp_frame = state->interp_frame->next;
131 else
132 #endif
134 #ifdef HAVE_GETIPINFO
135 _Unwind_Ptr ip;
136 int ip_before_insn = 0;
137 ip = _Unwind_GetIPInfo (context, &ip_before_insn);
139 // If the unwinder gave us a 'return' address, roll it back a little
140 // to ensure we get the correct line number for the call itself.
141 if (! ip_before_insn)
142 --ip;
143 #endif
144 state->frames[pos].type = frame_native;
145 #ifdef HAVE_GETIPINFO
146 state->frames[pos].ip = (void *) ip;
147 #else
148 state->frames[pos].ip = (void *) _Unwind_GetIP (context);
149 #endif
150 state->frames[pos].start_ip = func_addr;
153 _Unwind_Reason_Code result = _URC_NO_REASON;
154 if (state->trace_function != NULL)
155 result = (state->trace_function) (state);
156 state->pos++;
157 return result;
160 // Return a raw stack trace from the current point of execution. The raw
161 // trace will include all functions that have unwind info.
162 _Jv_StackTrace *
163 _Jv_StackTrace::GetStackTrace(void)
165 int trace_size = 100;
166 _Jv_StackFrame frames[trace_size];
167 _Jv_UnwindState state (trace_size);
168 state.frames = (_Jv_StackFrame *) &frames;
170 _Unwind_Backtrace (UnwindTraceFn, &state);
172 // Copy the trace and return it.
173 int traceSize = sizeof (_Jv_StackTrace) +
174 (sizeof (_Jv_StackFrame) * state.pos);
175 _Jv_StackTrace *trace = (_Jv_StackTrace *) _Jv_AllocBytes (traceSize);
176 trace->length = state.pos;
177 memcpy (trace->frames, state.frames, sizeof (_Jv_StackFrame) * state.pos);
178 return trace;
181 void
182 _Jv_StackTrace::getLineNumberForFrame(_Jv_StackFrame *frame, NameFinder *finder,
183 jstring *sourceFileName, jint *lineNum,
184 jstring *methodName)
186 #ifdef INTERPRETER
187 if (frame->type == frame_interpreter)
189 _Jv_InterpMethod *interp_meth = frame->interp.meth;
190 _Jv_InterpClass *interp_class =
191 (_Jv_InterpClass *) interp_meth->defining_class->aux_info;
192 *sourceFileName = interp_class->source_file_name;
193 // The interpreter advances the PC before executing an instruction,
194 // so roll-back 1 byte to ensure the line number is accurate.
195 *lineNum = interp_meth->get_source_line(frame->interp.pc - 1);
196 return;
198 #endif
200 // Use _Jv_platform_dladdr() to determine in which binary the address IP
201 // resides.
202 _Jv_AddrInfo info;
203 jstring binaryName = NULL;
204 const char *argv0 = _Jv_GetSafeArg(0);
206 void *ip = frame->ip;
207 _Unwind_Ptr offset = 0;
209 if (_Jv_platform_dladdr (ip, &info))
211 if (info.file_name)
212 binaryName = JvNewStringUTF (info.file_name);
213 else
214 return;
216 if (*methodName == NULL && info.sym_name)
217 *methodName = JvNewStringUTF (info.sym_name);
219 // addr2line expects relative addresses for shared libraries.
220 if (strcmp (info.file_name, argv0) == 0)
221 offset = (_Unwind_Ptr) ip;
222 else
223 offset = (_Unwind_Ptr) ip - (_Unwind_Ptr) info.base;
225 #ifndef HAVE_GETIPINFO
226 // The unwinder gives us the return address. In order to get the right
227 // line number for the stack trace, roll it back a little.
228 offset -= 1;
229 #endif
231 finder->lookup (binaryName, (jlong) offset);
232 *sourceFileName = finder->getSourceFile();
233 *lineNum = finder->getLineNum();
234 if (*lineNum == -1 && NameFinder::showRaw())
236 gnu::gcj::runtime::StringBuffer *t =
237 new gnu::gcj::runtime::StringBuffer(binaryName);
238 t->append ((jchar)' ');
239 t->append ((jchar)'[');
240 // + 1 to compensate for the - 1 adjustment above;
241 t->append (Long::toHexString (offset + 1));
242 t->append ((jchar)']');
243 *sourceFileName = t->toString();
248 // Look up class and method info for the given stack frame, setting
249 // frame->klass and frame->meth if they are known.
250 void
251 _Jv_StackTrace::FillInFrameInfo (_Jv_StackFrame *frame)
253 jclass klass = NULL;
254 _Jv_Method *meth = NULL;
256 if (frame->type == frame_native)
258 klass = _Jv_StackTrace::ClassForFrame (frame);
260 if (klass != NULL)
261 // Find method in class
262 for (int j = 0; j < klass->method_count; j++)
264 void *wncode = UNWRAP_FUNCTION_DESCRIPTOR (klass->methods[j].ncode);
265 if (wncode == frame->start_ip)
267 meth = &klass->methods[j];
268 break;
272 #ifdef INTERPRETER
273 else if (frame->type == frame_interpreter)
275 _Jv_InterpMethod *interp_meth = frame->interp.meth;
276 klass = interp_meth->defining_class;
277 meth = interp_meth->self;
279 #endif
280 else
281 JvFail ("Unknown frame type");
283 frame->klass = klass;
284 frame->meth = meth;
287 // Convert raw stack frames to a Java array of StackTraceElement objects.
288 JArray< ::java::lang::StackTraceElement *>*
289 _Jv_StackTrace::GetStackTraceElements (_Jv_StackTrace *trace,
290 Throwable *throwable __attribute__((unused)))
292 ArrayList *list = new ArrayList ();
294 #if defined (SJLJ_EXCEPTIONS) && ! defined (WIN32)
295 // We can't use the nCodeMap without unwinder support. Instead,
296 // fake the method name by giving the IP in hex - better than nothing.
297 jstring hex = JvNewStringUTF ("0x");
299 for (int i = 0; i < trace->length; i++)
301 jstring sourceFileName = NULL;
302 jint lineNum = -1;
303 _Jv_StackFrame *frame = &trace->frames[i];
305 jstring className = NULL;
306 jstring methodName = hex->concat (Long::toHexString ((jlong) frame->ip));
308 StackTraceElement *element = new StackTraceElement (sourceFileName,
309 lineNum, className, methodName, 0);
310 list->add (element);
313 #else /* SJLJ_EXCEPTIONS && !WIN32 */
315 //JvSynchronized (ncodeMap);
316 UpdateNCodeMap ();
318 NameFinder *finder = new NameFinder();
319 int start_idx = 0;
320 int end_idx = trace->length - 1;
322 // First pass: strip superfluous frames from beginning and end of the trace.
323 for (int i = 0; i < trace->length; i++)
325 _Jv_StackFrame *frame = &trace->frames[i];
326 FillInFrameInfo (frame);
328 if (!frame->klass || !frame->meth)
329 // Not a Java frame.
330 continue;
332 // Throw away the top of the stack till we see:
333 // - the constructor(s) of this Throwable, or
334 // - the Throwable.fillInStackTrace call.
335 if (frame->klass == throwable->getClass()
336 && strcmp (frame->meth->name->chars(), "<init>") == 0)
337 start_idx = i + 1;
339 if (frame->klass == &Throwable::class$
340 && strcmp (frame->meth->name->chars(), "fillInStackTrace") == 0)
341 start_idx = i + 1;
343 // End the trace at the application's main() method if we see call_main.
344 if (frame->klass == &gnu::java::lang::MainThread::class$
345 && strcmp (frame->meth->name->chars(), "call_main") == 0)
346 end_idx = i - 1;
349 const jboolean remove_unknown
350 = gnu::gcj::runtime::NameFinder::removeUnknown();
352 // Second pass: Look up line-number info for remaining frames.
353 for (int i = start_idx; i <= end_idx; i++)
355 _Jv_StackFrame *frame = &trace->frames[i];
357 if (frame->klass == NULL && remove_unknown)
358 // Not a Java frame.
359 continue;
361 jstring className = NULL;
362 if (frame->klass != NULL)
363 className = frame->klass->getName ();
365 jstring methodName = NULL;
366 if (frame->meth)
367 methodName = JvNewStringUTF (frame->meth->name->chars());
369 jstring sourceFileName = NULL;
370 jint lineNum = -1;
372 getLineNumberForFrame(frame, finder, &sourceFileName, &lineNum,
373 &methodName);
375 StackTraceElement *element = new StackTraceElement (sourceFileName, lineNum,
376 className, methodName, 0);
377 list->add (element);
380 finder->close();
381 #endif /* SJLJ_EXCEPTIONS && !WIN32 */
383 JArray<Object *> *array = JvNewObjectArray (list->size (),
384 &StackTraceElement::class$, NULL);
386 return (JArray<StackTraceElement *>*) list->toArray (array);
389 struct CallingClassTraceData
391 jclass checkClass;
392 jclass foundClass;
393 _Jv_Method *foundMeth;
394 bool seen_checkClass;
397 _Unwind_Reason_Code
398 _Jv_StackTrace::calling_class_trace_fn (_Jv_UnwindState *state)
400 CallingClassTraceData *trace_data = (CallingClassTraceData *)
401 state->trace_data;
402 _Jv_StackFrame *frame = &state->frames[state->pos];
403 FillInFrameInfo (frame);
405 if (trace_data->seen_checkClass
406 && frame->klass
407 && frame->klass != trace_data->checkClass)
409 trace_data->foundClass = frame->klass;
410 trace_data->foundMeth = frame->meth;
411 return _URC_NORMAL_STOP;
414 if (frame->klass == trace_data->checkClass)
415 trace_data->seen_checkClass = true;
417 return _URC_NO_REASON;
420 // Find the class immediately above the given class on the call stack. Any
421 // intermediate non-Java
422 // frames are ignored. If the calling class could not be determined (eg because
423 // the unwinder is not supported on this platform), NULL is returned.
424 // This function is used to implement calling-classloader checks and reflection
425 // accessibility checks.
426 // CHECKCLASS is typically the class calling GetCallingClass. The first class
427 // above CHECKCLASS on the call stack will be returned.
428 jclass
429 _Jv_StackTrace::GetCallingClass (jclass checkClass)
431 jclass result = NULL;
432 GetCallerInfo (checkClass, &result, NULL);
433 return result;
436 void
437 _Jv_StackTrace::GetCallerInfo (jclass checkClass, jclass *caller_class,
438 _Jv_Method **caller_meth)
440 int trace_size = 20;
441 _Jv_StackFrame frames[trace_size];
442 _Jv_UnwindState state (trace_size);
443 state.frames = (_Jv_StackFrame *) &frames;
445 CallingClassTraceData trace_data;
446 trace_data.checkClass = checkClass;
447 trace_data.seen_checkClass = false;
448 trace_data.foundClass = NULL;
449 trace_data.foundMeth = NULL;
451 state.trace_function = calling_class_trace_fn;
452 state.trace_data = (void *) &trace_data;
454 //JvSynchronized (ncodeMap);
455 UpdateNCodeMap ();
457 _Unwind_Backtrace (UnwindTraceFn, &state);
459 if (caller_class)
460 *caller_class = trace_data.foundClass;
461 if (caller_meth)
462 *caller_meth = trace_data.foundMeth;
465 // Return a java array containing the Java classes on the stack above CHECKCLASS.
466 JArray<jclass> *
467 _Jv_StackTrace::GetClassContext (jclass checkClass)
469 JArray<jclass> *result = NULL;
471 int trace_size = 100;
472 _Jv_StackFrame frames[trace_size];
473 _Jv_UnwindState state (trace_size);
474 state.frames = (_Jv_StackFrame *) &frames;
476 //JvSynchronized (ncodeMap);
477 UpdateNCodeMap ();
479 _Unwind_Backtrace (UnwindTraceFn, &state);
481 // Count the number of Java frames on the stack.
482 int jframe_count = 0;
483 bool seen_checkClass = false;
484 int start_pos = -1;
485 for (int i = 0; i < state.pos; i++)
487 _Jv_StackFrame *frame = &state.frames[i];
488 FillInFrameInfo (frame);
490 if (seen_checkClass)
492 if (frame->klass)
494 jframe_count++;
495 if (start_pos == -1)
496 start_pos = i;
499 else
500 seen_checkClass = frame->klass == checkClass;
502 result = (JArray<jclass> *) _Jv_NewObjectArray (jframe_count, &Class::class$, NULL);
503 int pos = 0;
505 for (int i = start_pos; i < state.pos; i++)
507 _Jv_StackFrame *frame = &state.frames[i];
508 if (frame->klass)
509 elements(result)[pos++] = frame->klass;
511 return result;
514 _Unwind_Reason_Code
515 _Jv_StackTrace::non_system_trace_fn (_Jv_UnwindState *state)
517 _Jv_StackFrame *frame = &state->frames[state->pos];
518 FillInFrameInfo (frame);
520 ClassLoader *classLoader = NULL;
522 if (frame->klass)
524 classLoader = frame->klass->getClassLoaderInternal();
525 #ifdef INTERPRETER
526 if (classLoader != NULL)
528 state->trace_data = (void *) classLoader;
529 return _URC_NORMAL_STOP;
531 #endif
534 return _URC_NO_REASON;
537 ClassLoader *
538 _Jv_StackTrace::GetFirstNonSystemClassLoader ()
540 int trace_size = 32;
541 _Jv_StackFrame frames[trace_size];
542 _Jv_UnwindState state (trace_size);
543 state.frames = (_Jv_StackFrame *) &frames;
544 state.trace_function = non_system_trace_fn;
545 state.trace_data = NULL;
547 //JvSynchronized (ncodeMap);
548 UpdateNCodeMap ();
550 _Unwind_Backtrace (UnwindTraceFn, &state);
552 if (state.trace_data)
553 return (ClassLoader *) state.trace_data;
555 return NULL;
558 struct AccessControlTraceData
560 jint length;
561 jboolean privileged;
564 _Unwind_Reason_Code
565 _Jv_StackTrace::accesscontrol_trace_fn (_Jv_UnwindState *state)
567 AccessControlTraceData *trace_data = (AccessControlTraceData *)
568 state->trace_data;
569 _Jv_StackFrame *frame = &state->frames[state->pos];
570 FillInFrameInfo (frame);
572 if (!(frame->klass && frame->meth))
573 return _URC_NO_REASON;
575 trace_data->length++;
577 // If the previous frame was a call to doPrivileged, then this is
578 // the last frame we look at.
579 if (trace_data->privileged)
580 return _URC_NORMAL_STOP;
582 if (frame->klass == &::java::security::AccessController::class$
583 && strcmp (frame->meth->name->chars(), "doPrivileged") == 0)
584 trace_data->privileged = true;
586 return _URC_NO_REASON;
589 jobjectArray
590 _Jv_StackTrace::GetAccessControlStack (void)
592 int trace_size = 100;
593 _Jv_StackFrame frames[trace_size];
594 _Jv_UnwindState state (trace_size);
595 state.frames = (_Jv_StackFrame *) &frames;
597 AccessControlTraceData trace_data;
598 trace_data.length = 0;
599 trace_data.privileged = false;
601 state.trace_function = accesscontrol_trace_fn;
602 state.trace_data = (void *) &trace_data;
604 UpdateNCodeMap();
605 _Unwind_Backtrace (UnwindTraceFn, &state);
607 JArray<jclass> *classes = (JArray<jclass> *)
608 _Jv_NewObjectArray (trace_data.length, &::java::lang::Class::class$, NULL);
609 jclass *c = elements (classes);
611 for (int i = 0, j = 0; i < state.pos; i++)
613 _Jv_StackFrame *frame = &state.frames[i];
614 if (!frame->klass || !frame->meth)
615 continue;
616 c[j] = frame->klass;
617 j++;
620 jobjectArray result =
621 (jobjectArray) _Jv_NewObjectArray (2, &::java::lang::Object::class$,
622 NULL);
623 jobject *r = elements (result);
624 r[0] = (jobject) classes;
625 r[1] = (jobject) new Boolean (trace_data.privileged);
627 return result;