439 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			439 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright (C) 2013 The Android Open Source Project
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| #include <inttypes.h>
 | |
| #include <pthread.h>
 | |
| 
 | |
| #include <cstdio>
 | |
| #include <iomanip>
 | |
| #include <iostream>
 | |
| #include <sstream>
 | |
| #include <vector>
 | |
| 
 | |
| #include "android-base/logging.h"
 | |
| #include "android-base/stringprintf.h"
 | |
| 
 | |
| #include "jni.h"
 | |
| #include "jvmti.h"
 | |
| #include "scoped_primitive_array.h"
 | |
| 
 | |
| // Test infrastructure
 | |
| #include "jvmti_helper.h"
 | |
| #include "test_env.h"
 | |
| #include "ti_macros.h"
 | |
| #include "ti_utf.h"
 | |
| 
 | |
| namespace art {
 | |
| namespace Test906IterateHeap {
 | |
| 
 | |
| class IterationConfig {
 | |
|  public:
 | |
|   IterationConfig() {}
 | |
|   virtual ~IterationConfig() {}
 | |
| 
 | |
|   virtual jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) = 0;
 | |
| };
 | |
| 
 | |
| static jint JNICALL HeapIterationCallback(jlong class_tag,
 | |
|                                           jlong size,
 | |
|                                           jlong* tag_ptr,
 | |
|                                           jint length,
 | |
|                                           void* user_data) {
 | |
|   IterationConfig* config = reinterpret_cast<IterationConfig*>(user_data);
 | |
|   return config->Handle(class_tag, size, tag_ptr, length);
 | |
| }
 | |
| 
 | |
| static bool Run(JNIEnv* env, jint heap_filter, jclass klass_filter, IterationConfig* config) {
 | |
|   jvmtiHeapCallbacks callbacks;
 | |
|   memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
 | |
|   callbacks.heap_iteration_callback = HeapIterationCallback;
 | |
| 
 | |
|   jvmtiError ret = jvmti_env->IterateThroughHeap(heap_filter,
 | |
|                                                  klass_filter,
 | |
|                                                  &callbacks,
 | |
|                                                  config);
 | |
|   if (JvmtiErrorToException(env, jvmti_env, ret)) {
 | |
|     return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateThroughHeapCount(
 | |
|     JNIEnv* env,
 | |
|     jclass klass ATTRIBUTE_UNUSED,
 | |
|     jint heap_filter,
 | |
|     jclass klass_filter,
 | |
|     jint stop_after) {
 | |
|   class CountIterationConfig : public IterationConfig {
 | |
|    public:
 | |
|     CountIterationConfig(jint _counter, jint _stop_after)
 | |
|         : counter(_counter),
 | |
|           stop_after(_stop_after) {
 | |
|     }
 | |
| 
 | |
|     jint Handle(jlong class_tag ATTRIBUTE_UNUSED,
 | |
|                 jlong size ATTRIBUTE_UNUSED,
 | |
|                 jlong* tag_ptr ATTRIBUTE_UNUSED,
 | |
|                 jint length ATTRIBUTE_UNUSED) override {
 | |
|       counter++;
 | |
|       if (counter == stop_after) {
 | |
|         return JVMTI_VISIT_ABORT;
 | |
|       }
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     jint counter;
 | |
|     const jint stop_after;
 | |
|   };
 | |
| 
 | |
|   CountIterationConfig config(0, stop_after);
 | |
|   Run(env, heap_filter, klass_filter, &config);
 | |
| 
 | |
|   if (config.counter > config.stop_after) {
 | |
|     printf("Error: more objects visited than signaled.");
 | |
|   }
 | |
| 
 | |
|   return config.counter;
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateThroughHeapData(
 | |
|     JNIEnv* env,
 | |
|     jclass klass ATTRIBUTE_UNUSED,
 | |
|     jint heap_filter,
 | |
|     jclass klass_filter,
 | |
|     jlongArray class_tags,
 | |
|     jlongArray sizes,
 | |
|     jlongArray tags,
 | |
|     jintArray lengths) {
 | |
|   class DataIterationConfig : public IterationConfig {
 | |
|    public:
 | |
|     jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) override {
 | |
|       class_tags_.push_back(class_tag);
 | |
|       sizes_.push_back(size);
 | |
|       tags_.push_back(*tag_ptr);
 | |
|       lengths_.push_back(length);
 | |
| 
 | |
|       return 0;  // Continue.
 | |
|     }
 | |
| 
 | |
|     std::vector<jlong> class_tags_;
 | |
|     std::vector<jlong> sizes_;
 | |
|     std::vector<jlong> tags_;
 | |
|     std::vector<jint> lengths_;
 | |
|   };
 | |
| 
 | |
|   DataIterationConfig config;
 | |
|   if (!Run(env, heap_filter, klass_filter, &config)) {
 | |
|     return -1;
 | |
|   }
 | |
| 
 | |
|   ScopedLongArrayRW s_class_tags(env, class_tags);
 | |
|   ScopedLongArrayRW s_sizes(env, sizes);
 | |
|   ScopedLongArrayRW s_tags(env, tags);
 | |
|   ScopedIntArrayRW s_lengths(env, lengths);
 | |
| 
 | |
|   for (size_t i = 0; i != config.class_tags_.size(); ++i) {
 | |
|     s_class_tags[i] = config.class_tags_[i];
 | |
|     s_sizes[i] = config.sizes_[i];
 | |
|     s_tags[i] = config.tags_[i];
 | |
|     s_lengths[i] = config.lengths_[i];
 | |
|   }
 | |
| 
 | |
|   return static_cast<jint>(config.class_tags_.size());
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT void JNICALL Java_art_Test906_iterateThroughHeapAdd(
 | |
|     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint heap_filter, jclass klass_filter) {
 | |
|   class AddIterationConfig : public IterationConfig {
 | |
|    public:
 | |
|     AddIterationConfig() {}
 | |
| 
 | |
|     jint Handle(jlong class_tag ATTRIBUTE_UNUSED,
 | |
|                 jlong size ATTRIBUTE_UNUSED,
 | |
|                 jlong* tag_ptr,
 | |
|                 jint length ATTRIBUTE_UNUSED) override {
 | |
|       jlong current_tag = *tag_ptr;
 | |
|       if (current_tag != 0) {
 | |
|         *tag_ptr = current_tag + 10;
 | |
|       }
 | |
|       return 0;
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   AddIterationConfig config;
 | |
|   Run(env, heap_filter, klass_filter, &config);
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapString(
 | |
|     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) {
 | |
|   struct FindStringCallbacks {
 | |
|     explicit FindStringCallbacks(jlong t) : tag_to_find(t) {}
 | |
| 
 | |
|     static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED,
 | |
|                                               jlong size ATTRIBUTE_UNUSED,
 | |
|                                               jlong* tag_ptr ATTRIBUTE_UNUSED,
 | |
|                                               jint length ATTRIBUTE_UNUSED,
 | |
|                                               void* user_data ATTRIBUTE_UNUSED) {
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     static jint JNICALL StringValueCallback(jlong class_tag,
 | |
|                                             jlong size,
 | |
|                                             jlong* tag_ptr,
 | |
|                                             const jchar* value,
 | |
|                                             jint value_length,
 | |
|                                             void* user_data) {
 | |
|       FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data);
 | |
|       if (*tag_ptr == p->tag_to_find) {
 | |
|         size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length);
 | |
|         std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]);
 | |
|         memset(mod_utf.get(), 0, utf_byte_count + 1);
 | |
|         ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
 | |
|         if (!p->data.empty()) {
 | |
|           p->data += "\n";
 | |
|         }
 | |
|         p->data += android::base::StringPrintf("%" PRId64 "@%" PRId64 " (%" PRId64 ", '%s')",
 | |
|                                                *tag_ptr,
 | |
|                                                class_tag,
 | |
|                                                size,
 | |
|                                                mod_utf.get());
 | |
|         // Update the tag to test whether that works.
 | |
|         *tag_ptr = *tag_ptr + 1;
 | |
|       }
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     std::string data;
 | |
|     const jlong tag_to_find;
 | |
|   };
 | |
| 
 | |
|   jvmtiHeapCallbacks callbacks;
 | |
|   memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
 | |
|   callbacks.heap_iteration_callback = FindStringCallbacks::HeapIterationCallback;
 | |
|   callbacks.string_primitive_value_callback = FindStringCallbacks::StringValueCallback;
 | |
| 
 | |
|   FindStringCallbacks fsc(tag);
 | |
|   jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fsc);
 | |
|   if (JvmtiErrorToException(env, jvmti_env, ret)) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return env->NewStringUTF(fsc.data.c_str());
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapPrimitiveArray(
 | |
|     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) {
 | |
|   struct FindArrayCallbacks {
 | |
|     explicit FindArrayCallbacks(jlong t) : tag_to_find(t) {}
 | |
| 
 | |
|     static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED,
 | |
|                                               jlong size ATTRIBUTE_UNUSED,
 | |
|                                               jlong* tag_ptr ATTRIBUTE_UNUSED,
 | |
|                                               jint length ATTRIBUTE_UNUSED,
 | |
|                                               void* user_data ATTRIBUTE_UNUSED) {
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     static jint JNICALL ArrayValueCallback(jlong class_tag,
 | |
|                                            jlong size,
 | |
|                                            jlong* tag_ptr,
 | |
|                                            jint element_count,
 | |
|                                            jvmtiPrimitiveType element_type,
 | |
|                                            const void* elements,
 | |
|                                            void* user_data) {
 | |
|       FindArrayCallbacks* p = reinterpret_cast<FindArrayCallbacks*>(user_data);
 | |
|       if (*tag_ptr == p->tag_to_find) {
 | |
|         std::ostringstream oss;
 | |
|         oss << *tag_ptr
 | |
|             << '@'
 | |
|             << class_tag
 | |
|             << " ("
 | |
|             << size
 | |
|             << ", "
 | |
|             << element_count
 | |
|             << "x"
 | |
|             << static_cast<char>(element_type)
 | |
|             << " '";
 | |
|         size_t element_size;
 | |
|         switch (element_type) {
 | |
|           case JVMTI_PRIMITIVE_TYPE_BOOLEAN:
 | |
|           case JVMTI_PRIMITIVE_TYPE_BYTE:
 | |
|             element_size = 1;
 | |
|             break;
 | |
|           case JVMTI_PRIMITIVE_TYPE_CHAR:
 | |
|           case JVMTI_PRIMITIVE_TYPE_SHORT:
 | |
|             element_size = 2;
 | |
|             break;
 | |
|           case JVMTI_PRIMITIVE_TYPE_INT:
 | |
|           case JVMTI_PRIMITIVE_TYPE_FLOAT:
 | |
|             element_size = 4;
 | |
|             break;
 | |
|           case JVMTI_PRIMITIVE_TYPE_LONG:
 | |
|           case JVMTI_PRIMITIVE_TYPE_DOUBLE:
 | |
|             element_size = 8;
 | |
|             break;
 | |
|           default:
 | |
|             LOG(FATAL) << "Unknown type " << static_cast<size_t>(element_type);
 | |
|             UNREACHABLE();
 | |
|         }
 | |
|         const uint8_t* data = reinterpret_cast<const uint8_t*>(elements);
 | |
|         for (size_t i = 0; i != element_size * element_count; ++i) {
 | |
|           oss << android::base::StringPrintf("%02x", data[i]);
 | |
|         }
 | |
|         oss << "')";
 | |
| 
 | |
|         if (!p->data.empty()) {
 | |
|           p->data += "\n";
 | |
|         }
 | |
|         p->data += oss.str();
 | |
|         // Update the tag to test whether that works.
 | |
|         *tag_ptr = *tag_ptr + 1;
 | |
|       }
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     std::string data;
 | |
|     const jlong tag_to_find;
 | |
|   };
 | |
| 
 | |
|   jvmtiHeapCallbacks callbacks;
 | |
|   memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
 | |
|   callbacks.heap_iteration_callback = FindArrayCallbacks::HeapIterationCallback;
 | |
|   callbacks.array_primitive_value_callback = FindArrayCallbacks::ArrayValueCallback;
 | |
| 
 | |
|   FindArrayCallbacks fac(tag);
 | |
|   jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fac);
 | |
|   if (JvmtiErrorToException(env, jvmti_env, ret)) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return env->NewStringUTF(fac.data.c_str());
 | |
| }
 | |
| 
 | |
| static constexpr const char* GetPrimitiveTypeName(jvmtiPrimitiveType type) {
 | |
|   switch (type) {
 | |
|     case JVMTI_PRIMITIVE_TYPE_BOOLEAN:
 | |
|       return "boolean";
 | |
|     case JVMTI_PRIMITIVE_TYPE_BYTE:
 | |
|       return "byte";
 | |
|     case JVMTI_PRIMITIVE_TYPE_CHAR:
 | |
|       return "char";
 | |
|     case JVMTI_PRIMITIVE_TYPE_SHORT:
 | |
|       return "short";
 | |
|     case JVMTI_PRIMITIVE_TYPE_INT:
 | |
|       return "int";
 | |
|     case JVMTI_PRIMITIVE_TYPE_FLOAT:
 | |
|       return "float";
 | |
|     case JVMTI_PRIMITIVE_TYPE_LONG:
 | |
|       return "long";
 | |
|     case JVMTI_PRIMITIVE_TYPE_DOUBLE:
 | |
|       return "double";
 | |
|   }
 | |
|   LOG(FATAL) << "Unknown type " << static_cast<size_t>(type);
 | |
|   UNREACHABLE();
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT jstring JNICALL Java_art_Test906_iterateThroughHeapPrimitiveFields(
 | |
|     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jlong tag) {
 | |
|   struct FindFieldCallbacks {
 | |
|     explicit FindFieldCallbacks(jlong t) : tag_to_find(t) {}
 | |
| 
 | |
|     static jint JNICALL HeapIterationCallback(jlong class_tag ATTRIBUTE_UNUSED,
 | |
|                                               jlong size ATTRIBUTE_UNUSED,
 | |
|                                               jlong* tag_ptr ATTRIBUTE_UNUSED,
 | |
|                                               jint length ATTRIBUTE_UNUSED,
 | |
|                                               void* user_data ATTRIBUTE_UNUSED) {
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     static jint JNICALL PrimitiveFieldValueCallback(jvmtiHeapReferenceKind kind,
 | |
|                                                     const jvmtiHeapReferenceInfo* info,
 | |
|                                                     jlong class_tag,
 | |
|                                                     jlong* tag_ptr,
 | |
|                                                     jvalue value,
 | |
|                                                     jvmtiPrimitiveType value_type,
 | |
|                                                     void* user_data) {
 | |
|       FindFieldCallbacks* p = reinterpret_cast<FindFieldCallbacks*>(user_data);
 | |
|       if (*tag_ptr >= p->tag_to_find) {
 | |
|         std::ostringstream oss;
 | |
|         oss << *tag_ptr
 | |
|             << '@'
 | |
|             << class_tag
 | |
|             << " ("
 | |
|             << (kind == JVMTI_HEAP_REFERENCE_FIELD ? "instance, " : "static, ")
 | |
|             << GetPrimitiveTypeName(value_type)
 | |
|             << ", index="
 | |
|             << info->field.index
 | |
|             << ") ";
 | |
|         // Be lazy, always print eight bytes.
 | |
|         static_assert(sizeof(jvalue) == sizeof(uint64_t), "Unexpected jvalue size");
 | |
|         uint64_t val;
 | |
|         memcpy(&val, &value, sizeof(uint64_t));  // To avoid undefined behavior.
 | |
|         oss << android::base::StringPrintf("%016" PRIx64, val);
 | |
| 
 | |
|         if (!p->data.empty()) {
 | |
|           p->data += "\n";
 | |
|         }
 | |
|         p->data += oss.str();
 | |
|         *tag_ptr = *tag_ptr + 1;
 | |
|       }
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|     std::string data;
 | |
|     const jlong tag_to_find;
 | |
|   };
 | |
| 
 | |
|   jvmtiHeapCallbacks callbacks;
 | |
|   memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
 | |
|   callbacks.heap_iteration_callback = FindFieldCallbacks::HeapIterationCallback;
 | |
|   callbacks.primitive_field_callback = FindFieldCallbacks::PrimitiveFieldValueCallback;
 | |
| 
 | |
|   FindFieldCallbacks ffc(tag);
 | |
|   jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &ffc);
 | |
|   if (JvmtiErrorToException(env, jvmti_env, ret)) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return env->NewStringUTF(ffc.data.c_str());
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT jboolean JNICALL Java_art_Test906_checkInitialized(
 | |
|     JNIEnv* env, jclass, jclass c) {
 | |
|   jint status;
 | |
|   jvmtiError error = jvmti_env->GetClassStatus(c, &status);
 | |
|   if (JvmtiErrorToException(env, jvmti_env, error)) {
 | |
|     return false;
 | |
|   }
 | |
|   return (status & JVMTI_CLASS_STATUS_INITIALIZED) != 0;
 | |
| }
 | |
| 
 | |
| extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateOverInstancesCount(
 | |
|     JNIEnv* env, jclass, jclass target) {
 | |
|   jint cnt = 0;
 | |
|   auto count_func = [](jlong, jlong, jlong*, void* user_data) -> jvmtiIterationControl {
 | |
|     *reinterpret_cast<jint*>(user_data) += 1;
 | |
|     return JVMTI_ITERATION_CONTINUE;
 | |
|   };
 | |
|   JvmtiErrorToException(env,
 | |
|                         jvmti_env,
 | |
|                         jvmti_env->IterateOverInstancesOfClass(target,
 | |
|                                                                JVMTI_HEAP_OBJECT_EITHER,
 | |
|                                                                count_func,
 | |
|                                                                &cnt));
 | |
|   return cnt;
 | |
| }
 | |
| 
 | |
| }  // namespace Test906IterateHeap
 | |
| }  // namespace art
 |