1 /*
   2  * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
   3  *
   4  * Redistribution and use in source and binary forms, with or without
   5  * modification, are permitted provided that the following conditions
   6  * are met:
   7  *
   8  *   - Redistributions of source code must retain the above copyright
   9  *     notice, this list of conditions and the following disclaimer.
  10  *
  11  *   - Redistributions in binary form must reproduce the above copyright
  12  *     notice, this list of conditions and the following disclaimer in the
  13  *     documentation and/or other materials provided with the distribution.
  14  *
  15  *   - Neither the name of Oracle nor the names of its
  16  *     contributors may be used to endorse or promote products derived
  17  *     from this software without specific prior written permission.
  18  *
  19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30  */
  31 
  32 /*
  33  * This source code is provided to illustrate the usage of a given feature
  34  * or technique and has been deliberately simplified. Additional steps
  35  * required for a production-quality application, such as security checks,
  36  * input validation and proper error handling, might not be present in
  37  * this sample code.
  38  */
  39 
  40 
  41 /* The Class Loader table. */
  42 
  43 /*
  44  * The Class Loader objects show up so early in the VM process that a
  45  *   separate table was designated for Class Loaders.
  46  *
  47  * The Class Loader is unique by way of it's jobject uniqueness, unfortunately
  48  *   use of JNI too early for jobject comparisons is problematic.
  49  *   It is assumed that the number of class loaders will be limited, and
  50  *   a simple linear search will be performed for now.
  51  *   That logic is isolated here and can be changed to use the standard
  52  *   table hash table search once we know JNI can be called safely.
  53  *
  54  * A weak global reference is created to keep tabs on loaders, and as
  55  *   each search for a loader happens, NULL weak global references will
  56  *   trigger the freedom of those entries.
  57  *
  58  */
  59 
  60 #include "hprof.h"
  61 
  62 typedef struct {
  63     jobject         globalref;    /* Weak Global reference for object */
  64     ObjectIndex     object_index;
  65 } LoaderInfo;
  66 
  67 static LoaderInfo *
  68 get_info(LoaderIndex index)
  69 {
  70     return (LoaderInfo*)table_get_info(gdata->loader_table, index);
  71 }
  72 
  73 static void
  74 delete_globalref(JNIEnv *env, LoaderInfo *info)
  75 {
  76     jobject     ref;
  77 
  78     HPROF_ASSERT(env!=NULL);
  79     HPROF_ASSERT(info!=NULL);
  80     ref = info->globalref;
  81     info->globalref = NULL;
  82     if ( ref != NULL ) {
  83         deleteWeakGlobalReference(env, ref);
  84     }
  85     info->object_index = 0;
  86 }
  87 
  88 static void
  89 cleanup_item(TableIndex index, void *key_ptr, int key_len,
  90                         void *info_ptr, void *arg)
  91 {
  92 }
  93 
  94 static void
  95 delete_ref_item(TableIndex index, void *key_ptr, int key_len,
  96                         void *info_ptr, void *arg)
  97 {
  98     delete_globalref((JNIEnv*)arg, (LoaderInfo*)info_ptr);
  99 }
 100 
 101 static void
 102 list_item(TableIndex index, void *key_ptr, int key_len,
 103                         void *info_ptr, void *arg)
 104 {
 105     LoaderInfo     *info;
 106 
 107     HPROF_ASSERT(info_ptr!=NULL);
 108 
 109     info        = (LoaderInfo*)info_ptr;
 110     debug_message( "Loader 0x%08x: globalref=%p, object_index=%d\n",
 111                 index, (void*)info->globalref, info->object_index);
 112 }
 113 
 114 static void
 115 free_entry(JNIEnv *env, LoaderIndex index)
 116 {
 117     LoaderInfo *info;
 118 
 119     info = get_info(index);
 120     delete_globalref(env, info);
 121     table_free_entry(gdata->loader_table, index);
 122 }
 123 
 124 typedef struct SearchData {
 125     JNIEnv *env;
 126     jobject loader;
 127     LoaderIndex found;
 128 } SearchData;
 129 
 130 static void
 131 search_item(TableIndex index, void *key_ptr, int key_len, void *info_ptr, void *arg)
 132 {
 133     LoaderInfo  *info;
 134     SearchData  *data;
 135 
 136     HPROF_ASSERT(info_ptr!=NULL);
 137     HPROF_ASSERT(arg!=NULL);
 138     info        = (LoaderInfo*)info_ptr;
 139     data        = (SearchData*)arg;
 140     if ( data->loader == info->globalref ) {
 141         /* Covers when looking for NULL too. */
 142         HPROF_ASSERT(data->found==0); /* Did we find more than one? */
 143         data->found = index;
 144     } else if ( data->env != NULL && data->loader != NULL &&
 145                 info->globalref != NULL ) {
 146         jobject lref;
 147 
 148         lref = newLocalReference(data->env, info->globalref);
 149         if ( lref == NULL ) {
 150             /* Object went away, free reference and entry */
 151             free_entry(data->env, index);
 152         } else if ( isSameObject(data->env, data->loader, lref) ) {
 153             HPROF_ASSERT(data->found==0); /* Did we find more than one? */
 154             data->found = index;
 155         }
 156         if ( lref != NULL ) {
 157             deleteLocalReference(data->env, lref);
 158         }
 159     }
 160 
 161 }
 162 
 163 static LoaderIndex
 164 search(JNIEnv *env, jobject loader)
 165 {
 166     SearchData  data;
 167 
 168     data.env    = env;
 169     data.loader = loader;
 170     data.found  = 0;
 171     table_walk_items(gdata->loader_table, &search_item, (void*)&data);
 172     return data.found;
 173 }
 174 
 175 LoaderIndex
 176 loader_find_or_create(JNIEnv *env, jobject loader)
 177 {
 178     LoaderIndex index;
 179 
 180     /* See if we remembered the system loader */
 181     if ( loader==NULL && gdata->system_loader != 0 ) {
 182         return gdata->system_loader;
 183     }
 184     if ( loader==NULL ) {
 185         env = NULL;
 186     }
 187     index = search(env, loader);
 188     if ( index == 0 ) {
 189         static LoaderInfo  empty_info;
 190         LoaderInfo  info;
 191 
 192         info = empty_info;
 193         if ( loader != NULL ) {
 194             HPROF_ASSERT(env!=NULL);
 195             info.globalref = newWeakGlobalReference(env, loader);
 196             info.object_index = 0;
 197         }
 198         index = table_create_entry(gdata->loader_table, NULL, 0, (void*)&info);
 199     }
 200     HPROF_ASSERT(search(env,loader)==index);
 201     /* Remember the system loader */
 202     if ( loader==NULL && gdata->system_loader == 0 ) {
 203         gdata->system_loader = index;
 204     }
 205     return index;
 206 }
 207 
 208 void
 209 loader_init(void)
 210 {
 211     gdata->loader_table = table_initialize("Loader",
 212                             16, 16, 0, (int)sizeof(LoaderInfo));
 213 }
 214 
 215 void
 216 loader_list(void)
 217 {
 218     debug_message(
 219         "--------------------- Loader Table ------------------------\n");
 220     table_walk_items(gdata->loader_table, &list_item, NULL);
 221     debug_message(
 222         "----------------------------------------------------------\n");
 223 }
 224 
 225 void
 226 loader_cleanup(void)
 227 {
 228     table_cleanup(gdata->loader_table, &cleanup_item, NULL);
 229     gdata->loader_table = NULL;
 230 }
 231 
 232 void
 233 loader_delete_global_references(JNIEnv *env)
 234 {
 235     table_walk_items(gdata->loader_table, &delete_ref_item, (void*)env);
 236 }
 237 
 238 /* Get the object index for a class loader */
 239 ObjectIndex
 240 loader_object_index(JNIEnv *env, LoaderIndex index)
 241 {
 242     LoaderInfo *info;
 243     ObjectIndex object_index;
 244     jobject     wref;
 245 
 246     /* Assume no object index at first (default class loader) */
 247     info = get_info(index);
 248     object_index = info->object_index;
 249     wref = info->globalref;
 250     if ( wref != NULL && object_index == 0 ) {
 251         jobject lref;
 252 
 253         object_index = 0;
 254         lref = newLocalReference(env, wref);
 255         if ( lref != NULL && !isSameObject(env, lref, NULL) ) {
 256             jlong tag;
 257 
 258             /* Get the tag on the object and extract the object_index */
 259             tag = getTag(lref);
 260             if ( tag != (jlong)0 ) {
 261                 object_index = tag_extract(tag);
 262             }
 263         }
 264         if ( lref != NULL ) {
 265             deleteLocalReference(env, lref);
 266         }
 267         info->object_index = object_index;
 268     }
 269     return object_index;
 270 }