PR149: Used wrong class loader.
[cacao.git] / src / vm / jit / stacktrace.cpp
index 37c72d05d2bf3f56fe3561e1e72c743ab4213029..c59d05ac1bf7299add8422432dbd1cc7041da8ba 100644 (file)
@@ -1,7 +1,8 @@
 /* src/vm/jit/stacktrace.cpp - machine independent stacktrace system
 
-   Copyright (C) 1996-2005, 2006, 2007, 2008
+   Copyright (C) 1996-2011
    CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+   Copyright (C) 2009 Theobroma Systems Ltd.
 
    This file is part of CACAO.
 
 
 #include "vm/types.h"
 
+#include "arch.h"
 #include "md.h"
 
 #include "mm/gc.hpp"
-#include "mm/memory.h"
+#include "mm/memory.hpp"
 
 #include "vm/jit/stacktrace.hpp"
 
 
 #include "threads/thread.hpp"
 
-#include "toolbox/logging.h"
+#include "toolbox/logging.hpp"
 
-#include "vm/array.h"
+#include "vm/array.hpp"
 #include "vm/jit/builtin.hpp"
-#include "vm/class.h"
+#include "vm/class.hpp"
 #include "vm/cycles-stats.h"
 #include "vm/exceptions.hpp"
 #include "vm/globals.hpp"
 #include "vm/javaobjects.hpp"
 #include "vm/loader.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
 #include "vm/options.h"
 #include "vm/string.hpp"
 #include "vm/vm.hpp"
@@ -137,7 +139,7 @@ void stacktrace_stackframeinfo_add(stackframeinfo_t* sfi, void* pv, void* sp, vo
                /* On S390 we use REG_RA as REG_ITMP3, so we have always to get
                   the RA from stack. */
 
-               framesize = *((u4 *) (((uintptr_t) pv) + FrameSize));
+               framesize = md_stacktrace_get_framesize(code);
 
                ra = md_stacktrace_get_returnaddress(sp, framesize);
 # else
@@ -148,7 +150,7 @@ void stacktrace_stackframeinfo_add(stackframeinfo_t* sfi, void* pv, void* sp, vo
                   the asm_vm_call_method special case. */
 
                if ((code == NULL) || !code_is_leafmethod(code)) {
-                       framesize = *((u4 *) (((uintptr_t) pv) + FrameSize));
+                       framesize = md_stacktrace_get_framesize(code);
 
                        ra = md_stacktrace_get_returnaddress(sp, framesize);
                }
@@ -305,7 +307,7 @@ static inline void stacktrace_stackframeinfo_next(stackframeinfo_t *tmpsfi)
  
        /* Get the current stack frame size. */
 
-       framesize = *((uint32_t *) (((intptr_t) pv) + FrameSize));
+       framesize = md_stacktrace_get_framesize(code);
 
        /* Get the RA of the current stack frame (RA to the parent Java
           method) if the current method is a non-leaf method.  Otherwise
@@ -356,7 +358,7 @@ static inline void stacktrace_stackframeinfo_next(stackframeinfo_t *tmpsfi)
        else
 #endif
                {
-#if defined(__I386__) || defined (__X86_64__) || defined (__M68K__)
+#if STACKFRMAE_RA_BETWEEN_FRAMES
                        sp = (void *) (((intptr_t) sp) + framesize + SIZEOF_VOID_P);
 #elif defined(__SPARC_64__)
                        /* already has the new sp */
@@ -516,15 +518,14 @@ static int stacktrace_depth(stackframeinfo_t *sfi)
 
 java_handle_bytearray_t *stacktrace_get(stackframeinfo_t *sfi)
 {
-       stackframeinfo_t         tmpsfi;
-       int                      depth;
-       java_handle_bytearray_t *ba;
-       int32_t                  ba_size;
-       stacktrace_t            *st;
-       stacktrace_entry_t      *ste;
-       methodinfo              *m;
-       bool                     skip_fillInStackTrace;
-       bool                     skip_init;
+       stackframeinfo_t    tmpsfi;
+       int                 depth;
+       int32_t             ba_size;
+       stacktrace_t       *st;
+       stacktrace_entry_t *ste;
+       methodinfo         *m;
+       bool                skip_fillInStackTrace;
+       bool                skip_init;
 
        CYCLES_STATS_DECLARE_AND_START_WITH_OVERHEAD
 
@@ -550,9 +551,9 @@ java_handle_bytearray_t *stacktrace_get(stackframeinfo_t *sfi)
 
        ba_size = sizeof(stacktrace_t) + sizeof(stacktrace_entry_t) * depth;
 
-       ba = builtin_newarray_byte(ba_size);
+       ByteArray ba(ba_size);
 
-       if (ba == NULL)
+       if (ba.is_null())
                goto return_NULL;
 
        /* Get a stacktrace entry pointer. */
@@ -561,7 +562,7 @@ java_handle_bytearray_t *stacktrace_get(stackframeinfo_t *sfi)
 
        LLNI_CRITICAL_START;
 
-       st = (stacktrace_t *) LLNI_array_data(ba);
+       st = (stacktrace_t *) ba.get_raw_data_ptr();
 
        ste = st->entries;
 
@@ -639,7 +640,7 @@ java_handle_bytearray_t *stacktrace_get(stackframeinfo_t *sfi)
 
        CYCLES_STATS_END_WITH_OVERHEAD(stacktrace_fillInStackTrace,
                                                                   stacktrace_overhead)
-       return ba;
+       return ba.get_handle();
 
 return_NULL:
 /*     dump_release(dumpsize); */
@@ -674,6 +675,129 @@ java_handle_bytearray_t *stacktrace_get_current(void)
 }
 
 
+/**
+ * Creates a java.lang.StackTraceElement for one element of the given
+ * stacktrace.
+ *
+ * @param st Given stacktrace.
+ * @param index Index of element inside stacktrace.
+ * @return The filled StackTraceElement object.
+ */
+#if defined(ENABLE_JAVASE)
+java_handle_t* stacktrace_get_StackTraceElement(stacktrace_t* st, int32_t index)
+{
+       assert(st != NULL);
+
+       if ((index < 0) || (index >= st->length)) {
+               /* XXX This should be an IndexOutOfBoundsException (check this
+                  again). */
+               exceptions_throw_arrayindexoutofboundsexception();
+               return NULL;
+       }
+
+       // Get the stacktrace entry.
+       stacktrace_entry_t* ste = &(st->entries[index]);
+
+       // Get the codeinfo, methodinfo and classinfo.
+       codeinfo*   code = ste->code;
+       methodinfo* m    = code->m;
+       classinfo*  c    = m->clazz;
+
+       // Get filename.
+       java_handle_t* filename;
+
+       if (!(m->flags & ACC_NATIVE)) {
+               if (c->sourcefile != NULL)
+                       filename = javastring_new(c->sourcefile);
+               else
+                       filename = NULL;
+       }
+       else
+               filename = NULL;
+
+       // Get line number.
+       int32_t linenumber;
+
+       if (m->flags & ACC_NATIVE) {
+#if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
+               linenumber = -1;
+#elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
+               linenumber = -2;
+#else
+# error unknown classpath configuration
+#endif
+       }
+       else {
+               // FIXME linenumbertable->find could change the methodinfo
+               // pointer when hitting an inlined method.
+               linenumber = code->linenumbertable->find(&m, ste->pc);
+               linenumber = (linenumber == 0) ? -1 : linenumber;
+       }
+
+       // Get declaring class name.
+       java_handle_t* declaringclass = class_get_classname(c);
+
+#if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
+       // Allocate a new StackTraceElement object.
+       java_handle_t* h = builtin_new(class_java_lang_StackTraceElement);
+
+       if (h == NULL)
+                       return NULL;
+
+       java_lang_StackTraceElement jlste(h, filename, linenumber, declaringclass, javastring_new(m->name), ((m->flags & ACC_NATIVE) ? 1 : 0));
+#elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
+       // Allocate a new StackTraceElement object.
+       java_lang_StackTraceElement jlste(declaringclass, javastring_new(m->name), filename, linenumber);
+
+       if (jlste.is_null())
+               return NULL;
+#else
+# error unknown classpath configuration
+#endif
+
+       return jlste.get_handle();
+}
+#endif
+
+
+/**
+ * Creates a complete array of java.lang.StackTraceElement objects
+ * for the given stacktrace.
+ *
+ * @param st Given stacktrace.
+ * @return Array of filled StackTraceElement objects.
+ */
+#if defined(ENABLE_JAVASE)
+java_handle_objectarray_t* stacktrace_get_StackTraceElements(stacktrace_t* st)
+{
+       // Get length of stacktrace. If stacktrace is not available
+       // an empty array should be returned.
+       int32_t length = (st != NULL) ? st->length : 0;
+
+       // Create the stacktrace element array.
+       ObjectArray oa(length, class_java_lang_StackTraceElement);
+
+       if (oa.is_null())
+               return NULL;
+
+       // Iterate over all stacktrace elements.
+       for (int i = 0; i < length; i++) {
+
+               // Get stacktrace element at current index.
+               java_handle_t* h = stacktrace_get_StackTraceElement(st, i);
+
+               if (h == NULL)
+                       return NULL;
+
+               // Store stacktrace element in array.
+               oa.set_element(i, h);
+       }
+
+       return oa.get_handle();
+}
+#endif
+
+
 /* stacktrace_get_caller_class *************************************************
 
    Get the class on the stack at the given depth.  This function skips
@@ -750,17 +874,12 @@ classinfo *stacktrace_get_caller_class(int depth)
 #endif
 
 
-/* stacktrace_first_nonnull_classloader ****************************************
-
-   Returns the first non-null (user-defined) classloader on the stack.
-   If none is found NULL is returned.
-
-   RETURN:
-       classloader
-
-*******************************************************************************/
-
-classloader_t *stacktrace_first_nonnull_classloader(void)
+/**
+ * Returns the first non-null (user-defined) classloader on the stack.
+ *
+ * @return The first non-null classloader or NULL if none is found.
+ */
+classloader_t* stacktrace_first_nonnull_classloader(void)
 {
        stackframeinfo_t *sfi;
        stackframeinfo_t  tmpsfi;
@@ -785,6 +904,14 @@ classloader_t *stacktrace_first_nonnull_classloader(void)
                m  = tmpsfi.code->m;
                cl = class_get_classloader(m->clazz);
 
+#if defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
+               /* NOTE: See hotspot/src/share/vm/runtime/vframe.cpp
+                  (vframeStreamCommon::skip_reflection_related_frames). */
+               if (class_issubclass(m->clazz, class_sun_reflect_MethodAccessorImpl) ||
+                       class_issubclass(m->clazz, class_sun_reflect_ConstructorAccessorImpl))
+                       continue;
+#endif
+
                if (cl != NULL)
                        return cl;
        }
@@ -793,6 +920,82 @@ classloader_t *stacktrace_first_nonnull_classloader(void)
 }
 
 
+/**
+ * Checks if a given classloader is equal to the the second classloader
+ * or one of its ancestors (parents).
+ *
+ * XXX: This helper method should be moved to java_lang_Classloader.
+ */
+#if defined(ENABLE_JAVASE)
+static bool is_ancestor_of(classloader_t* loader, classloader_t* parent)
+{
+       // Iterate over chain of possible parents.
+       while (parent != NULL) {
+
+               // Check if given loader is parent.
+               if (loader == parent)
+                       return true;
+
+               // Jump to next parent.
+               java_lang_ClassLoader jlcl(parent);
+               parent = jlcl.get_parent();
+       }
+
+       return false;
+}
+#endif /* defined(ENABLE_JAVASE) */
+
+
+/**
+ * Returns the first non-system (user-defined) classloader on the stack.
+ * A non-system classloader is a non-null classloader being not equal to
+ * the system classloader (or one of its ancestors).
+ *
+ * @return The first non-system classloader or NULL if none is found.
+ */
+#if defined(ENABLE_JAVASE)
+classloader_t* stacktrace_first_nonsystem_classloader(void)
+{
+       stackframeinfo_t *sfi;
+       stackframeinfo_t  tmpsfi;
+       methodinfo       *m;
+       classloader_t    *cl;
+       classloader_t    *syscl;
+
+#if !defined(NDEBUG)
+       if (opt_DebugStackTrace)
+               log_println("[stacktrace_first_nonsystem_classloader]");
+#endif
+
+       // Get the stackframeinfo of the current thread.
+       sfi = threads_get_current_stackframeinfo();
+
+       // Get the system class class loader.
+       syscl = java_lang_ClassLoader::invoke_getSystemClassLoader();
+
+       // Iterate over the whole stack.
+       for (stacktrace_stackframeinfo_fill(&tmpsfi, sfi);
+                stacktrace_stackframeinfo_end_check(&tmpsfi) == false;
+                stacktrace_stackframeinfo_next(&tmpsfi)) {
+
+               m  = tmpsfi.code->m;
+               cl = class_get_classloader(m->clazz);
+
+               if (cl == NULL)
+                       continue;
+
+               // XXX if a method in a class in a trusted loader is in a
+               // doPrivileged, return NULL (or break) here.
+
+               if (!is_ancestor_of(cl, syscl))
+                       return cl;
+       }
+
+       return NULL;
+}
+#endif /* defined(ENABLE_JAVASE) */
+
+
 /* stacktrace_getClassContext **************************************************
 
    Creates a Class context array.
@@ -808,8 +1011,6 @@ java_handle_objectarray_t *stacktrace_getClassContext(void)
        stackframeinfo_t           *sfi;
        stackframeinfo_t            tmpsfi;
        int                         depth;
-       java_handle_objectarray_t  *oa;
-       java_object_t             **data;
        int                         i;
        methodinfo                 *m;
 
@@ -836,20 +1037,15 @@ java_handle_objectarray_t *stacktrace_getClassContext(void)
 
        /* Allocate the Class array. */
 
-       oa = builtin_anewarray(depth, class_java_lang_Class);
+       ClassArray ca(depth);
 
-       if (oa == NULL) {
+       if (ca.is_null()) {
                CYCLES_STATS_END(stacktrace_getClassContext);
 
                return NULL;
        }
 
        /* Fill the Class array from the stacktrace list. */
-
-       LLNI_CRITICAL_START;
-
-       data = LLNI_array_data(oa);
-
        /* Iterate over the whole stack. */
 
        i = 0;
@@ -868,7 +1064,7 @@ java_handle_objectarray_t *stacktrace_getClassContext(void)
 
                /* Store the class in the array. */
 
-               data[i] = (java_object_t *) m->clazz;
+               ca.set_element(i, m->clazz);
 
                i++;
        }
@@ -877,7 +1073,7 @@ java_handle_objectarray_t *stacktrace_getClassContext(void)
 
        CYCLES_STATS_END(stacktrace_getClassContext)
 
-       return oa;
+       return ca.get_handle();
 }
 
 
@@ -969,15 +1165,12 @@ classinfo *stacktrace_get_current_class(void)
 #if defined(ENABLE_JAVASE) && defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
 java_handle_objectarray_t *stacktrace_get_stack(void)
 {
-       stackframeinfo_t          *sfi;
-       stackframeinfo_t           tmpsfi;
-       int                        depth;
-       java_handle_objectarray_t *oa;
-       java_handle_objectarray_t *classes;
-       java_handle_objectarray_t *methodnames;
-       methodinfo                *m;
-       java_handle_t             *string;
-       int                        i;
+       stackframeinfo_t *sfi;
+       stackframeinfo_t  tmpsfi;
+       int               depth;
+       methodinfo       *m;
+       java_handle_t    *string;
+       int               i;
 
        CYCLES_STATS_DECLARE_AND_START
 
@@ -999,25 +1192,23 @@ java_handle_objectarray_t *stacktrace_get_stack(void)
 
        /* Allocate the required arrays. */
 
-       oa = builtin_anewarray(2, arrayclass_java_lang_Object);
+       ObjectArray oa(2, arrayclass_java_lang_Object);
+       ClassArray  classes(depth);
+       ObjectArray methodnames(depth, class_java_lang_String);
 
-       if (oa == NULL)
+       if (oa.is_null())
                goto return_NULL;
 
-       classes = builtin_anewarray(depth, class_java_lang_Class);
-
-       if (classes == NULL)
+       if (classes.is_null())
                goto return_NULL;
 
-       methodnames = builtin_anewarray(depth, class_java_lang_String);
-
-       if (methodnames == NULL)
+       if (methodnames.is_null())
                goto return_NULL;
 
        /* Set up the 2-dimensional array. */
 
-       array_objectarray_element_set(oa, 0, (java_handle_t *) classes);
-       array_objectarray_element_set(oa, 1, (java_handle_t *) methodnames);
+       oa.set_element(0, (java_handle_t *) classes.get_handle());
+       oa.set_element(1, (java_handle_t *) methodnames.get_handle());
 
        /* Iterate over the whole stack. */
        /* TODO We should use a critical section here to speed things
@@ -1038,10 +1229,8 @@ java_handle_objectarray_t *stacktrace_get_stack(void)
                        continue;
 
                /* Store the class in the array. */
-               /* NOTE: We use a LLNI-macro here, because a classinfo is not
-                  a handle. */
 
-               LLNI_array_direct(classes, i) = (java_object_t *) m->clazz;
+               classes.set_element(i, m->clazz);
 
                /* Store the name in the array. */
 
@@ -1050,14 +1239,14 @@ java_handle_objectarray_t *stacktrace_get_stack(void)
                if (string == NULL)
                        goto return_NULL;
 
-               array_objectarray_element_set(methodnames, i, string);
+               methodnames.set_element(i, string);
 
                i++;
        }
 
        CYCLES_STATS_END(stacktrace_get_stack)
 
-       return oa;
+       return oa.get_handle();
 
 return_NULL:
        CYCLES_STATS_END(stacktrace_get_stack)
@@ -1187,9 +1376,42 @@ void stacktrace_print_current(void)
 }
 
 
+/**
+ * Creates a stacktrace for the given thread.
+ *
+ * @param t Given thread.
+ * @return Current stacktrace of the given thread.
+ *
+ * XXX: Creation of the stacktrace starts at the most recent
+ * stackframeinfo block. If the thread is not inside the native
+ * world, the created stacktrace is not complete!
+ */
+#if defined(ENABLE_THREADS)
+stacktrace_t* stacktrace_get_of_thread(threadobject* t)
+{
+       stackframeinfo_t*        sfi;
+       java_handle_bytearray_t* stba;
+       stacktrace_t*            st;
+
+       sfi  = t->_stackframeinfo;
+       stba = stacktrace_get(sfi);
+
+       ByteArray ba(stba);
+
+       if (ba.is_null())
+               return NULL;
+
+       st  = (stacktrace_t*) ba.get_raw_data_ptr();
+
+       return st;
+}
+#endif
+
+
 /* stacktrace_print_of_thread **************************************************
 
-   Print the current stacktrace of the given thread.
+   Print the current stacktrace of the given thread. It will only work
+   for suspended threads.
 
    ARGUMENTS:
        t ... thread
@@ -1209,7 +1431,7 @@ void stacktrace_print_of_thread(threadobject *t)
 
        sfi = t->_stackframeinfo;
        
-       if (sfi == NULL) {
+       if (!t->suspended || sfi == NULL) {
                puts("\t<<No stacktrace available>>");
                fflush(stdout);
                return;
@@ -1254,11 +1476,11 @@ void stacktrace_print_exception(java_handle_t *h)
 #if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
 
        java_lang_VMThrowable vmt(t.get_vmState());
-       java_handle_bytearray_t* backtrace = vmt.get_vmdata();
+       ByteArray backtrace(vmt.get_vmdata());
 
 #elif defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) || defined(WITH_JAVA_RUNTIME_LIBRARY_CLDC1_1)
 
-       java_handle_bytearray_t* backtrace = t.get_backtrace();
+       ByteArray backtrace(t.get_backtrace());
 
 #else
 # error unknown classpath configuration
@@ -1266,14 +1488,14 @@ void stacktrace_print_exception(java_handle_t *h)
 
        // Sanity check.
 
-       assert(backtrace != NULL);
+       assert(backtrace.is_non_null());
 
        /* We need a critical section here as we use the byte-array data
           pointer directly. */
 
        LLNI_CRITICAL_START;
        
-       stacktrace_t* st = (stacktrace_t*) LLNI_array_data(backtrace);
+       stacktrace_t* st = (stacktrace_t*) backtrace.get_raw_data_ptr();
 
        stacktrace_print(st);