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 #include <pthread.h>
19 
20 #include <cstdio>
21 #include <iomanip>
22 #include <iostream>
23 #include <sstream>
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:
IterationConfig()44   IterationConfig() {}
~IterationConfig()45   virtual ~IterationConfig() {}
46 
47   virtual jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) = 0;
48 };
49 
HeapIterationCallback(jlong class_tag,jlong size,jlong * tag_ptr,jint length,void * user_data)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 
Run(JNIEnv * env,jint heap_filter,jclass klass_filter,IterationConfig * config)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 
Java_art_Test906_iterateThroughHeapCount(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jint heap_filter,jclass klass_filter,jint stop_after)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 
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)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 
Java_art_Test906_iterateThroughHeapAdd(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jint heap_filter,jclass klass_filter)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 
Java_art_Test906_iterateThroughHeapString(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jlong tag)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 
Java_art_Test906_iterateThroughHeapPrimitiveArray(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jlong tag)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 
GetPrimitiveTypeName(jvmtiPrimitiveType type)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 
Java_art_Test906_iterateThroughHeapPrimitiveFields(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jlong tag)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 
Java_art_Test906_checkInitialized(JNIEnv * env,jclass,jclass c)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 
Java_art_Test906_iterateOverInstancesCount(JNIEnv * env,jclass,jclass target)421 extern "C" JNIEXPORT jint JNICALL Java_art_Test906_iterateOverInstancesCount(
422     JNIEnv* env, jclass, jclass target) {
423   jint cnt = 0;
424   auto count_func = [](jlong, jlong, jlong*, void* user_data) -> jvmtiIterationControl {
425     *reinterpret_cast<jint*>(user_data) += 1;
426     return JVMTI_ITERATION_CONTINUE;
427   };
428   JvmtiErrorToException(env,
429                         jvmti_env,
430                         jvmti_env->IterateOverInstancesOfClass(target,
431                                                                JVMTI_HEAP_OBJECT_EITHER,
432                                                                count_func,
433                                                                &cnt));
434   return cnt;
435 }
436 
437 }  // namespace Test906IterateHeap
438 }  // namespace art
439