1 /*
   2  * Copyright (c) 2003, 2013, 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 /* Table of class information.
  42  *
  43  *   Each element in this table is identified with a ClassIndex.
  44  *   Each element is uniquely identified by it's signature and loader.
  45  *   Every class load has a unique class serial number.
  46  *   While loaded, each element will have a cache of a global reference
  47  *     to it's jclass object, plus jmethodID's as needed.
  48  *   Method signatures and names are obtained via BCI.
  49  *   Methods can be identified with a ClassIndex and MethodIndex pair,
  50  *     where the MethodIndex matches the index of the method name and
  51  *     signature arrays obtained from the BCI pass.
  52  *   Strings are stored in the string table and a StringIndex is used.
  53  *   Class Loaders are stored in the loader table and a LoaderIndex is used.
  54  *   Since the jclass object is an object, at some point an object table
  55  *      entry may be allocated for the jclass as an ObjectIndex.
  56  */
  57 
  58 #include "hprof.h"
  59 
  60 /* Effectively represents a jclass object. */
  61 
  62 /* These table elements are made unique by and sorted by signature name. */
  63 
  64 typedef struct ClassKey {
  65     StringIndex    sig_string_index;    /* Signature of class */
  66     LoaderIndex    loader_index;        /* Index for class loader */
  67 } ClassKey;
  68 
  69 /* Each class could contain method information, gotten from BCI callback */
  70 
  71 typedef struct MethodInfo {
  72     StringIndex  name_index;    /* Method name, index into string table */
  73     StringIndex  sig_index;     /* Method signature, index into string table */
  74     jmethodID    method_id;     /* Method ID, possibly NULL at first */
  75 } MethodInfo;
  76 
  77 /* The basic class information we save */
  78 
  79 typedef struct ClassInfo {
  80     jclass         classref;            /* Global ref to jclass */
  81     MethodInfo    *method;              /* Array of method data */
  82     int            method_count;        /* Count of methods */
  83     ObjectIndex    object_index;        /* Optional object index for jclass */
  84     SerialNumber   serial_num;          /* Unique to the actual class load */
  85     ClassStatus    status;              /* Current class status (bit mask) */
  86     ClassIndex     super;               /* Super class in this table */
  87     StringIndex    name;                /* Name of class */
  88     jint           inst_size;           /* #bytes needed for instance fields */
  89     jint           field_count;         /* Number of all fields */
  90     FieldInfo     *field;               /* Pointer to all FieldInfo's */
  91 } ClassInfo;
  92 
  93 /* Private interfaces */
  94 
  95 static ClassKey*
  96 get_pkey(ClassIndex index)
  97 {
  98     void *key_ptr;
  99     int   key_len;
 100 
 101     table_get_key(gdata->class_table, index, (void*)&key_ptr, &key_len);
 102     HPROF_ASSERT(key_len==sizeof(ClassKey));
 103     HPROF_ASSERT(key_ptr!=NULL);
 104     return (ClassKey*)key_ptr;
 105 }
 106 
 107 static void
 108 fillin_pkey(const char *sig, LoaderIndex loader_index, ClassKey *pkey)
 109 {
 110     static ClassKey empty_key;
 111 
 112     HPROF_ASSERT(loader_index!=0);
 113     *pkey                  = empty_key;
 114     pkey->sig_string_index = string_find_or_create(sig);
 115     pkey->loader_index     = loader_index;
 116 }
 117 
 118 static ClassInfo *
 119 get_info(ClassIndex index)
 120 {
 121     ClassInfo *info;
 122 
 123     info = (ClassInfo*)table_get_info(gdata->class_table, index);
 124     return info;
 125 }
 126 
 127 static void
 128 fill_info(TableIndex index, ClassKey *pkey)
 129 {
 130     ClassInfo *info;
 131     char      *sig;
 132 
 133     info = get_info(index);
 134     info->serial_num = gdata->class_serial_number_counter++;
 135     info->method_count = 0;
 136     info->inst_size = -1;
 137     info->field_count = -1;
 138     info->field = NULL;
 139     sig = string_get(pkey->sig_string_index);
 140     if ( sig[0] != JVM_SIGNATURE_CLASS ) {
 141         info->name = pkey->sig_string_index;
 142     } else {
 143         int        len;
 144 
 145         len = string_get_len(pkey->sig_string_index);
 146         if ( len > 2  ) {
 147             char      *name;
 148 
 149             /* Class signature looks like "Lname;", we want "name" here. */
 150             name = HPROF_MALLOC(len-1);
 151             (void)memcpy(name, sig+1, len-2);
 152             name[len-2] = 0;
 153             info->name = string_find_or_create(name);
 154             HPROF_FREE(name);
 155         } else {
 156             /* This would be strange, a class signature not in "Lname;" form? */
 157             info->name = pkey->sig_string_index;
 158         }
 159    }
 160 }
 161 
 162 static ClassIndex
 163 find_entry(ClassKey *pkey)
 164 {
 165     ClassIndex index;
 166 
 167     index = table_find_entry(gdata->class_table,
 168                                 (void*)pkey, (int)sizeof(ClassKey));
 169     return index;
 170 }
 171 
 172 static ClassIndex
 173 create_entry(ClassKey *pkey)
 174 {
 175     ClassIndex index;
 176 
 177     index = table_create_entry(gdata->class_table,
 178                                 (void*)pkey, (int)sizeof(ClassKey), NULL);
 179     fill_info(index, pkey);
 180     return index;
 181 }
 182 
 183 static ClassIndex
 184 find_or_create_entry(ClassKey *pkey)
 185 {
 186     ClassIndex      index;
 187 
 188     HPROF_ASSERT(pkey!=NULL);
 189     HPROF_ASSERT(pkey->loader_index!=0);
 190     index = find_entry(pkey);
 191     if ( index == 0 ) {
 192         index = create_entry(pkey);
 193     }
 194     return index;
 195 }
 196 
 197 static void
 198 delete_classref(JNIEnv *env, ClassInfo *info, jclass klass)
 199 {
 200     jclass ref;
 201     int    i;
 202 
 203     HPROF_ASSERT(env!=NULL);
 204     HPROF_ASSERT(info!=NULL);
 205 
 206     for ( i = 0 ; i < info->method_count ; i++ ) {
 207         info->method[i].method_id  = NULL;
 208     }
 209     ref = info->classref;
 210     if ( klass != NULL ) {
 211         info->classref = newGlobalReference(env, klass);
 212     } else {
 213         info->classref = NULL;
 214     }
 215     if ( ref != NULL ) {
 216         deleteGlobalReference(env, ref);
 217     }
 218 }
 219 
 220 static void
 221 cleanup_item(TableIndex index, void *key_ptr, int key_len,
 222                                 void *info_ptr, void *arg)
 223 {
 224     ClassInfo *info;
 225 
 226     /* Cleanup any information in this ClassInfo structure. */
 227     HPROF_ASSERT(key_ptr!=NULL);
 228     HPROF_ASSERT(key_len==sizeof(ClassKey));
 229     HPROF_ASSERT(info_ptr!=NULL);
 230     info = (ClassInfo *)info_ptr;
 231     if ( info->method_count > 0 ) {
 232         HPROF_FREE((void*)info->method);
 233         info->method_count = 0;
 234         info->method       = NULL;
 235     }
 236     if ( info->field != NULL ) {
 237         HPROF_FREE((void*)info->field);
 238         info->field_count = 0;
 239         info->field      = NULL;
 240     }
 241 }
 242 
 243 static void
 244 delete_ref_item(TableIndex index, void *key_ptr, int key_len,
 245                                 void *info_ptr, void *arg)
 246 {
 247     delete_classref((JNIEnv*)arg, (ClassInfo*)info_ptr, NULL);
 248 }
 249 
 250 static void
 251 list_item(TableIndex index, void *key_ptr, int key_len,
 252                                 void *info_ptr, void *arg)
 253 {
 254     ClassInfo *info;
 255     ClassKey   key;
 256     char      *sig;
 257     int        i;
 258 
 259     HPROF_ASSERT(key_ptr!=NULL);
 260     HPROF_ASSERT(key_len==sizeof(ClassKey));
 261     HPROF_ASSERT(info_ptr!=NULL);
 262     key = *((ClassKey*)key_ptr);
 263     sig = string_get(key.sig_string_index);
 264     info = (ClassInfo *)info_ptr;
 265     debug_message(
 266              "0x%08x: Class %s, SN=%u, status=0x%08x, ref=%p,"
 267              " method_count=%d\n",
 268              index,
 269              (const char *)sig,
 270              info->serial_num,
 271              info->status,
 272              (void*)info->classref,
 273              info->method_count);
 274     if ( info->method_count > 0 ) {
 275         for ( i = 0 ; i < info->method_count ; i++ ) {
 276             debug_message(
 277                 "    Method %d: \"%s\", sig=\"%s\", method=%p\n",
 278                 i,
 279                 string_get(info->method[i].name_index),
 280                 string_get(info->method[i].sig_index),
 281                 (void*)info->method[i].method_id);
 282         }
 283     }
 284 }
 285 
 286 static void
 287 all_status_remove(TableIndex index, void *key_ptr, int key_len,
 288                                 void *info_ptr, void *arg)
 289 {
 290     ClassInfo   *info;
 291     ClassStatus  status;
 292 
 293     HPROF_ASSERT(info_ptr!=NULL);
 294     /*LINTED*/
 295     status = (ClassStatus)(long)(ptrdiff_t)arg;
 296     info = (ClassInfo *)info_ptr;
 297     info->status &= (~status);
 298 }
 299 
 300 static void
 301 unload_walker(TableIndex index, void *key_ptr, int key_len,
 302                                 void *info_ptr, void *arg)
 303 {
 304     ClassInfo        *info;
 305 
 306     HPROF_ASSERT(info_ptr!=NULL);
 307     info = (ClassInfo *)info_ptr;
 308     if ( ! ( info->status & CLASS_IN_LOAD_LIST ) ) {
 309         if ( ! (info->status & (CLASS_SPECIAL|CLASS_SYSTEM|CLASS_UNLOADED)) ) {
 310             io_write_class_unload(info->serial_num, info->object_index);
 311             info->status |= CLASS_UNLOADED;
 312             delete_classref((JNIEnv*)arg, info, NULL);
 313         }
 314     }
 315 }
 316 
 317 /* External interfaces */
 318 
 319 void
 320 class_init(void)
 321 {
 322     HPROF_ASSERT(gdata->class_table==NULL);
 323     gdata->class_table = table_initialize("Class", 512, 512, 511,
 324                                     (int)sizeof(ClassInfo));
 325 }
 326 
 327 ClassIndex
 328 class_find_or_create(const char *sig, LoaderIndex loader_index)
 329 {
 330     ClassKey key;
 331 
 332     fillin_pkey(sig, loader_index, &key);
 333     return find_or_create_entry(&key);
 334 }
 335 
 336 ClassIndex
 337 class_create(const char *sig, LoaderIndex loader_index)
 338 {
 339     ClassKey key;
 340 
 341     fillin_pkey(sig, loader_index, &key);
 342     return create_entry(&key);
 343 }
 344 
 345 void
 346 class_prime_system_classes(void)
 347 {
 348     /* Prime System classes? Anything before VM_START is System class.
 349      *   Or classes loaded before env arg is non-NULL.
 350      *   Or any of the classes listed below.
 351      */
 352     static const char * signatures[] =
 353         {
 354             "Ljava/lang/Object;",
 355             "Ljava/io/Serializable;",
 356             "Ljava/lang/String;",
 357             "Ljava/lang/Class;",
 358             "Ljava/lang/ClassLoader;",
 359             "Ljava/lang/System;",
 360             "Ljava/lang/Thread;",
 361             "Ljava/lang/ThreadGroup;",
 362         };
 363     int n_signatures;
 364     int i;
 365     LoaderIndex loader_index;
 366 
 367     n_signatures = (int)sizeof(signatures)/(int)sizeof(signatures[0]);
 368     loader_index = loader_find_or_create(NULL, NULL);
 369     for ( i = 0 ; i < n_signatures ; i++ ) {
 370         ClassInfo  *info;
 371         ClassIndex  index;
 372         ClassKey    key;
 373 
 374         fillin_pkey(signatures[i], loader_index, &key);
 375         index = find_or_create_entry(&key);
 376         info = get_info(index);
 377         info->status |= CLASS_SYSTEM;
 378     }
 379 }
 380 
 381 void
 382 class_add_status(ClassIndex index, ClassStatus status)
 383 {
 384     ClassInfo *info;
 385 
 386     info = get_info(index);
 387     info->status |= status;
 388 }
 389 
 390 ClassStatus
 391 class_get_status(ClassIndex index)
 392 {
 393     ClassInfo *info;
 394 
 395     info = get_info(index);
 396     return info->status;
 397 }
 398 
 399 StringIndex
 400 class_get_signature(ClassIndex index)
 401 {
 402     ClassKey *pkey;
 403 
 404     pkey = get_pkey(index);
 405     return pkey->sig_string_index;
 406 }
 407 
 408 SerialNumber
 409 class_get_serial_number(ClassIndex index)
 410 {
 411     ClassInfo *info;
 412 
 413     if ( index == 0 ) {
 414         return 0;
 415     }
 416     info = get_info(index);
 417     return info->serial_num;
 418 }
 419 
 420 void
 421 class_all_status_remove(ClassStatus status)
 422 {
 423     table_walk_items(gdata->class_table, &all_status_remove,
 424                 (void*)(ptrdiff_t)(long)status);
 425 }
 426 
 427 void
 428 class_do_unloads(JNIEnv *env)
 429 {
 430     table_walk_items(gdata->class_table, &unload_walker, (void*)env);
 431 }
 432 
 433 void
 434 class_list(void)
 435 {
 436     debug_message(
 437         "--------------------- Class Table ------------------------\n");
 438     table_walk_items(gdata->class_table, &list_item, NULL);
 439     debug_message(
 440         "----------------------------------------------------------\n");
 441 }
 442 
 443 void
 444 class_cleanup(void)
 445 {
 446     table_cleanup(gdata->class_table, &cleanup_item, NULL);
 447     gdata->class_table = NULL;
 448 }
 449 
 450 void
 451 class_delete_global_references(JNIEnv* env)
 452 {
 453     table_walk_items(gdata->class_table, &delete_ref_item, (void*)env);
 454 }
 455 
 456 void
 457 class_set_methods(ClassIndex index, const char **name, const char **sig,
 458                         int count)
 459 {
 460     ClassInfo *info;
 461     int        i;
 462 
 463     info               = get_info(index);
 464     if ( info->method_count > 0 ) {
 465         HPROF_FREE((void*)info->method);
 466         info->method_count = 0;
 467         info->method       = NULL;
 468     }
 469     info->method_count = count;
 470     if ( count > 0 ) {
 471         info->method = (MethodInfo *)HPROF_MALLOC(count*(int)sizeof(MethodInfo));
 472         for ( i = 0 ; i < count ; i++ ) {
 473             info->method[i].name_index = string_find_or_create(name[i]);
 474             info->method[i].sig_index  = string_find_or_create(sig[i]);
 475             info->method[i].method_id  = NULL;
 476         }
 477     }
 478 }
 479 
 480 jclass
 481 class_new_classref(JNIEnv *env, ClassIndex index, jclass classref)
 482 {
 483     ClassInfo *info;
 484 
 485     HPROF_ASSERT(classref!=NULL);
 486     info = get_info(index);
 487     if ( ! isSameObject(env, classref, info->classref) ) {
 488         delete_classref(env, info, classref);
 489     }
 490     return info->classref;
 491 }
 492 
 493 jclass
 494 class_get_class(JNIEnv *env, ClassIndex index)
 495 {
 496     ClassInfo *info;
 497     jclass     clazz;
 498 
 499     info        = get_info(index);
 500     clazz       = info->classref;
 501     if ( env != NULL && clazz == NULL ) {
 502         WITH_LOCAL_REFS(env, 1) {
 503             jclass   new_clazz;
 504             char    *class_name;
 505 
 506             class_name = string_get(info->name);
 507             /* This really only makes sense for the bootclass classes,
 508              *   since FindClass doesn't provide a way to load a class in
 509              *   a specific class loader.
 510              */
 511             new_clazz = findClass(env, class_name);
 512             if ( new_clazz == NULL ) {
 513                 HPROF_ERROR(JNI_TRUE, "Cannot load class with findClass");
 514             }
 515             HPROF_ASSERT(new_clazz!=NULL);
 516             clazz = class_new_classref(env, index, new_clazz);
 517         } END_WITH_LOCAL_REFS;
 518         HPROF_ASSERT(clazz!=NULL);
 519     }
 520     return clazz;
 521 }
 522 
 523 jmethodID
 524 class_get_methodID(JNIEnv *env, ClassIndex index, MethodIndex mnum)
 525 {
 526     ClassInfo *info;
 527     jmethodID  method;
 528 
 529     info = get_info(index);
 530     if (mnum >= info->method_count) {
 531         jclass newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
 532         (*env)->ThrowNew(env, newExcCls, "Illegal mnum");
 533 
 534         return NULL;
 535     }
 536     method = info->method[mnum].method_id;
 537     if ( method == NULL ) {
 538         char * name;
 539         char * sig;
 540         jclass clazz;
 541 
 542         name  = (char *)string_get(info->method[mnum].name_index);
 543         if (name==NULL) {
 544             jclass newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
 545             (*env)->ThrowNew(env, newExcCls, "Name not found");
 546 
 547             return NULL;
 548         }
 549         sig   = (char *)string_get(info->method[mnum].sig_index);
 550         HPROF_ASSERT(sig!=NULL);
 551         clazz = class_get_class(env, index);
 552         if ( clazz != NULL ) {
 553             method = getMethodID(env, clazz, name, sig);
 554             HPROF_ASSERT(method!=NULL);
 555             info = get_info(index);
 556             info->method[mnum].method_id = method;
 557         }
 558     }
 559     return method;
 560 }
 561 
 562 void
 563 class_set_inst_size(ClassIndex index, jint inst_size)
 564 {
 565     ClassInfo *info;
 566 
 567     info = get_info(index);
 568     info->inst_size = inst_size;
 569 }
 570 
 571 jint
 572 class_get_inst_size(ClassIndex index)
 573 {
 574     ClassInfo *info;
 575 
 576     info = get_info(index);
 577     return info->inst_size;
 578 }
 579 
 580 void
 581 class_set_object_index(ClassIndex index, ObjectIndex object_index)
 582 {
 583     ClassInfo *info;
 584 
 585     info = get_info(index);
 586     info->object_index = object_index;
 587 }
 588 
 589 ObjectIndex
 590 class_get_object_index(ClassIndex index)
 591 {
 592     ClassInfo *info;
 593 
 594     info = get_info(index);
 595     return info->object_index;
 596 }
 597 
 598 ClassIndex
 599 class_get_super(ClassIndex index)
 600 {
 601     ClassInfo *info;
 602 
 603     info = get_info(index);
 604     return info->super;
 605 }
 606 
 607 void
 608 class_set_super(ClassIndex index, ClassIndex super)
 609 {
 610     ClassInfo *info;
 611 
 612     info = get_info(index);
 613     info->super = super;
 614 }
 615 
 616 LoaderIndex
 617 class_get_loader(ClassIndex index)
 618 {
 619     ClassKey *pkey;
 620 
 621     pkey = get_pkey(index);
 622     HPROF_ASSERT(pkey->loader_index!=0);
 623     return pkey->loader_index;
 624 }
 625 
 626 /* Get ALL class fields (supers too), return 1 on error, 0 if ok */
 627 jint
 628 class_get_all_fields(JNIEnv *env, ClassIndex index,
 629                 jint *pfield_count, FieldInfo **pfield)
 630 {
 631     ClassInfo  *info;
 632     FieldInfo  *finfo;
 633     jint        count;
 634     jint        ret;
 635 
 636     count = 0;
 637     finfo = NULL;
 638     ret   = 1;       /* Default is to return an error condition */
 639 
 640     info = get_info(index);
 641     if ( info != NULL ) {
 642         if ( info->field_count >= 0 ) {
 643             /* Get cache */
 644             count = info->field_count;
 645             finfo = info->field;
 646             ret   = 0;                 /* Return of cache data, no error */
 647         } else {
 648             jclass     klass;
 649 
 650             klass = info->classref;
 651             if ( klass == NULL || isSameObject(env, klass, NULL) ) {
 652                 /* This is probably an error because this will cause the field
 653                  *    index values to be off, but I'm hesitant to generate a
 654                  *    fatal error here, so I will issue something and continue.
 655                  *    I should have been holding a global reference to all the
 656                  *    jclass, so I'm not sure how this could happen.
 657                  *    Issuing a FindClass() here is just asking for trouble
 658                  *    because if the class went away, we aren't even sure
 659                  *    what ClassLoader to use.
 660                  */
 661                 HPROF_ERROR(JNI_FALSE, "Missing jclass when fields needed");
 662             } else {
 663                 jint status;
 664 
 665                 status = getClassStatus(klass);
 666                 if ( status &
 667                     (JVMTI_CLASS_STATUS_PRIMITIVE|JVMTI_CLASS_STATUS_ARRAY) ) {
 668                     /* Set cache */
 669                     info->field_count = count;
 670                     info->field       = finfo;
 671                     ret               = 0;      /* Primitive or array ok */
 672                 } else if ( status & JVMTI_CLASS_STATUS_PREPARED ) {
 673                     /* Call JVMTI to get them */
 674                     getAllClassFieldInfo(env, klass, &count, &finfo);
 675                     /* Set cache */
 676                     info->field_count = count;
 677                     info->field       = finfo;
 678                     ret               = 0;
 679                 }
 680             }
 681         }
 682     }
 683     *pfield_count = count;
 684     *pfield       = finfo;
 685     return ret;
 686 }