/*
* 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,
[[maybe_unused]] jclass klass,
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([[maybe_unused]] jlong class_tag,
[[maybe_unused]] jlong size,
[[maybe_unused]] jlong* tag_ptr,
[[maybe_unused]] jint length) 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,
[[maybe_unused]] jclass klass,
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, [[maybe_unused]] jclass klass, jint heap_filter, jclass klass_filter) {
class AddIterationConfig : public IterationConfig {
public :
AddIterationConfig() {}
jint Handle([[maybe_unused]] jlong class_tag,
[[maybe_unused]] jlong size,
jlong* tag_ptr,
[[maybe_unused]] jint length) 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, [[maybe_unused]] jclass klass, jlong tag) {
struct FindStringCallbacks {
explicit FindStringCallbacks(jlong t) : tag_to_find(t) {}
static jint JNICALL HeapIterationCallback([[maybe_unused]] jlong class_tag,
[[maybe_unused]] jlong size,
[[maybe_unused]] jlong* tag_ptr,
[[maybe_unused]] jint length,
[[maybe_unused]] void * user_data) {
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::CountModifiedUtf8BytesInUtf16(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, [[maybe_unused]] jclass klass, jlong tag) {
struct FindArrayCallbacks {
explicit FindArrayCallbacks(jlong t) : tag_to_find(t) {}
static jint JNICALL HeapIterationCallback([[maybe_unused]] jlong class_tag,
[[maybe_unused]] jlong size,
[[maybe_unused]] jlong* tag_ptr,
[[maybe_unused]] jint length,
[[maybe_unused]] void * user_data) {
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 ;
}
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, [[maybe_unused]] jclass klass, jlong tag) {
struct FindFieldCallbacks {
explicit FindFieldCallbacks(jlong t) : tag_to_find(t) {}
static jint JNICALL HeapIterationCallback([[maybe_unused]] jlong class_tag,
[[maybe_unused]] jlong size,
[[maybe_unused]] jlong* tag_ptr,
[[maybe_unused]] jint length,
[[maybe_unused]] void * user_data) {
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
Messung V0.5 in Prozent C=87 H=92 G=89
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-29)
¤
*© Formatika GbR, Deutschland