Home | History | Annotate | Download | only in 906-iterate-heap
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "inttypes.h"
     18 
     19 #include <iomanip>
     20 #include <iostream>
     21 #include <pthread.h>
     22 #include <sstream>
     23 #include <stdio.h>
     24 #include <vector>
     25 
     26 #include "android-base/logging.h"
     27 #include "android-base/stringprintf.h"
     28 
     29 #include "jni.h"
     30 #include "jvmti.h"
     31 #include "scoped_primitive_array.h"
     32 
     33 // Test infrastructure
     34 #include "jvmti_helper.h"
     35 #include "test_env.h"
     36 #include "ti_macros.h"
     37 #include "ti_utf.h"
     38 
     39 namespace art {
     40 namespace Test906IterateHeap {
     41 
     42 class IterationConfig {
     43  public:
     44   IterationConfig() {}
     45   virtual ~IterationConfig() {}
     46 
     47   virtual jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) = 0;
     48 };
     49 
     50 static jint JNICALL HeapIterationCallback(jlong class_tag,
     51                                           jlong size,
     52                                           jlong* tag_ptr,
     53                                           jint length,
     54                                           void* user_data) {
     55   IterationConfig* config = reinterpret_cast<IterationConfig*>(user_data);
     56   return config->Handle(class_tag, size, tag_ptr, length);
     57 }
     58 
     59 static bool Run(JNIEnv* env, jint heap_filter, jclass klass_filter, IterationConfig* config) {
     60   jvmtiHeapCallbacks callbacks;
     61   memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
     62   callbacks.heap_iteration_callback = HeapIterationCallback;
     63 
     64   jvmtiError ret = jvmti_env->IterateThroughHeap(heap_filter,
     65                                                  klass_filter,
     66                                                  &callbacks,
     67                                                  config);
     68   if (JvmtiErrorToException(env, jvmti_env, ret)) {
     69     return false;
     70   }
     71   return true;
     72 }
     73 
     74 extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateThroughHeapCount(
     75     JNIEnv* env,
     76     jclass klass ATTRIBUTE_UNUSED,
     77     jint heap_filter,
     78     jclass klass_filter,
     79     jint stop_after) {
     80   class CountIterationConfig : public IterationConfig {
     81    public:
     82     CountIterationConfig(jint _counter, jint _stop_after)
     83         : counter(_counter),
     84           stop_after(_stop_after) {
     85     }
     86 
     87     jint Handle(jlong class_tag ATTRIBUTE_UNUSED,
     88                 jlong size ATTRIBUTE_UNUSED,
     89                 jlong* tag_ptr ATTRIBUTE_UNUSED,
     90                 jint length ATTRIBUTE_UNUSED) OVERRIDE {
     91       counter++;
     92       if (counter == stop_after) {
     93         return JVMTI_VISIT_ABORT;
     94       }
     95       return 0;
     96     }
     97 
     98     jint counter;
     99     const jint stop_after;
    100   };
    101 
    102   CountIterationConfig config(0, stop_after);
    103   Run(env, heap_filter, klass_filter, &config);
    104 
    105   if (config.counter > config.stop_after) {
    106     printf("Error: more objects visited than signaled.");
    107   }
    108 
    109   return config.counter;
    110 }
    111 
    112 extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateThroughHeapData(
    113     JNIEnv* env,
    114     jclass klass ATTRIBUTE_UNUSED,
    115     jint heap_filter,
    116     jclass klass_filter,
    117     jlongArray class_tags,
    118     jlongArray sizes,
    119     jlongArray tags,
    120     jintArray lengths) {
    121   class DataIterationConfig : public IterationConfig {
    122    public:
    123     jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) OVERRIDE {
    124       class_tags_.push_back(class_tag);
    125       sizes_.push_back(size);
    126       tags_.push_back(*tag_ptr);
    127       lengths_.push_back(length);
    128 
    129       return 0;  // Continue.
    130     }
    131 
    132     std::vector<jlong> class_tags_;
    133     std::vector<jlong> sizes_;
    134     std::vector<jlong> tags_;
    135     std::vector<jint> lengths_;
    136   };
    137 
    138   DataIterationConfig config;
    139   if (!Run(env, heap_filter, klass_filter, &config)) {
    140     return -1;
    141   }
    142 
    143   ScopedLongArrayRW s_class_tags(env, class_tags);
    144   ScopedLongArrayRW s_sizes(env, sizes);
    145   ScopedLongArrayRW s_tags(env, tags);
    146   ScopedIntArrayRW s_lengths(env, lengths);
    147 
    148   for (size_t i = 0; i != config.class_tags_.size(); ++i) {
    149     s_class_tags[i] = config.class_tags_[i];
    150     s_sizes[i] = config.sizes_[i];
    151     s_tags[i] = config.tags_[i];
    152     s_lengths[i] = config.lengths_[i];
    153   }
    154 
    155   return static_cast<jint>(config.class_tags_.size());
    156 }
    157 
    158 extern "C" JNIEXPORT void JNICALL Java_art_Test906_iterateThroughHeapAdd(
    159     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint heap_filter, jclass klass_filter) {
    160   class AddIterationConfig : public IterationConfig {
    161    public:
    162     AddIterationConfig() {}
    163 
    164     jint Handle(jlong class_tag ATTRIBUTE_UNUSED,
    165                 jlong size ATTRIBUTE_UNUSED,
    166                 jlong* tag_ptr,
    167                 jint length ATTRIBUTE_UNUSED) OVERRIDE {
    168       jlong current_tag = *tag_ptr;
    169       if (current_tag != 0) {
    170         *tag_ptr = current_tag + 10;
    171       }
    172       return 0;
    173     }
    174   };
    175 
    176   AddIterationConfig config;
    177   Run(env, heap_filter, klass_filter, &config);
    178 }
    179 
    180 extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapString(
    181     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) {
    182   struct FindStringCallbacks {
    183     explicit FindStringCallbacks(jlong t) : tag_to_find(t) {}
    184 
    185     static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED,
    186                                               jlong size ATTRIBUTE_UNUSED,
    187                                               jlong* tag_ptr ATTRIBUTE_UNUSED,
    188                                               jint length ATTRIBUTE_UNUSED,
    189                                               void* user_data ATTRIBUTE_UNUSED) {
    190       return 0;
    191     }
    192 
    193     static jint JNICALL StringValueCallback(jlong class_tag,
    194                                             jlong size,
    195                                             jlong* tag_ptr,
    196                                             const jchar* value,
    197                                             jint value_length,
    198                                             void* user_data) {
    199       FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data);
    200       if (*tag_ptr == p->tag_to_find) {
    201         size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length);
    202         std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]);
    203         memset(mod_utf.get(), 0, utf_byte_count + 1);
    204         ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
    205         if (!p->data.empty()) {
    206           p->data += "\n";
    207         }
    208         p->data += android::base::StringPrintf("%" PRId64 "@%" PRId64 " (%" PRId64 ", '%s')",
    209                                                *tag_ptr,
    210                                                class_tag,
    211                                                size,
    212                                                mod_utf.get());
    213         // Update the tag to test whether that works.
    214         *tag_ptr = *tag_ptr + 1;
    215       }
    216       return 0;
    217     }
    218 
    219     std::string data;
    220     const jlong tag_to_find;
    221   };
    222 
    223   jvmtiHeapCallbacks callbacks;
    224   memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
    225   callbacks.heap_iteration_callback = FindStringCallbacks::HeapIterationCallback;
    226   callbacks.string_primitive_value_callback = FindStringCallbacks::StringValueCallback;
    227 
    228   FindStringCallbacks fsc(tag);
    229   jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fsc);
    230   if (JvmtiErrorToException(env, jvmti_env, ret)) {
    231     return nullptr;
    232   }
    233   return env->NewStringUTF(fsc.data.c_str());
    234 }
    235 
    236 extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapPrimitiveArray(
    237     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) {
    238   struct FindArrayCallbacks {
    239     explicit FindArrayCallbacks(jlong t) : tag_to_find(t) {}
    240 
    241     static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED,
    242                                               jlong size ATTRIBUTE_UNUSED,
    243                                               jlong* tag_ptr ATTRIBUTE_UNUSED,
    244                                               jint length ATTRIBUTE_UNUSED,
    245                                               void* user_data ATTRIBUTE_UNUSED) {
    246       return 0;
    247     }
    248 
    249     static jint JNICALL ArrayValueCallback(jlong class_tag,
    250                                            jlong size,
    251                                            jlong* tag_ptr,
    252                                            jint element_count,
    253                                            jvmtiPrimitiveType element_type,
    254                                            const void* elements,
    255                                            void* user_data) {
    256       FindArrayCallbacks* p = reinterpret_cast<FindArrayCallbacks*>(user_data);
    257       if (*tag_ptr == p->tag_to_find) {
    258         std::ostringstream oss;
    259         oss << *tag_ptr
    260             << '@'
    261             << class_tag
    262             << " ("
    263             << size
    264             << ", "
    265             << element_count
    266             << "x"
    267             << static_cast<char>(element_type)
    268             << " '";
    269         size_t element_size;
    270         switch (element_type) {
    271           case JVMTI_PRIMITIVE_TYPE_BOOLEAN:
    272           case JVMTI_PRIMITIVE_TYPE_BYTE:
    273             element_size = 1;
    274             break;
    275           case JVMTI_PRIMITIVE_TYPE_CHAR:
    276           case JVMTI_PRIMITIVE_TYPE_SHORT:
    277             element_size = 2;
    278             break;
    279           case JVMTI_PRIMITIVE_TYPE_INT:
    280           case JVMTI_PRIMITIVE_TYPE_FLOAT:
    281             element_size = 4;
    282             break;
    283           case JVMTI_PRIMITIVE_TYPE_LONG:
    284           case JVMTI_PRIMITIVE_TYPE_DOUBLE:
    285             element_size = 8;
    286             break;
    287           default:
    288             LOG(FATAL) << "Unknown type " << static_cast<size_t>(element_type);
    289             UNREACHABLE();
    290         }
    291         const uint8_t* data = reinterpret_cast<const uint8_t*>(elements);
    292         for (size_t i = 0; i != element_size * element_count; ++i) {
    293           oss << android::base::StringPrintf("%02x", data[i]);
    294         }
    295         oss << "')";
    296 
    297         if (!p->data.empty()) {
    298           p->data += "\n";
    299         }
    300         p->data += oss.str();
    301         // Update the tag to test whether that works.
    302         *tag_ptr = *tag_ptr + 1;
    303       }
    304       return 0;
    305     }
    306 
    307     std::string data;
    308     const jlong tag_to_find;
    309   };
    310 
    311   jvmtiHeapCallbacks callbacks;
    312   memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
    313   callbacks.heap_iteration_callback = FindArrayCallbacks::HeapIterationCallback;
    314   callbacks.array_primitive_value_callback = FindArrayCallbacks::ArrayValueCallback;
    315 
    316   FindArrayCallbacks fac(tag);
    317   jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fac);
    318   if (JvmtiErrorToException(env, jvmti_env, ret)) {
    319     return nullptr;
    320   }
    321   return env->NewStringUTF(fac.data.c_str());
    322 }
    323 
    324 static constexpr const char* GetPrimitiveTypeName(jvmtiPrimitiveType type) {
    325   switch (type) {
    326     case JVMTI_PRIMITIVE_TYPE_BOOLEAN:
    327       return "boolean";
    328     case JVMTI_PRIMITIVE_TYPE_BYTE:
    329       return "byte";
    330     case JVMTI_PRIMITIVE_TYPE_CHAR:
    331       return "char";
    332     case JVMTI_PRIMITIVE_TYPE_SHORT:
    333       return "short";
    334     case JVMTI_PRIMITIVE_TYPE_INT:
    335       return "int";
    336     case JVMTI_PRIMITIVE_TYPE_FLOAT:
    337       return "float";
    338     case JVMTI_PRIMITIVE_TYPE_LONG:
    339       return "long";
    340     case JVMTI_PRIMITIVE_TYPE_DOUBLE:
    341       return "double";
    342   }
    343   LOG(FATAL) << "Unknown type " << static_cast<size_t>(type);
    344   UNREACHABLE();
    345 }
    346 
    347 extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapPrimitiveFields(
    348     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) {
    349   struct FindFieldCallbacks {
    350     explicit FindFieldCallbacks(jlong t) : tag_to_find(t) {}
    351 
    352     static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED,
    353                                               jlong size ATTRIBUTE_UNUSED,
    354                                               jlong* tag_ptr ATTRIBUTE_UNUSED,
    355                                               jint length ATTRIBUTE_UNUSED,
    356                                               void* user_data ATTRIBUTE_UNUSED) {
    357       return 0;
    358     }
    359 
    360     static jint JNICALL PrimitiveFieldValueCallback(jvmtiHeapReferenceKind kind,
    361                                                     const jvmtiHeapReferenceInfo* info,
    362                                                     jlong class_tag,
    363                                                     jlong* tag_ptr,
    364                                                     jvalue value,
    365                                                     jvmtiPrimitiveType value_type,
    366                                                     void* user_data) {
    367       FindFieldCallbacks* p = reinterpret_cast<FindFieldCallbacks*>(user_data);
    368       if (*tag_ptr >= p->tag_to_find) {
    369         std::ostringstream oss;
    370         oss << *tag_ptr
    371             << '@'
    372             << class_tag
    373             << " ("
    374             << (kind == JVMTI_HEAP_REFERENCE_FIELD ? "instance, " : "static, ")
    375             << GetPrimitiveTypeName(value_type)
    376             << ", index="
    377             << info->field.index
    378             << ") ";
    379         // Be lazy, always print eight bytes.
    380         static_assert(sizeof(jvalue) == sizeof(uint64_t), "Unexpected jvalue size");
    381         uint64_t val;
    382         memcpy(&val, &value, sizeof(uint64_t));  // To avoid undefined behavior.
    383         oss << android::base::StringPrintf("%016" PRIx64, val);
    384 
    385         if (!p->data.empty()) {
    386           p->data += "\n";
    387         }
    388         p->data += oss.str();
    389         *tag_ptr = *tag_ptr + 1;
    390       }
    391       return 0;
    392     }
    393 
    394     std::string data;
    395     const jlong tag_to_find;
    396   };
    397 
    398   jvmtiHeapCallbacks callbacks;
    399   memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
    400   callbacks.heap_iteration_callback = FindFieldCallbacks::HeapIterationCallback;
    401   callbacks.primitive_field_callback = FindFieldCallbacks::PrimitiveFieldValueCallback;
    402 
    403   FindFieldCallbacks ffc(tag);
    404   jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &ffc);
    405   if (JvmtiErrorToException(env, jvmti_env, ret)) {
    406     return nullptr;
    407   }
    408   return env->NewStringUTF(ffc.data.c_str());
    409 }
    410 
    411 extern "C" JNIEXPORT jboolean JNICALL Java_art_Test906_checkInitialized(
    412     JNIEnv* env, jclass, jclass c) {
    413   jint status;
    414   jvmtiError error = jvmti_env->GetClassStatus(c, &status);
    415   if (JvmtiErrorToException(env, jvmti_env, error)) {
    416     return false;
    417   }
    418   return (status & JVMTI_CLASS_STATUS_INITIALIZED) != 0;
    419 }
    420 
    421 }  // namespace Test906IterateHeap
    422 }  // namespace art
    423