OSDN Git Service

* java/lang/natClassLoader.cc (_Jv_NewClass): Use
[pf3gnuchains/gcc-fork.git] / libjava / stacktrace.cc
1 // stacktrace.cc - Functions for unwinding & inspecting the call stack.
2
3 /* Copyright (C) 2005  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
13 #include <jvm.h>
14 #include <gcj/cni.h>
15 #include <java-interp.h>
16 #include <java-stack.h>
17
18 #ifdef HAVE_DLFCN_H
19 #include <dlfcn.h>
20 #endif
21
22 #include <stdio.h>
23
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>
30
31 #include <sysdep/backtrace.h>
32
33 using namespace java::lang;
34 using namespace java::lang::reflect;
35 using namespace java::util;
36 using namespace gnu::gcj::runtime;
37
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;
43
44 // Check the "class stack" for any classes initialized since we were last 
45 // called, and add them to ncodeMap.
46 void 
47 _Jv_StackTrace::UpdateNCodeMap ()
48 {
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 
51   // enough.
52   if (ncodeMap == NULL)
53     ncodeMap = new java::util::IdentityHashMap (5087);
54   
55   jclass klass;
56   while ((klass = _Jv_PopClass ()))
57     {
58       //printf ("got %s\n", klass->name->data);
59 #ifdef INTERPRETER
60       JvAssert (! _Jv_IsInterpretedClass (klass));
61 #endif
62       for (int i=0; i < klass->method_count; i++)
63         {
64           _Jv_Method *method = &klass->methods[i];
65           // Add non-abstract methods to ncodeMap.
66           if (method->ncode)
67             {
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);
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   // use _Unwind_FindEnclosingFunction to find start of method
86   //void *entryPoint = _Unwind_FindEnclosingFunction (ip);
87
88   // look it up in ncodeMap
89   if (frame->start_ip)
90     klass = (jclass) ncodeMap->get ((jobject) frame->start_ip);
91
92   return klass;
93 }
94
95 _Unwind_Reason_Code
96 _Jv_StackTrace::UnwindTraceFn (struct _Unwind_Context *context, void *state_ptr)
97 {
98   _Jv_UnwindState *state = (_Jv_UnwindState *) state_ptr;
99   jint pos = state->pos;
100
101   // Check if the trace buffer needs to be extended.
102   if (pos == state->length)
103     {
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;
109     }
110   
111   _Unwind_Ptr func_addr = _Unwind_GetRegionStart (context);
112   
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.
118 #ifdef INTERPRETER
119   if (func_addr == (_Unwind_Ptr) &_Jv_InterpMethod::run)
120     {
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;
125     }
126   else
127 #endif
128     {
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;
132     }
133
134   //printf ("unwind ip: %p\n", _Unwind_GetIP (context));
135
136   _Unwind_Reason_Code result = _URC_NO_REASON;
137   if (state->trace_function != NULL)
138     result = (state->trace_function) (state);
139   state->pos++;
140   return result;
141 }
142
143 // Return a raw stack trace from the current point of execution. The raw 
144 // trace will include all functions that have unwind info.
145 _Jv_StackTrace *
146 _Jv_StackTrace::GetStackTrace(void)
147 {
148   int trace_size = 100;
149   _Jv_StackFrame frames[trace_size];
150   _Jv_UnwindState state (trace_size);
151   state.frames = (_Jv_StackFrame *) &frames;
152
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 */
160   
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);  
167   return trace;
168 }
169
170 void
171 _Jv_StackTrace::getLineNumberForFrame(_Jv_StackFrame *frame, NameFinder *finder, 
172                  jstring *sourceFileName, jint *lineNum)
173 {
174 #ifdef INTERPRETER
175   if (frame->type == frame_interpreter)
176     {
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);
182       return;
183     }
184 #endif
185   // Use dladdr() to determine in which binary the address IP resides.
186 #if defined (HAVE_DLFCN_H) && defined (HAVE_DLADDR)
187   Dl_info info;
188   jstring binaryName = NULL;
189   const char *argv0 = _Jv_GetSafeArg(0);
190
191   void *ip = frame->ip;
192   _Unwind_Ptr offset = 0;
193
194   if (dladdr (ip, &info))
195     {
196       if (info.dli_fname)
197         binaryName = JvNewStringUTF (info.dli_fname);
198       else
199         return;
200
201       // addr2line expects relative addresses for shared libraries.
202       if (strcmp (info.dli_fname, argv0) == 0)
203         offset = (_Unwind_Ptr) ip;
204       else
205         offset = (_Unwind_Ptr) ip - (_Unwind_Ptr) info.dli_fbase;
206
207       //printf ("linenum ip: %p\n", ip);
208       //printf ("%s: 0x%x\n", info.dli_fname, offset);
209       //offset -= sizeof(void *);
210       
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.
213       offset -= 1;
214
215       // printf ("%s: 0x%x\n", info.dli_fname, offset);
216       
217       finder->lookup (binaryName, (jlong) offset);
218       *sourceFileName = finder->getSourceFile();
219       *lineNum = finder->getLineNum();
220     }
221 #endif
222 }
223
224 // Look up class and method info for the given stack frame, setting 
225 // frame->klass and frame->meth if they are known.
226 void
227 _Jv_StackTrace::FillInFrameInfo (_Jv_StackFrame *frame)
228 {
229   jclass klass = NULL;
230   _Jv_Method *meth = NULL;
231   
232   if (frame->type == frame_native)
233     {
234       klass = _Jv_StackTrace::ClassForFrame (frame);
235
236       if (klass != NULL)
237         // Find method in class
238         for (int j = 0; j < klass->method_count; j++)
239           {
240             if (klass->methods[j].ncode == frame->start_ip)
241               {
242                 meth = &klass->methods[j];
243                 break;
244               }
245           }
246     }
247 #ifdef INTERPRETER
248   else if (frame->type == frame_interpreter)
249     {
250       _Jv_InterpMethod *interp_meth = frame->interp.meth;
251       klass = interp_meth->defining_class;
252       meth = interp_meth->self;
253     }
254 #endif
255   else
256     JvFail ("Unknown frame type");
257   
258   frame->klass = klass;
259   frame->meth = meth;
260 }
261
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)))
266 {
267   ArrayList *list = new ArrayList ();
268
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");
273
274   for (int i = 0; i < trace->length; i++)
275     {
276       jstring sourceFileName = NULL;
277       jint lineNum = -1;
278       _Jv_StackFrame *frame = &trace->frames[i];
279       
280       jstring className = NULL;
281       jstring methodName = hex->concat (Long::toHexString ((jlong) frame->ip));
282
283       StackTraceElement *element = new StackTraceElement (sourceFileName, 
284         lineNum, className, methodName, 0);    
285       list->add (element);
286     }
287
288 #else /* SJLJ_EXCEPTIONS */
289
290   //JvSynchronized (ncodeMap);
291   UpdateNCodeMap ();
292
293   NameFinder *finder = new NameFinder();
294   int start_idx = 0;
295   int end_idx = trace->length - 1;
296
297   // First pass: strip superfluous frames from beginning and end of the trace.  
298   for (int i = 0; i < trace->length; i++)
299     {
300       _Jv_StackFrame *frame = &trace->frames[i];
301       FillInFrameInfo (frame);
302
303       if (!frame->klass || !frame->meth)
304         // Not a Java frame.
305         continue;
306
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)
312         start_idx = i + 1;
313
314       if (frame->klass == &Throwable::class$
315           && strcmp (frame->meth->name->chars(), "fillInStackTrace") == 0)
316         start_idx = i + 1;
317
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)
321         end_idx = i - 1;
322     }
323   
324   // Second pass: Look up line-number info for remaining frames.
325   for (int i = start_idx; i <= end_idx; i++)
326     {
327       _Jv_StackFrame *frame = &trace->frames[i];
328       
329       if (frame->klass == NULL)
330         // Not a Java frame.
331         continue;
332       
333       jstring className = frame->klass->getName ();
334       jstring methodName = NULL;
335       if (frame->meth)
336         methodName = JvNewStringUTF (frame->meth->name->chars());
337       
338       jstring sourceFileName = NULL;
339       jint lineNum = -1;
340       
341       getLineNumberForFrame(frame, finder, &sourceFileName, &lineNum);
342       
343       StackTraceElement *element = new StackTraceElement (sourceFileName, lineNum,
344         className, methodName, 0);
345       list->add (element);
346     }
347   
348   finder->close();
349 #endif /* SJLJ_EXCEPTIONS */
350
351   JArray<Object *> *array = JvNewObjectArray (list->size (), 
352     &StackTraceElement::class$, NULL);
353
354   return (JArray<StackTraceElement *>*) list->toArray (array);
355 }
356
357 struct CallingClassTraceData
358 {
359   jclass checkClass;    
360   jclass foundClass;
361   _Jv_Method *foundMeth;
362   bool seen_checkClass;
363 };
364
365 _Unwind_Reason_Code
366 _Jv_StackTrace::calling_class_trace_fn (_Jv_UnwindState *state)
367 {
368   CallingClassTraceData *trace_data = (CallingClassTraceData *)
369     state->trace_data;
370   _Jv_StackFrame *frame = &state->frames[state->pos];
371   FillInFrameInfo (frame);
372
373   if (trace_data->seen_checkClass
374       && frame->klass
375       && frame->klass != trace_data->checkClass)
376     {
377       trace_data->foundClass = frame->klass;
378       trace_data->foundMeth = frame->meth;
379       return _URC_NORMAL_STOP;
380     }
381   
382   if (frame->klass == trace_data->checkClass)
383     trace_data->seen_checkClass = true;
384     
385   return _URC_NO_REASON;
386 }
387
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.
396 jclass
397 _Jv_StackTrace::GetCallingClass (jclass checkClass)
398 {
399   jclass result = NULL;
400   GetCallerInfo (checkClass, &result, NULL);
401   return result;
402 }
403
404 void
405 _Jv_StackTrace::GetCallerInfo (jclass checkClass, jclass *caller_class,
406   _Jv_Method **caller_meth)
407 {
408 #ifndef SJLJ_EXCEPTIONS
409   int trace_size = 20;
410   _Jv_StackFrame frames[trace_size];
411   _Jv_UnwindState state (trace_size);
412   state.frames = (_Jv_StackFrame *) &frames;
413
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;
419
420   state.trace_function = calling_class_trace_fn;
421   state.trace_data = (void *) &trace_data;
422
423   //JvSynchronized (ncodeMap);
424   UpdateNCodeMap ();
425
426   _Unwind_Backtrace (UnwindTraceFn, &state);
427   
428   if (caller_class)
429     *caller_class = trace_data.foundClass;
430   if (caller_meth)
431     *caller_meth = trace_data.foundMeth;
432 #else
433   return;
434 #endif
435 }
436
437 // Return a java array containing the Java classes on the stack above CHECKCLASS.
438 JArray<jclass> *
439 _Jv_StackTrace::GetClassContext (jclass checkClass)
440 {
441   JArray<jclass> *result = NULL;
442
443   int trace_size = 100;
444   _Jv_StackFrame frames[trace_size];
445   _Jv_UnwindState state (trace_size);
446   state.frames = (_Jv_StackFrame *) &frames;
447
448   //JvSynchronized (ncodeMap);
449   UpdateNCodeMap ();
450
451   _Unwind_Backtrace (UnwindTraceFn, &state);
452
453   // Count the number of Java frames on the stack.
454   int jframe_count = 0;
455   bool seen_checkClass = false;
456   int start_pos = -1;
457   for (int i = 0; i < state.pos; i++)
458     {
459       _Jv_StackFrame *frame = &state.frames[i];
460       FillInFrameInfo (frame);
461       
462       if (seen_checkClass)
463         {
464           if (frame->klass)
465             {
466               jframe_count++;
467               if (start_pos == -1)
468                 start_pos = i;
469             }
470         }
471       else
472         seen_checkClass = frame->klass == checkClass;
473     }
474   result = (JArray<jclass> *) _Jv_NewObjectArray (jframe_count, &Class::class$, NULL);
475   int pos = 0;
476   
477   for (int i = start_pos; i < state.pos; i++)
478     {
479       _Jv_StackFrame *frame = &state.frames[i];
480       if (frame->klass)
481         elements(result)[pos++] = frame->klass;
482     }
483   return result;
484 }
485
486 _Unwind_Reason_Code
487 _Jv_StackTrace::non_system_trace_fn (_Jv_UnwindState *state)
488 {
489   _Jv_StackFrame *frame = &state->frames[state->pos];
490   FillInFrameInfo (frame);
491   
492   ClassLoader *classLoader = NULL;
493
494   if (frame->klass)
495     {
496       classLoader = frame->klass->getClassLoaderInternal();
497 #ifdef INTERPRETER
498       if (classLoader != NULL)
499         {
500           state->trace_data = (void *) classLoader;
501           return _URC_NORMAL_STOP;
502         }
503 #endif
504     }
505
506   return _URC_NO_REASON;
507 }
508
509 ClassLoader *
510 _Jv_StackTrace::GetFirstNonSystemClassLoader ()
511 {
512   int trace_size = 32;
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;
518
519   //JvSynchronized (ncodeMap);
520   UpdateNCodeMap ();
521   
522   _Unwind_Backtrace (UnwindTraceFn, &state);
523
524   if (state.trace_data)
525     return (ClassLoader *) state.trace_data;
526   
527   return NULL;
528 }