1 /*
2 * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24
25 #include <assert.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include "jvmti.h"
30
31 extern "C" {
32
33 #define TRUE 1
34 #define FALSE 0
35 #define PRINT_OUT 0
36
37 static jvmtiEnv *jvmti = nullptr;
38 static jvmtiEnv *second_jvmti = nullptr;
39
40 typedef struct _ObjectTrace{
41 jweak object;
42 jlong size;
43 jvmtiFrameInfo* frames;
44 size_t frame_count;
45 jthread thread;
46 } ObjectTrace;
47
48 typedef struct _EventStorage {
49 int live_object_additions;
50 int live_object_size;
51 int live_object_count;
52 ObjectTrace** live_objects;
53
54 int garbage_history_size;
55 int garbage_history_index;
56 ObjectTrace** garbage_collected_objects;
57
58 // Two separate monitors to separate storage data race and the compaction field
59 // data race.
60 jrawMonitorID storage_monitor;
61
62 int compaction_required;
63 jrawMonitorID compaction_monitor;
64 } EventStorage;
65
66 typedef struct _ExpectedContentFrame {
67 const char *name;
68 const char *signature;
69 const char *file_name;
70 int line_number;
71 } ExpectedContentFrame;
72
73 static
74 void event_storage_lock(EventStorage* storage) {
75 jvmti->RawMonitorEnter(storage->storage_monitor);
76 }
77
78 static
79 void event_storage_unlock(EventStorage* storage) {
80 jvmti->RawMonitorExit(storage->storage_monitor);
81 }
82
83 static
84 void event_storage_lock_compaction(EventStorage* storage) {
85 jvmti->RawMonitorEnter(storage->compaction_monitor);
86 }
87
88 static
89 void event_storage_unlock_compaction(EventStorage* storage) {
90 jvmti->RawMonitorExit(storage->compaction_monitor);
91 }
92
93 // Given a method and a location, this method gets the line number.
94 static
95 jint get_line_number(jmethodID method, jlocation location) {
96 // Read the line number table.
97 jvmtiLineNumberEntry *table_ptr = 0;
98 jint line_number_table_entries;
99 int l;
100 jlocation last_location;
101 int jvmti_error = jvmti->GetLineNumberTable(method,
102 &line_number_table_entries,
103 &table_ptr);
104
105 if (JVMTI_ERROR_NONE != jvmti_error) {
106 return -1;
107 }
108 if (line_number_table_entries <= 0) {
109 return -1;
110 }
111 if (line_number_table_entries == 1) {
112 return table_ptr[0].line_number;
113 }
114
115 // Go through all the line numbers...
116 last_location = table_ptr[0].start_location;
117 for (l = 1; l < line_number_table_entries; l++) {
118 // ... and if you see one that is in the right place for your
119 // location, you've found the line number!
120 if ((location < table_ptr[l].start_location) &&
121 (location >= last_location)) {
122 return table_ptr[l - 1].line_number;
123 }
124 last_location = table_ptr[l].start_location;
125 }
126
127 if (location >= last_location) {
128 return table_ptr[line_number_table_entries - 1].line_number;
129 } else {
130 return -1;
131 }
132 }
133
134 static void print_out_frames(JNIEnv* env, ObjectTrace* trace) {
135 jvmtiFrameInfo* frames = trace->frames;
136 size_t i;
137 for (i = 0; i < trace->frame_count; i++) {
138 // Get basic information out of the trace.
139 jlocation bci = frames[i].location;
140 jmethodID methodid = frames[i].method;
141 char *name = nullptr, *signature = nullptr, *file_name = nullptr;
142 jclass declaring_class;
143 int line_number;
144 jvmtiError err;
145
146 if (bci < 0) {
147 fprintf(stderr, "\tNative frame\n");
148 continue;
149 }
150
151 // Transform into usable information.
152 line_number = get_line_number(methodid, bci);
153 if (JVMTI_ERROR_NONE != jvmti->GetMethodName(methodid, &name, &signature, 0)) {
154 fprintf(stderr, "\tUnknown method name\n");
155 continue;
156 }
157
158 if (JVMTI_ERROR_NONE != jvmti->GetMethodDeclaringClass(methodid, &declaring_class)) {
159 fprintf(stderr, "\tUnknown class\n");
160 continue;
161 }
162
163 err = jvmti->GetSourceFileName(declaring_class, &file_name);
164 if (err != JVMTI_ERROR_NONE) {
165 fprintf(stderr, "\tUnknown file\n");
166 continue;
167 }
168
169 // Compare now, none should be null.
170 if (name == nullptr) {
171 fprintf(stderr, "\tUnknown name\n");
172 continue;
173 }
174
175 if (file_name == nullptr) {
176 fprintf(stderr, "\tUnknown file\n");
177 continue;
178 }
179
180 if (signature == nullptr) {
181 fprintf(stderr, "\tUnknown signature\n");
182 continue;
183 }
184
185 fprintf(stderr, "\t%s%s (%s: %d)\n", name, signature, file_name, line_number);
186 }
187 }
188
189 static jboolean check_sample_content(JNIEnv* env,
190 ObjectTrace* trace,
191 ExpectedContentFrame *expected,
192 size_t expected_count,
193 jboolean check_lines,
194 int print_out_comparisons) {
195 jvmtiFrameInfo* frames;
196 size_t i;
197
198 if (expected_count > trace->frame_count) {
199 return FALSE;
200 }
201
202 frames = trace->frames;
203 for (i = 0; i < expected_count; i++) {
204 // Get basic information out of the trace.
205 jlocation bci = frames[i].location;
206 jmethodID methodid = frames[i].method;
207 char *name = nullptr, *signature = nullptr, *file_name = nullptr;
208 jclass declaring_class;
209 int line_number;
210 jboolean differ;
211 jvmtiError err;
212
213 if (bci < 0 && expected[i].line_number != -1) {
214 return FALSE;
215 }
216
217 // Transform into usable information.
218 line_number = get_line_number(methodid, bci);
219 jvmti->GetMethodName(methodid, &name, &signature, 0);
220
221 if (JVMTI_ERROR_NONE != jvmti->GetMethodDeclaringClass(methodid, &declaring_class)) {
222 return FALSE;
223 }
224
225 err = jvmti->GetSourceFileName(declaring_class, &file_name);
226 if (err != JVMTI_ERROR_NONE) {
227 return FALSE;
228 }
229
230 // Compare now, none should be null.
231 if (name == nullptr) {
232 return FALSE;
233 }
234
235 if (file_name == nullptr) {
236 return FALSE;
237 }
238
239 if (signature == nullptr) {
240 return FALSE;
241 }
242
243 differ = (strcmp(name, expected[i].name) ||
244 strcmp(signature, expected[i].signature) ||
245 strcmp(file_name, expected[i].file_name) ||
246 (check_lines && line_number != expected[i].line_number));
247
248 if (print_out_comparisons) {
249 fprintf(stderr, "\tComparing: (check_lines: %d)\n", check_lines);
250 fprintf(stderr, "\t\tNames: %s and %s\n", name, expected[i].name);
251 fprintf(stderr, "\t\tSignatures: %s and %s\n", signature, expected[i].signature);
252 fprintf(stderr, "\t\tFile name: %s and %s\n", file_name, expected[i].file_name);
253 fprintf(stderr, "\t\tLines: %d and %d\n", line_number, expected[i].line_number);
254 fprintf(stderr, "\t\tResult is %d\n", differ);
255 }
256
257 if (differ) {
258 return FALSE;
259 }
260 }
261
262 return TRUE;
263 }
264
265 // Static native API for various tests.
266 static int fill_native_frames(JNIEnv* env, jobjectArray frames,
267 ExpectedContentFrame* native_frames, size_t size) {
268 size_t i;
269 for (i = 0; i < size; i++) {
270 jclass frame_class = nullptr;
271 jfieldID line_number_field_id = 0;
272 int line_number = 0;
273 jfieldID string_id = 0;
274 jstring string_object = nullptr;
275 const char* method = nullptr;
276 const char* file_name = nullptr;
277 const char* signature = nullptr;
278
279 jobject obj = env->GetObjectArrayElement(frames, (jsize) i);
280
281 if (env->ExceptionOccurred()) {
282 fprintf(stderr, "fill_native_frames: Exception in jni GetObjectArrayElement\n");
283 return -1;
284 }
285
286 frame_class = env->GetObjectClass(obj);
287
288 if (env->ExceptionOccurred()) {
289 fprintf(stderr, "fill_native_frames: Exception in jni GetObjectClass\n");
290 return -1;
291 }
292
293 line_number_field_id = env->GetFieldID(frame_class, "lineNumber", "I");
294
295 if (env->ExceptionOccurred()) {
296 fprintf(stderr, "fill_native_frames: Exception in jni GetFieldID\n");
297 return -1;
298 }
299
300 line_number = env->GetIntField(obj, line_number_field_id);
301
302 if (env->ExceptionOccurred()) {
303 fprintf(stderr, "fill_native_frames: Exception in jni GetIntField\n");
304 return -1;
305 }
306
307 string_id = env->GetFieldID(frame_class, "method", "Ljava/lang/String;");
308
309 if (env->ExceptionOccurred()) {
310 fprintf(stderr, "fill_native_frames: Exception in jni GetFieldID\n");
311 return -1;
312 }
313
314 string_object = (jstring) env->GetObjectField(obj, string_id);
315
316 if (env->ExceptionOccurred()) {
317 fprintf(stderr, "fill_native_frames: Exception in jni GetObjectField\n");
318 return -1;
319 }
320
321 method = env->GetStringUTFChars(string_object, 0);
322
323 if (env->ExceptionOccurred()) {
324 fprintf(stderr, "Exception in jni GetStringUTFChars\n");
325 return -1;
326 }
327
328 string_id = env->GetFieldID(frame_class, "fileName", "Ljava/lang/String;");
329
330 if (env->ExceptionOccurred()) {
331 fprintf(stderr, "Exception in jni GetFieldID\n");
332 return -1;
333 }
334
335 string_object = (jstring) (env->GetObjectField(obj, string_id));
336
337 if (env->ExceptionOccurred()) {
338 fprintf(stderr, "fill_native_frames: Exception in second jni GetObjectField\n");
339 return -1;
340 }
341
342 file_name = env->GetStringUTFChars(string_object, 0);
343
344 if (env->ExceptionOccurred()) {
345 fprintf(stderr, "fill_native_frames: Exception in jni GetStringUTFChars\n");
346 return -1;
347 }
348
349 string_id = env->GetFieldID(frame_class, "signature", "Ljava/lang/String;");
350
351 if (env->ExceptionOccurred()) {
352 fprintf(stderr, "fill_native_frames: Exception in second jni GetFieldID\n");
353 return -1;
354 }
355
356 string_object = (jstring) (env->GetObjectField(obj, string_id));
357
358 if (env->ExceptionOccurred()) {
359 fprintf(stderr, "fill_native_frames: Exception in third jni GetObjectField\n");
360 return -1;
361 }
362
363 signature = env->GetStringUTFChars(string_object, 0);
364
365 if (env->ExceptionOccurred()) {
366 fprintf(stderr, "fill_native_frames: Exception in jni GetStringUTFChars\n");
367 return -1;
368 }
369
370 native_frames[i].name = method;
371 native_frames[i].file_name = file_name;
372 native_frames[i].signature = signature;
373 native_frames[i].line_number = line_number;
374 }
375
376 return 0;
377 }
378
379 // Internal storage system implementation.
380 static EventStorage global_event_storage;
381 static EventStorage second_global_event_storage;
382
383 static void event_storage_set_compaction_required(EventStorage* storage) {
384 event_storage_lock_compaction(storage);
385 storage->compaction_required = 1;
386 event_storage_unlock_compaction(storage);
387 }
388
389 static int event_storage_get_compaction_required(EventStorage* storage) {
390 int result;
391 event_storage_lock_compaction(storage);
392 result = storage->compaction_required;
393 event_storage_unlock_compaction(storage);
394 return result;
395 }
396
397 static void event_storage_set_garbage_history(EventStorage* storage, int value) {
398 size_t size;
399 event_storage_lock(storage);
400 global_event_storage.garbage_history_size = value;
401 free(global_event_storage.garbage_collected_objects);
402 size = sizeof(*global_event_storage.garbage_collected_objects) * value;
403 global_event_storage.garbage_collected_objects = reinterpret_cast<ObjectTrace**>(malloc(size));
404 memset(global_event_storage.garbage_collected_objects, 0, size);
405 event_storage_unlock(storage);
406 }
407
408 // No mutex here, it is handled by the caller.
409 static void event_storage_add_garbage_collected_object(EventStorage* storage,
410 ObjectTrace* object) {
411 int idx = storage->garbage_history_index;
412 ObjectTrace* old_object = storage->garbage_collected_objects[idx];
413 if (old_object != nullptr) {
414 free(old_object->frames);
415 free(storage->garbage_collected_objects[idx]);
416 }
417
418 storage->garbage_collected_objects[idx] = object;
419 storage->garbage_history_index = (idx + 1) % storage->garbage_history_size;
420 }
421
422 static int event_storage_get_count(EventStorage* storage) {
423 int result;
424 event_storage_lock(storage);
425 result = storage->live_object_count;
426 event_storage_unlock(storage);
427 return result;
428 }
429
430 static double event_storage_get_average_size(EventStorage* storage) {
431 double accumulation = 0;
432 int max_size;
433 int i;
434
435 event_storage_lock(storage);
436 max_size = storage->live_object_count;
437
438 for (i = 0; i < max_size; i++) {
439 accumulation += storage->live_objects[i]->size;
440 }
441
442 event_storage_unlock(storage);
443 return accumulation / max_size;
444 }
445
446 static jboolean event_storage_contains(JNIEnv* env,
447 EventStorage* storage,
448 ExpectedContentFrame* frames,
449 size_t size,
450 jboolean check_lines) {
451 int i;
452 event_storage_lock(storage);
453 fprintf(stderr, "Checking storage count %d\n", storage->live_object_count);
454 for (i = 0; i < storage->live_object_count; i++) {
455 ObjectTrace* trace = storage->live_objects[i];
456
457 if (check_sample_content(env, trace, frames, size, check_lines, PRINT_OUT)) {
458 event_storage_unlock(storage);
459 return TRUE;
460 }
461 }
462 event_storage_unlock(storage);
463 return FALSE;
464 }
465
466 static jlong event_storage_get_size(JNIEnv* env,
467 EventStorage* storage,
468 ExpectedContentFrame* frames,
469 size_t size,
470 jboolean check_lines) {
471 int i;
472 event_storage_lock(storage);
473 fprintf(stderr, "Getting element from storage count, size %d\n", storage->live_object_count);
474 for (i = 0; i < storage->live_object_count; i++) {
475 ObjectTrace* trace = storage->live_objects[i];
476
477 if (check_sample_content(env, trace, frames, size, check_lines, PRINT_OUT)) {
478 jlong result = trace->size;
479 event_storage_unlock(storage);
480 return result;
481 }
482 }
483 event_storage_unlock(storage);
484 return 0;
485 }
486
487 static jboolean event_storage_garbage_contains(JNIEnv* env,
488 EventStorage* storage,
489 ExpectedContentFrame* frames,
490 size_t size,
491 jboolean check_lines) {
492 int i;
493 event_storage_lock(storage);
494 fprintf(stderr, "Checking garbage storage count %d\n",
495 storage->garbage_history_size);
496 for (i = 0; i < storage->garbage_history_size; i++) {
497 ObjectTrace* trace = storage->garbage_collected_objects[i];
498
499 if (trace == nullptr) {
500 continue;
501 }
502
503 if (check_sample_content(env, trace, frames, size, check_lines, PRINT_OUT)) {
504 event_storage_unlock(storage);
505 return TRUE;
506 }
507 }
508 event_storage_unlock(storage);
509 return FALSE;
510 }
511
512 // No mutex here, handled by the caller.
513 static void event_storage_augment_storage(EventStorage* storage) {
514 int new_max = (storage->live_object_size * 2) + 1;
515 ObjectTrace** new_objects = reinterpret_cast<ObjectTrace**>(malloc(new_max * sizeof(*new_objects)));
516
517 int current_count = storage->live_object_count;
518 if (storage->live_objects != nullptr) {
519 memcpy(new_objects, storage->live_objects, current_count * sizeof(*new_objects));
520 }
521 free(storage->live_objects);
522 storage->live_objects = new_objects;
523 storage->live_object_size = new_max;
524 }
525
526 static void event_storage_add(EventStorage* storage,
527 JNIEnv* jni,
528 jthread thread,
529 jobject object,
530 jclass klass,
531 jlong size) {
532 jvmtiFrameInfo frames[64];
533 jint count;
534 jvmtiError err;
535
536 err = jvmti->GetStackTrace(thread, 0, 64, frames, &count);
537 if (err == JVMTI_ERROR_NONE && count >= 1) {
538 ObjectTrace* live_object;
539 jvmtiFrameInfo* allocated_frames = (jvmtiFrameInfo*) malloc(count * sizeof(*allocated_frames));
540 memcpy(allocated_frames, frames, count * sizeof(*allocated_frames));
541
542 live_object = (ObjectTrace*) malloc(sizeof(*live_object));
543 live_object->frames = allocated_frames;
544 live_object->frame_count = count;
545 live_object->size = size;
546 live_object->thread = thread;
547 live_object->object = jni->NewWeakGlobalRef(object);
548
549 if (jni->ExceptionOccurred()) {
550 jni->FatalError("Error in event_storage_add: Exception in jni NewWeakGlobalRef");
551 }
552
553 // Only now lock and get things done quickly.
554 event_storage_lock(storage);
555
556 storage->live_object_additions++;
557
558 if (storage->live_object_count >= storage->live_object_size) {
559 event_storage_augment_storage(storage);
560 }
561 assert(storage->live_object_count < storage->live_object_size);
562
563 if (PRINT_OUT) {
564 fprintf(stderr, "Adding trace for thread %p, frame_count %d, storage %p\n",
565 thread, count, storage);
566 print_out_frames(jni, live_object);
567 }
568 storage->live_objects[storage->live_object_count] = live_object;
569 storage->live_object_count++;
570
571 event_storage_unlock(storage);
572 }
573 }
574
575 static void event_storage_compact(EventStorage* storage, JNIEnv* jni) {
576 int max, i, dest;
577 ObjectTrace** live_objects;
578
579 event_storage_lock_compaction(storage);
580 storage->compaction_required = 0;
581 event_storage_unlock_compaction(storage);
582
583 event_storage_lock(storage);
584
585 max = storage->live_object_count;
586 live_objects = storage->live_objects;
587
588 for (i = 0, dest = 0; i < max; i++) {
589 ObjectTrace* live_object = live_objects[i];
590 jweak object = live_object->object;
591
592 if (!jni->IsSameObject(object, nullptr)) {
593 if (dest != i) {
594 live_objects[dest] = live_object;
595 dest++;
596 }
597 } else {
598 jni->DeleteWeakGlobalRef(object);
599 live_object->object = nullptr;
600
601 event_storage_add_garbage_collected_object(storage, live_object);
602 }
603 }
604
605 storage->live_object_count = dest;
606 event_storage_unlock(storage);
607 }
608
609 static void event_storage_free_objects(ObjectTrace** array, int max) {
610 int i;
611 for (i = 0; i < max; i++) {
612 free(array[i]), array[i] = nullptr;
613 }
614 }
615
616 static void event_storage_reset(EventStorage* storage) {
617 event_storage_lock(storage);
618
619 // Reset everything except the mutex and the garbage collection.
620 event_storage_free_objects(storage->live_objects,
621 storage->live_object_count);
622 storage->live_object_additions = 0;
623 storage->live_object_size = 0;
624 storage->live_object_count = 0;
625 free(storage->live_objects), storage->live_objects = nullptr;
626
627 event_storage_free_objects(storage->garbage_collected_objects,
628 storage->garbage_history_size);
629
630 storage->compaction_required = 0;
631 storage->garbage_history_index = 0;
632
633 event_storage_unlock(storage);
634 }
635
636 static int event_storage_number_additions(EventStorage* storage) {
637 int result;
638 event_storage_lock(storage);
639 result = storage->live_object_additions;
640 event_storage_unlock(storage);
641 return result;
642 }
643
644 // Start of the JVMTI agent code.
645 static const char *EXC_CNAME = "java/lang/Exception";
646
647 static int check_error(jvmtiError err, const char *s) {
648 if (err != JVMTI_ERROR_NONE) {
649 printf(" ## %s error: %d\n", s, err);
650 return 1;
651 }
652 return 0;
653 }
654
655 static int check_capability_error(jvmtiError err, const char *s) {
656 if (err != JVMTI_ERROR_NONE) {
657 if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY) {
658 return 0;
659 }
660 fprintf(stderr, " ## %s error: %d\n", s, err);
661 }
662 return 1;
663 }
664
665 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
666
667 JNIEXPORT
668 jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
669 return Agent_Initialize(jvm, options, reserved);
670 }
671
672 JNIEXPORT
673 jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
674 return Agent_Initialize(jvm, options, reserved);
675 }
676
677 JNIEXPORT
678 jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
679 return JNI_VERSION_1_8;
680 }
681
682 #define MAX_THREADS 500
683
684 typedef struct ThreadStats {
685 int thread_count;
686 int counts[MAX_THREADS];
687 char* threads[MAX_THREADS];
688 } ThreadStats;
689
690 static ThreadStats thread_stats;
691
692 JNIEXPORT jboolean JNICALL
693 Java_MyPackage_HeapMonitorThreadDisabledTest_checkThreadSamplesOnlyFrom(
694 JNIEnv* env, jclass cls, jthread thread) {
695 jvmtiThreadInfo info;
696 jvmtiError err;
697 char* expected_name;
698 int found_thread = FALSE;
699
700 err = jvmti->GetThreadInfo(thread, &info);
701 expected_name = info.name;
702
703 if (err != JVMTI_ERROR_NONE) {
704 fprintf(stderr, "Failed to get thread information\n");
705 return FALSE;
706 }
707
708 if (thread_stats.thread_count != 1) {
709 fprintf(stderr, "Wrong thread number: %d (expected 1)\n",
710 thread_stats.thread_count);
711 return FALSE;
712 }
713
714 if (strcmp(expected_name, thread_stats.threads[0]) != 0) {
715 fprintf(stderr, "Unexpected thread name: '%s' (expected '%s')\n",
716 thread_stats.threads[0], expected_name);
717 return FALSE;
718 }
719
720 return TRUE;
721 }
722
723 static void add_thread_count(jthread thread) {
724 int i;
725 jvmtiThreadInfo info;
726 jvmtiError err;
727
728 err = jvmti->GetThreadInfo(thread, &info);
729 if (err != JVMTI_ERROR_NONE) {
730 fprintf(stderr, "Thread info for %p failed, ignoring thread count\n",
731 thread);
732 return;
733 }
734
735 event_storage_lock(&global_event_storage);
736 for (i = 0; i < thread_stats.thread_count; i++) {
737 if (!strcmp(thread_stats.threads[i], info.name)) {
738 thread_stats.counts[i]++;
739 event_storage_unlock(&global_event_storage);
740 return;
741 }
742 }
743
744 thread_stats.threads[thread_stats.thread_count] = info.name;
745 thread_stats.counts[thread_stats.thread_count]++;
746 thread_stats.thread_count++;
747 event_storage_unlock(&global_event_storage);
748 }
749
750 JNIEXPORT void JNICALL
751 Java_MyPackage_HeapMonitorThreadDisabledTest_enableSamplingEvents(
752 JNIEnv* env, jclass cls, jthread thread) {
753 fprintf(stderr, "Enabling for %p\n", thread);
754 check_error(jvmti->SetEventNotificationMode(
755 JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, thread),
756 "Set event notifications for a single thread");
757 }
758
759 static void print_thread_stats() {
760 int i;
761 event_storage_lock(&global_event_storage);
762 fprintf(stderr, "Thread count:\n");
763 for (i = 0; i < thread_stats.thread_count; i++) {
764 fprintf(stderr, "\t%s: %d\n", thread_stats.threads[i], thread_stats.counts[i]);
765 }
766 event_storage_unlock(&global_event_storage);
767 }
768
769 JNIEXPORT
770 void JNICALL SampledObjectAlloc(jvmtiEnv *jvmti_env,
771 JNIEnv* jni_env,
772 jthread thread,
773 jobject object,
774 jclass object_klass,
775 jlong size) {
776 add_thread_count(thread);
777
778 if (event_storage_get_compaction_required(&global_event_storage)) {
779 event_storage_compact(&global_event_storage, jni_env);
780 }
781
782 event_storage_add(&global_event_storage, jni_env, thread, object,
783 object_klass, size);
784 }
785
786 JNIEXPORT
787 void JNICALL VMObjectAlloc(jvmtiEnv *jvmti_env,
788 JNIEnv* jni_env,
789 jthread thread,
790 jobject object,
791 jclass object_klass,
792 jlong size) {
793 event_storage_add(&second_global_event_storage, jni_env, thread, object,
794 object_klass, size);
795 }
796
797 JNIEXPORT
798 void JNICALL GarbageCollectionFinish(jvmtiEnv *jvmti_env) {
799 event_storage_set_compaction_required(&global_event_storage);
800 }
801
802 static int enable_notifications() {
803 if (check_error(jvmti->SetEventNotificationMode(
804 JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, nullptr),
805 "Set event notifications")) {
806 return 1;
807 }
808
809 return check_error(jvmti->SetEventNotificationMode(
810 JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, nullptr),
811 "Set event notifications");
812 }
813
814 static
815 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
816 jint res;
817 jvmtiEventCallbacks callbacks;
818 jvmtiCapabilities caps;
819
820 res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION);
821 if (res != JNI_OK || jvmti == nullptr) {
822 fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n");
823 return JNI_ERR;
824 }
825
826 // Get second jvmti environment.
827 res = jvm->GetEnv((void **) &second_jvmti, JVMTI_VERSION);
828 if (res != JNI_OK || second_jvmti == nullptr) {
829 fprintf(stderr, "Error: wrong result of a valid second call to GetEnv!\n");
830 return JNI_ERR;
831 }
832
833 if (PRINT_OUT) {
834 fprintf(stderr, "Storage is at %p, secondary is at %p\n",
835 &global_event_storage, &second_global_event_storage);
836 }
837
838 jvmti->CreateRawMonitor("storage_monitor",
839 &global_event_storage.storage_monitor);
840 jvmti->CreateRawMonitor("second_storage_monitor",
841 &second_global_event_storage.storage_monitor);
842
843 jvmti->CreateRawMonitor("compaction_monitor",
844 &global_event_storage.compaction_monitor);
845 jvmti->CreateRawMonitor("second_compaction_monitor",
846 &second_global_event_storage.compaction_monitor);
847
848 event_storage_set_garbage_history(&global_event_storage, 200);
849 event_storage_set_garbage_history(&second_global_event_storage, 200);
850
851 memset(&callbacks, 0, sizeof(callbacks));
852 callbacks.SampledObjectAlloc = &SampledObjectAlloc;
853 callbacks.VMObjectAlloc = &VMObjectAlloc;
854 callbacks.GarbageCollectionFinish = &GarbageCollectionFinish;
855
856 memset(&caps, 0, sizeof(caps));
857 // Get line numbers, sample events, filename, and gc events for the tests.
858 caps.can_get_line_numbers = 1;
859 caps.can_get_source_file_name = 1;
860 caps.can_generate_garbage_collection_events = 1;
861 caps.can_generate_sampled_object_alloc_events = 1;
862 caps.can_generate_vm_object_alloc_events = 1;
863 if (check_error(jvmti->AddCapabilities(&caps), "Add capabilities")) {
864 return JNI_ERR;
865 }
866
867 if (check_error(jvmti->SetEventCallbacks(&callbacks,
868 sizeof(jvmtiEventCallbacks)),
869 " Set Event Callbacks")) {
870 return JNI_ERR;
871 }
872 return JNI_OK;
873 }
874
875 JNIEXPORT void JNICALL
876 Java_MyPackage_HeapMonitor_setSamplingInterval(JNIEnv* env, jclass cls, jint value) {
877 jvmti->SetHeapSamplingInterval(value);
878 }
879
880 JNIEXPORT jboolean JNICALL
881 Java_MyPackage_HeapMonitor_eventStorageIsEmpty(JNIEnv* env, jclass cls) {
882 return event_storage_get_count(&global_event_storage) == 0;
883 }
884
885 JNIEXPORT jint JNICALL
886 Java_MyPackage_HeapMonitor_getEventStorageElementCount(JNIEnv* env, jclass cls) {
887 return event_storage_get_count(&global_event_storage);
888 }
889
890 JNIEXPORT void JNICALL
891 Java_MyPackage_HeapMonitor_enableSamplingEvents(JNIEnv* env, jclass cls) {
892 enable_notifications();
893 }
894
895 JNIEXPORT void JNICALL
896 Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) {
897 check_error(jvmti->SetEventNotificationMode(
898 JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, nullptr),
899 "Set event notifications");
900
901 check_error(jvmti->SetEventNotificationMode(
902 JVMTI_DISABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, nullptr),
903 "Garbage Collection Finish");
904 }
905
906 static ExpectedContentFrame *get_native_frames(JNIEnv* env, jclass cls,
907 jobjectArray frames) {
908 ExpectedContentFrame *native_frames;
909 jsize size = env->GetArrayLength(frames);
910
911 if (env->ExceptionOccurred()) {
912 env->FatalError("get_native_frames failed with the GetArrayLength call");
913 }
914
915 native_frames = reinterpret_cast<ExpectedContentFrame*> (malloc(size * sizeof(*native_frames)));
916
917 if (native_frames == nullptr) {
918 env->FatalError("Error in get_native_frames: malloc returned null\n");
919 }
920
921 if (fill_native_frames(env, frames, native_frames, size) != 0) {
922 env->FatalError("Error in get_native_frames: fill_native_frames returned failed status\n");
923 }
924
925 return native_frames;
926 }
927
928 JNIEXPORT jboolean JNICALL
929 Java_MyPackage_HeapMonitor_obtainedEvents(JNIEnv* env, jclass cls,
930 jobjectArray frames,
931 jboolean check_lines) {
932 jboolean result;
933 jsize size = env->GetArrayLength(frames);
934 ExpectedContentFrame *native_frames = get_native_frames(env, cls, frames);
935
936 result = event_storage_contains(env, &global_event_storage, native_frames,
937 size, check_lines);
938
939 free(native_frames), native_frames = nullptr;
940 return result;
941 }
942
943 JNIEXPORT jboolean JNICALL
944 Java_MyPackage_HeapMonitor_garbageContains(JNIEnv* env, jclass cls,
945 jobjectArray frames,
946 jboolean check_lines) {
947 jboolean result;
948 jsize size = env->GetArrayLength(frames);
949 ExpectedContentFrame *native_frames = get_native_frames(env, cls, frames);
950
951 result = event_storage_garbage_contains(env, &global_event_storage,
952 native_frames, size, check_lines);
953
954 free(native_frames), native_frames = nullptr;
955 return result;
956 }
957
958 JNIEXPORT jlong JNICALL
959 Java_MyPackage_HeapMonitor_getSize(JNIEnv* env, jclass cls,
960 jobjectArray frames,
961 jboolean check_lines) {
962 jlong result = 0;
963 jsize size = env->GetArrayLength(frames);
964 ExpectedContentFrame *native_frames = get_native_frames(env, cls, frames);
965
966 result = event_storage_get_size(env, &global_event_storage,
967 native_frames, size, check_lines);
968
969 free(native_frames), native_frames = nullptr;
970 return result;
971 }
972
973 JNIEXPORT void JNICALL
974 Java_MyPackage_HeapMonitor_forceGarbageCollection(JNIEnv* env, jclass cls) {
975 check_error(jvmti->ForceGarbageCollection(),
976 "Forced Garbage Collection");
977 }
978
979 JNIEXPORT void JNICALL
980 Java_MyPackage_HeapMonitor_resetEventStorage(JNIEnv* env, jclass cls) {
981 event_storage_reset(&global_event_storage);
982 event_storage_reset(&second_global_event_storage);
983 }
984
985 JNIEXPORT jboolean JNICALL
986 Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env,
987 jclass cls) {
988 jvmtiCapabilities caps;
989 memset(&caps, 0, sizeof(caps));
990 caps.can_generate_sampled_object_alloc_events = 1;
991 if (check_error(jvmti->RelinquishCapabilities(&caps),
992 "Add capabilities\n")){
993 return FALSE;
994 }
995
996 if (check_capability_error(jvmti->SetHeapSamplingInterval(1<<19),
997 "Set Heap Sampling Interval")) {
998 return FALSE;
999 }
1000 return TRUE;
1001 }
1002
1003 JNIEXPORT jboolean JNICALL
1004 Java_MyPackage_HeapMonitorIllegalArgumentTest_testIllegalArgument(JNIEnv *env,
1005 jclass cls) {
1006 if (check_error(jvmti->SetHeapSamplingInterval(0),
1007 "Sampling interval 0 failed\n")){
1008 return FALSE;
1009 }
1010
1011 if (check_error(jvmti->SetHeapSamplingInterval(1024),
1012 "Sampling interval 1024 failed\n")){
1013 return FALSE;
1014 }
1015
1016 if (!check_error(jvmti->SetHeapSamplingInterval(-1),
1017 "Sampling interval -1 passed\n")){
1018 return FALSE;
1019 }
1020
1021 if (!check_error(jvmti->SetHeapSamplingInterval(-1024),
1022 "Sampling interval -1024 passed\n")){
1023 return FALSE;
1024 }
1025
1026 return TRUE;
1027 }
1028
1029 JNIEXPORT jdouble JNICALL
1030 Java_MyPackage_HeapMonitor_getAverageSize(JNIEnv *env, jclass cls) {
1031 return event_storage_get_average_size(&global_event_storage);
1032 }
1033
1034 typedef struct sThreadsFound {
1035 jthread* threads;
1036 int num_threads;
1037 } ThreadsFound;
1038
1039 JNIEXPORT jboolean JNICALL
1040 Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls,
1041 jint num_threads) {
1042 print_thread_stats();
1043 // Ensure we got stacks from at least num_threads.
1044 return thread_stats.thread_count >= num_threads;
1045 }
1046
1047 JNIEXPORT
1048 void JNICALL SampledObjectAlloc2(jvmtiEnv *jvmti_env,
1049 JNIEnv* jni_env,
1050 jthread thread,
1051 jobject object,
1052 jclass object_klass,
1053 jlong size) {
1054 // Nop for now, two agents are not yet implemented.
1055 assert(0);
1056 }
1057
1058 JNIEXPORT jboolean JNICALL
1059 Java_MyPackage_HeapMonitorTwoAgentsTest_enablingSamplingInSecondaryAgent(
1060 JNIEnv* env, jclass cls) {
1061 // Currently this method should be failing directly at the AddCapability step
1062 // but the implementation is correct for when multi-agent support is enabled.
1063 jvmtiCapabilities caps;
1064 jvmtiEventCallbacks callbacks;
1065
1066 memset(&caps, 0, sizeof(caps));
1067 caps.can_generate_sampled_object_alloc_events = 1;
1068 if (check_error(second_jvmti->AddCapabilities(&caps),
1069 "Set the capability for second agent")) {
1070 return FALSE;
1071 }
1072
1073 memset(&callbacks, 0, sizeof(callbacks));
1074 callbacks.SampledObjectAlloc = &SampledObjectAlloc2;
1075
1076 if (check_error(second_jvmti->SetEventCallbacks(&callbacks,
1077 sizeof(jvmtiEventCallbacks)),
1078 " Set Event Callbacks for second agent")) {
1079 return FALSE;
1080 }
1081
1082 return TRUE;
1083 }
1084
1085 JNIEXPORT void JNICALL
1086 Java_MyPackage_HeapMonitor_enableVMEvents(JNIEnv* env, jclass cls) {
1087 check_error(jvmti->SetEventNotificationMode(
1088 JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, nullptr),
1089 "Set vm event notifications");
1090 }
1091
1092 JNIEXPORT jint JNICALL
1093 Java_MyPackage_HeapMonitorVMEventsTest_vmEvents(JNIEnv* env, jclass cls) {
1094 return event_storage_number_additions(&second_global_event_storage);
1095 }
1096
1097 JNIEXPORT jint JNICALL
1098 Java_MyPackage_HeapMonitor_sampledEvents(JNIEnv* env, jclass cls) {
1099 return event_storage_number_additions(&global_event_storage);
1100 }
1101
1102 static jobject allocate_object(JNIEnv* env) {
1103 // Construct an Object.
1104 jclass cls = env->FindClass("java/lang/Object");
1105 jmethodID constructor;
1106 jobject result;
1107
1108 if (env->ExceptionOccurred() || cls == nullptr) {
1109 env->FatalError("Error in jni FindClass: Cannot find Object class\n");
1110 }
1111
1112 constructor = env->GetMethodID(cls, "<init>", "()V");
1113 if (env->ExceptionOccurred() || constructor == nullptr) {
1114 env->FatalError("Error in jni GetMethodID: Cannot find Object class constructor\n");
1115 }
1116
1117 // Call back constructor to allocate a new instance, with an int argument
1118 result = env->NewObject(cls, constructor);
1119
1120 if (env->ExceptionOccurred() || result == nullptr) {
1121 env->FatalError("Error in jni NewObject: Cannot allocate an object\n");
1122 }
1123 return result;
1124 }
1125
1126 // Ensure we got a callback for the test.
1127 static int did_recursive_callback_test;
1128
1129 JNIEXPORT
1130 void JNICALL RecursiveSampledObjectAlloc(jvmtiEnv *jvmti_env,
1131 JNIEnv* jni_env,
1132 jthread thread,
1133 jobject object,
1134 jclass object_klass,
1135 jlong size) {
1136 // Basically ensure that if we were to allocate objects, we would not have an
1137 // infinite recursion here.
1138 int i;
1139 for (i = 0; i < 1000; i++) {
1140 if (allocate_object(jni_env) == nullptr) {
1141 jni_env->FatalError("allocate_object returned null\n");
1142 }
1143 }
1144
1145 did_recursive_callback_test = 1;
1146 }
1147
1148 JNIEXPORT jboolean JNICALL
1149 Java_MyPackage_HeapMonitorRecursiveTest_didCallback(JNIEnv* env, jclass cls) {
1150 return did_recursive_callback_test != 0;
1151 }
1152
1153 JNIEXPORT void JNICALL
1154 Java_MyPackage_HeapMonitorRecursiveTest_setCallbackToCallAllocateSomeMore(JNIEnv* env, jclass cls) {
1155 jvmtiEventCallbacks callbacks;
1156
1157 memset(&callbacks, 0, sizeof(callbacks));
1158 callbacks.SampledObjectAlloc = &RecursiveSampledObjectAlloc;
1159
1160 if (check_error(jvmti->SetEventCallbacks(&callbacks,
1161 sizeof(jvmtiEventCallbacks)),
1162 "Set Event Callbacks")) {
1163 env->FatalError("Cannot reset the callback.");
1164 }
1165 }
1166
1167 }