OSDN Git Service

2006-10-03 Gary Benson <gbenson@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / stacktrace.cc
1 // stacktrace.cc - Functions for unwinding & inspecting the call stack.
2
3 /* Copyright (C) 2005, 2006  Free Software Foundation
4
5    This file is part of libgcj.
6
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
9 details.  */
10
11 #include <config.h>
12 #include <platform.h>
13
14 #include <jvm.h>
15 #include <gcj/cni.h>
16 #include <java-interp.h>
17 #include <java-stack.h>
18
19 #include <stdio.h>
20
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>
31
32 #include <sysdep/backtrace.h>
33 #include <sysdep/descriptor.h>
34
35 using namespace java::lang;
36 using namespace java::lang::reflect;
37 using namespace java::util;
38 using namespace gnu::gcj::runtime;
39
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;
45
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 ()
50 {
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);
56   
57   jclass klass;
58   while ((klass = _Jv_PopClass ()))
59     if (!_Jv_IsInterpretedClass (klass))
60       {
61         //printf ("got %s\n", klass->name->data);
62         for (int i = 0; i < klass->method_count; i++)
63           {
64             _Jv_Method *method = &klass->methods[i];
65             void *ncode = method->ncode;
66             // Add non-abstract methods to ncodeMap.
67             if (ncode)
68               {
69                 ncode = UNWRAP_FUNCTION_DESCRIPTOR (ncode);
70                 ncodeMap->put ((java::lang::Object *) ncode, klass);
71               }
72           }
73       }
74 }
75
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)
82 {
83   JvAssert (frame->type == frame_native);
84   jclass klass = NULL;
85
86   // look it up in ncodeMap
87   if (frame->start_ip)
88     klass = (jclass) ncodeMap->get ((jobject) frame->start_ip);
89
90   return klass;
91 }
92
93 _Unwind_Reason_Code
94 _Jv_StackTrace::UnwindTraceFn (struct _Unwind_Context *context, void *state_ptr)
95 {
96   _Jv_UnwindState *state = (_Jv_UnwindState *) state_ptr;
97   jint pos = state->pos;
98
99   // Check if the trace buffer needs to be extended.
100   if (pos == state->length)
101     {
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;
107     }
108
109   void *func_addr = (void *) _Unwind_GetRegionStart (context);
110
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;
118   
119   if (::gnu::classpath::jdwp::Jdwp::isDebugging)
120         interp_run = (void *) &_Jv_InterpMethod::run_debug;
121   else
122     interp_run = (void *) &_Jv_InterpMethod::run;
123         
124   if (func_addr == UNWRAP_FUNCTION_DESCRIPTOR (interp_run))
125     {
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;
130     }
131   else
132 #endif
133     {
134 #ifdef HAVE_GETIPINFO
135       _Unwind_Ptr ip;
136       int ip_before_insn = 0;
137       ip = _Unwind_GetIPInfo (context, &ip_before_insn);
138
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;
151     }
152
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;
158 }
159
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)
164 {
165   int trace_size = 100;
166   _Jv_StackFrame frames[trace_size];
167   _Jv_UnwindState state (trace_size);
168   state.frames = (_Jv_StackFrame *) &frames;
169
170   _Unwind_Backtrace (UnwindTraceFn, &state);
171   
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;
179 }
180
181 void
182 _Jv_StackTrace::getLineNumberForFrame(_Jv_StackFrame *frame, NameFinder *finder, 
183                                       jstring *sourceFileName, jint *lineNum,
184                                       jstring *methodName)
185 {
186 #ifdef INTERPRETER
187   if (frame->type == frame_interpreter)
188     {
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;
197     }
198 #endif
199
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);
205
206   void *ip = frame->ip;
207   _Unwind_Ptr offset = 0;
208
209   if (_Jv_platform_dladdr (ip, &info))
210     {
211       if (info.file_name)
212         binaryName = JvNewStringUTF (info.file_name);
213       else
214         return;
215
216       if (*methodName == NULL && info.sym_name)
217         *methodName = JvNewStringUTF (info.sym_name);
218
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;
224
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
230
231       finder->lookup (binaryName, (jlong) offset);
232       *sourceFileName = finder->getSourceFile();
233       *lineNum = finder->getLineNum();
234       if (*lineNum == -1 && NameFinder::showRaw())
235         {
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();
244         }
245     }
246 }
247
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)
252 {
253   jclass klass = NULL;
254   _Jv_Method *meth = NULL;
255   
256   if (frame->type == frame_native)
257     {
258       klass = _Jv_StackTrace::ClassForFrame (frame);
259
260       if (klass != NULL)
261         // Find method in class
262         for (int j = 0; j < klass->method_count; j++)
263           {
264             void *wncode = UNWRAP_FUNCTION_DESCRIPTOR (klass->methods[j].ncode);
265             if (wncode == frame->start_ip)
266               {
267                 meth = &klass->methods[j];
268                 break;
269               }
270           }
271     }
272 #ifdef INTERPRETER
273   else if (frame->type == frame_interpreter)
274     {
275       _Jv_InterpMethod *interp_meth = frame->interp.meth;
276       klass = interp_meth->defining_class;
277       meth = interp_meth->self;
278     }
279 #endif
280   else
281     JvFail ("Unknown frame type");
282   
283   frame->klass = klass;
284   frame->meth = meth;
285 }
286
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)))
291 {
292   ArrayList *list = new ArrayList ();
293
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");
298
299   for (int i = 0; i < trace->length; i++)
300     {
301       jstring sourceFileName = NULL;
302       jint lineNum = -1;
303       _Jv_StackFrame *frame = &trace->frames[i];
304       
305       jstring className = NULL;
306       jstring methodName = hex->concat (Long::toHexString ((jlong) frame->ip));
307
308       StackTraceElement *element = new StackTraceElement (sourceFileName, 
309         lineNum, className, methodName, 0);    
310       list->add (element);
311     }
312
313 #else /* SJLJ_EXCEPTIONS && !WIN32 */
314
315   //JvSynchronized (ncodeMap);
316   UpdateNCodeMap ();
317
318   NameFinder *finder = new NameFinder();
319   int start_idx = 0;
320   int end_idx = trace->length - 1;
321
322   // First pass: strip superfluous frames from beginning and end of the trace.  
323   for (int i = 0; i < trace->length; i++)
324     {
325       _Jv_StackFrame *frame = &trace->frames[i];
326       FillInFrameInfo (frame);
327
328       if (!frame->klass || !frame->meth)
329         // Not a Java frame.
330         continue;
331
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;
338
339       if (frame->klass == &Throwable::class$
340           && strcmp (frame->meth->name->chars(), "fillInStackTrace") == 0)
341         start_idx = i + 1;
342
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;
347     }
348   
349   const jboolean remove_unknown 
350     = gnu::gcj::runtime::NameFinder::removeUnknown();
351
352   // Second pass: Look up line-number info for remaining frames.
353   for (int i = start_idx; i <= end_idx; i++)
354     {
355       _Jv_StackFrame *frame = &trace->frames[i];
356       
357       if (frame->klass == NULL && remove_unknown)
358         // Not a Java frame.
359         continue;
360
361       jstring className = NULL;
362       if (frame->klass != NULL)
363         className = frame->klass->getName ();
364
365       jstring methodName = NULL;
366       if (frame->meth)
367         methodName = JvNewStringUTF (frame->meth->name->chars());
368       
369       jstring sourceFileName = NULL;
370       jint lineNum = -1;
371       
372       getLineNumberForFrame(frame, finder, &sourceFileName, &lineNum, 
373                             &methodName);
374       
375       StackTraceElement *element = new StackTraceElement (sourceFileName, lineNum,
376         className, methodName, 0);
377       list->add (element);
378     }
379   
380   finder->close();
381 #endif /* SJLJ_EXCEPTIONS && !WIN32 */
382
383   JArray<Object *> *array = JvNewObjectArray (list->size (), 
384     &StackTraceElement::class$, NULL);
385
386   return (JArray<StackTraceElement *>*) list->toArray (array);
387 }
388
389 struct CallingClassTraceData
390 {
391   jclass checkClass;    
392   jclass foundClass;
393   _Jv_Method *foundMeth;
394   bool seen_checkClass;
395 };
396
397 _Unwind_Reason_Code
398 _Jv_StackTrace::calling_class_trace_fn (_Jv_UnwindState *state)
399 {
400   CallingClassTraceData *trace_data = (CallingClassTraceData *)
401     state->trace_data;
402   _Jv_StackFrame *frame = &state->frames[state->pos];
403   FillInFrameInfo (frame);
404
405   if (trace_data->seen_checkClass
406       && frame->klass
407       && frame->klass != trace_data->checkClass)
408     {
409       trace_data->foundClass = frame->klass;
410       trace_data->foundMeth = frame->meth;
411       return _URC_NORMAL_STOP;
412     }
413   
414   if (frame->klass == trace_data->checkClass)
415     trace_data->seen_checkClass = true;
416     
417   return _URC_NO_REASON;
418 }
419
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)
430 {
431   jclass result = NULL;
432   GetCallerInfo (checkClass, &result, NULL);
433   return result;
434 }
435
436 void
437 _Jv_StackTrace::GetCallerInfo (jclass checkClass, jclass *caller_class,
438   _Jv_Method **caller_meth)
439 {
440   int trace_size = 20;
441   _Jv_StackFrame frames[trace_size];
442   _Jv_UnwindState state (trace_size);
443   state.frames = (_Jv_StackFrame *) &frames;
444
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;
450
451   state.trace_function = calling_class_trace_fn;
452   state.trace_data = (void *) &trace_data;
453
454   //JvSynchronized (ncodeMap);
455   UpdateNCodeMap ();
456
457   _Unwind_Backtrace (UnwindTraceFn, &state);
458   
459   if (caller_class)
460     *caller_class = trace_data.foundClass;
461   if (caller_meth)
462     *caller_meth = trace_data.foundMeth;
463 }
464
465 // Return a java array containing the Java classes on the stack above CHECKCLASS.
466 JArray<jclass> *
467 _Jv_StackTrace::GetClassContext (jclass checkClass)
468 {
469   JArray<jclass> *result = NULL;
470
471   int trace_size = 100;
472   _Jv_StackFrame frames[trace_size];
473   _Jv_UnwindState state (trace_size);
474   state.frames = (_Jv_StackFrame *) &frames;
475
476   //JvSynchronized (ncodeMap);
477   UpdateNCodeMap ();
478
479   _Unwind_Backtrace (UnwindTraceFn, &state);
480
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++)
486     {
487       _Jv_StackFrame *frame = &state.frames[i];
488       FillInFrameInfo (frame);
489       
490       if (seen_checkClass)
491         {
492           if (frame->klass)
493             {
494               jframe_count++;
495               if (start_pos == -1)
496                 start_pos = i;
497             }
498         }
499       else
500         seen_checkClass = frame->klass == checkClass;
501     }
502   result = (JArray<jclass> *) _Jv_NewObjectArray (jframe_count, &Class::class$, NULL);
503   int pos = 0;
504   
505   for (int i = start_pos; i < state.pos; i++)
506     {
507       _Jv_StackFrame *frame = &state.frames[i];
508       if (frame->klass)
509         elements(result)[pos++] = frame->klass;
510     }
511   return result;
512 }
513
514 _Unwind_Reason_Code
515 _Jv_StackTrace::non_system_trace_fn (_Jv_UnwindState *state)
516 {
517   _Jv_StackFrame *frame = &state->frames[state->pos];
518   FillInFrameInfo (frame);
519   
520   ClassLoader *classLoader = NULL;
521
522   if (frame->klass)
523     {
524       classLoader = frame->klass->getClassLoaderInternal();
525 #ifdef INTERPRETER
526       if (classLoader != NULL)
527         {
528           state->trace_data = (void *) classLoader;
529           return _URC_NORMAL_STOP;
530         }
531 #endif
532     }
533
534   return _URC_NO_REASON;
535 }
536
537 ClassLoader *
538 _Jv_StackTrace::GetFirstNonSystemClassLoader ()
539 {
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;
546
547   //JvSynchronized (ncodeMap);
548   UpdateNCodeMap ();
549   
550   _Unwind_Backtrace (UnwindTraceFn, &state);
551
552   if (state.trace_data)
553     return (ClassLoader *) state.trace_data;
554   
555   return NULL;
556 }
557
558 struct AccessControlTraceData
559 {
560   jint length;
561   jboolean privileged;
562 };
563
564 _Unwind_Reason_Code
565 _Jv_StackTrace::accesscontrol_trace_fn (_Jv_UnwindState *state)
566 {
567   AccessControlTraceData *trace_data = (AccessControlTraceData *)
568     state->trace_data;
569   _Jv_StackFrame *frame = &state->frames[state->pos];
570   FillInFrameInfo (frame);
571
572   if (!(frame->klass && frame->meth))
573     return _URC_NO_REASON;
574
575   trace_data->length++;
576
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;
581   
582   if (frame->klass == &::java::security::AccessController::class$
583       && strcmp (frame->meth->name->chars(), "doPrivileged") == 0)
584     trace_data->privileged = true;
585
586   return _URC_NO_REASON;
587 }
588
589 jobjectArray
590 _Jv_StackTrace::GetAccessControlStack (void)
591 {
592   int trace_size = 100;
593   _Jv_StackFrame frames[trace_size];
594   _Jv_UnwindState state (trace_size);
595   state.frames = (_Jv_StackFrame *) &frames;
596
597   AccessControlTraceData trace_data;
598   trace_data.length = 0;
599   trace_data.privileged = false;
600   
601   state.trace_function = accesscontrol_trace_fn;
602   state.trace_data = (void *) &trace_data;
603
604   UpdateNCodeMap();
605   _Unwind_Backtrace (UnwindTraceFn, &state);
606
607   JArray<jclass> *classes = (JArray<jclass> *)
608     _Jv_NewObjectArray (trace_data.length, &::java::lang::Class::class$, NULL);
609   jclass *c = elements (classes);
610
611   for (int i = 0, j = 0; i < state.pos; i++)
612     {
613       _Jv_StackFrame *frame = &state.frames[i];
614       if (!frame->klass || !frame->meth)
615         continue;
616       c[j] = frame->klass;
617       j++;
618     }
619
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);
626   
627   return result;
628 }