1 /*
2 * Copyright (c) 2018, 2026, 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 int print_out_comparisons) {
194 jvmtiFrameInfo* frames;
195 size_t i;
196
197 if (expected_count > trace->frame_count) {
198 return FALSE;
199 }
200
201 frames = trace->frames;
202 for (i = 0; i < expected_count; i++) {
203 // Get basic information out of the trace.
204 jlocation bci = frames[i].location;
205 jmethodID methodid = frames[i].method;
206 char *name = nullptr, *signature = nullptr, *file_name = nullptr;
207 jclass declaring_class;
208 int line_number;
209 jboolean differ;
210 jvmtiError err;
211
212 if (bci < 0 && expected[i].line_number != -1) {
213 return FALSE;
214 }
215
216 // Transform into usable information.
217 line_number = get_line_number(methodid, bci);
218 jvmti->GetMethodName(methodid, &name, &signature, 0);
219
220 if (JVMTI_ERROR_NONE != jvmti->GetMethodDeclaringClass(methodid, &declaring_class)) {
221 return FALSE;
222 }
223
224 err = jvmti->GetSourceFileName(declaring_class, &file_name);
225 if (err != JVMTI_ERROR_NONE) {
226 return FALSE;
227 }
228
229 // Compare now, none should be null.
230 if (name == nullptr) {
231 return FALSE;
232 }
233
234 if (file_name == nullptr) {
235 return FALSE;
236 }
237
238 if (signature == nullptr) {
239 return FALSE;
240 }
241
242 differ = (strcmp(name, expected[i].name) ||
243 strcmp(signature, expected[i].signature) ||
244 strcmp(file_name, expected[i].file_name) ||
245 line_number != expected[i].line_number);
246
247 if (print_out_comparisons) {
248 fprintf(stderr, "\tComparing:\n");
249 fprintf(stderr, "\t\tNames: %s and %s\n", name, expected[i].name);
250 fprintf(stderr, "\t\tSignatures: %s and %s\n", signature, expected[i].signature);
251 fprintf(stderr, "\t\tFile name: %s and %s\n", file_name, expected[i].file_name);
252 fprintf(stderr, "\t\tLines: %d and %d\n", line_number, expected[i].line_number);
253 fprintf(stderr, "\t\tResult is %d\n", differ);
254 }
255
256 if (differ) {
257 return FALSE;
258 }
259 }
260
261 return TRUE;
262 }
263
264 // Static native API for various tests.
265 static int fill_native_frames(JNIEnv* env, jobjectArray frames,
266 ExpectedContentFrame* native_frames, size_t size) {
267 size_t i;
268 for (i = 0; i < size; i++) {
269 jclass frame_class = nullptr;
270 jfieldID line_number_field_id = 0;
271 int line_number = 0;
272 jfieldID string_id = 0;
273 jstring string_object = nullptr;
274 const char* method = nullptr;
275 const char* file_name = nullptr;
276 const char* signature = nullptr;
277
278 jobject obj = env->GetObjectArrayElement(frames, (jsize) i);
279
280 if (env->ExceptionOccurred()) {
281 fprintf(stderr, "fill_native_frames: Exception in jni GetObjectArrayElement\n");
282 return -1;
283 }
284
285 frame_class = env->GetObjectClass(obj);
286
287 if (env->ExceptionOccurred()) {
288 fprintf(stderr, "fill_native_frames: Exception in jni GetObjectClass\n");
289 return -1;
290 }
291
292 line_number_field_id = env->GetFieldID(frame_class, "lineNumber", "I");
293
294 if (env->ExceptionOccurred()) {
295 fprintf(stderr, "fill_native_frames: Exception in jni GetFieldID\n");
296 return -1;
297 }
298
299 line_number = env->GetIntField(obj, line_number_field_id);
300
301 if (env->ExceptionOccurred()) {
302 fprintf(stderr, "fill_native_frames: Exception in jni GetIntField\n");
303 return -1;
304 }
305
306 string_id = env->GetFieldID(frame_class, "method", "Ljava/lang/String;");
307
308 if (env->ExceptionOccurred()) {
309 fprintf(stderr, "fill_native_frames: Exception in jni GetFieldID\n");
310 return -1;
311 }
312
313 string_object = (jstring) env->GetObjectField(obj, string_id);
314
315 if (env->ExceptionOccurred()) {
316 fprintf(stderr, "fill_native_frames: Exception in jni GetObjectField\n");
317 return -1;
318 }
319
320 method = env->GetStringUTFChars(string_object, 0);
321
322 if (env->ExceptionOccurred()) {
323 fprintf(stderr, "Exception in jni GetStringUTFChars\n");
324 return -1;
325 }
326
327 string_id = env->GetFieldID(frame_class, "fileName", "Ljava/lang/String;");
328
329 if (env->ExceptionOccurred()) {
330 fprintf(stderr, "Exception in jni GetFieldID\n");
331 return -1;
332 }
333
334 string_object = (jstring) (env->GetObjectField(obj, string_id));
335
336 if (env->ExceptionOccurred()) {
337 fprintf(stderr, "fill_native_frames: Exception in second jni GetObjectField\n");
338 return -1;
339 }
340
341 file_name = env->GetStringUTFChars(string_object, 0);
342
343 if (env->ExceptionOccurred()) {
344 fprintf(stderr, "fill_native_frames: Exception in jni GetStringUTFChars\n");
345 return -1;
346 }
347
348 string_id = env->GetFieldID(frame_class, "signature", "Ljava/lang/String;");
349
350 if (env->ExceptionOccurred()) {
351 fprintf(stderr, "fill_native_frames: Exception in second jni GetFieldID\n");
352 return -1;
353 }
354
355 string_object = (jstring) (env->GetObjectField(obj, string_id));
356
357 if (env->ExceptionOccurred()) {
358 fprintf(stderr, "fill_native_frames: Exception in third jni GetObjectField\n");
359 return -1;
360 }
361
362 signature = env->GetStringUTFChars(string_object, 0);
363
364 if (env->ExceptionOccurred()) {
365 fprintf(stderr, "fill_native_frames: Exception in jni GetStringUTFChars\n");
366 return -1;
367 }
368
369 native_frames[i].name = method;
370 native_frames[i].file_name = file_name;
371 native_frames[i].signature = signature;
372 native_frames[i].line_number = line_number;
373 }
374
375 return 0;
376 }
377
378 // Internal storage system implementation.
379 static EventStorage global_event_storage;
380 static EventStorage second_global_event_storage;
381
382 static void event_storage_set_compaction_required(EventStorage* storage) {
383 event_storage_lock_compaction(storage);
384 storage->compaction_required = 1;
385 event_storage_unlock_compaction(storage);
386 }
387
388 static int event_storage_get_compaction_required(EventStorage* storage) {
389 int result;
390 event_storage_lock_compaction(storage);
391 result = storage->compaction_required;
392 event_storage_unlock_compaction(storage);
393 return result;
394 }
395
396 static void event_storage_set_garbage_history(EventStorage* storage, int value) {
397 size_t size;
398 event_storage_lock(storage);
399 global_event_storage.garbage_history_size = value;
400 free(global_event_storage.garbage_collected_objects);
401 size = sizeof(*global_event_storage.garbage_collected_objects) * value;
402 global_event_storage.garbage_collected_objects = reinterpret_cast<ObjectTrace**>(malloc(size));
403 memset(global_event_storage.garbage_collected_objects, 0, size);
404 event_storage_unlock(storage);
405 }
406
407 // No mutex here, it is handled by the caller.
408 static void event_storage_add_garbage_collected_object(EventStorage* storage,
409 ObjectTrace* object) {
410 int idx = storage->garbage_history_index;
411 ObjectTrace* old_object = storage->garbage_collected_objects[idx];
412 if (old_object != nullptr) {
413 free(old_object->frames);
414 free(storage->garbage_collected_objects[idx]);
415 }
416
417 storage->garbage_collected_objects[idx] = object;
418 storage->garbage_history_index = (idx + 1) % storage->garbage_history_size;
419 }
420
421 static int event_storage_get_count(EventStorage* storage) {
422 int result;
423 event_storage_lock(storage);
424 result = storage->live_object_count;
425 event_storage_unlock(storage);
426 return result;
427 }
428
429 static double event_storage_get_average_size(EventStorage* storage) {
430 double accumulation = 0;
431 int max_size;
432 int i;
433
434 event_storage_lock(storage);
435 max_size = storage->live_object_count;
436
437 for (i = 0; i < max_size; i++) {
438 accumulation += storage->live_objects[i]->size;
439 }
440
441 event_storage_unlock(storage);
442 return accumulation / max_size;
443 }
444
445 static jboolean event_storage_contains(JNIEnv* env,
446 EventStorage* storage,
447 ExpectedContentFrame* frames,
448 size_t size) {
449 int i;
450 event_storage_lock(storage);
451 fprintf(stderr, "Checking storage count %d\n", storage->live_object_count);
452 for (i = 0; i < storage->live_object_count; i++) {
453 ObjectTrace* trace = storage->live_objects[i];
454
455 if (check_sample_content(env, trace, frames, size, PRINT_OUT)) {
456 event_storage_unlock(storage);
457 return TRUE;
458 }
459 }
460 event_storage_unlock(storage);
461 return FALSE;
462 }
463
464 static jlong event_storage_get_size(JNIEnv* env,
465 EventStorage* storage,
466 ExpectedContentFrame* frames,
467 size_t size) {
468 int i;
469 event_storage_lock(storage);
470 fprintf(stderr, "Getting element from storage count, size %d\n", storage->live_object_count);
471 for (i = 0; i < storage->live_object_count; i++) {
472 ObjectTrace* trace = storage->live_objects[i];
473
474 if (check_sample_content(env, trace, frames, size, PRINT_OUT)) {
475 jlong result = trace->size;
476 event_storage_unlock(storage);
477 return result;
478 }
479 }
480 event_storage_unlock(storage);
481 return 0;
482 }
483
484 static jboolean event_storage_garbage_contains(JNIEnv* env,
485 EventStorage* storage,
486 ExpectedContentFrame* frames,
487 size_t size) {
488 int i;
489 event_storage_lock(storage);
490 fprintf(stderr, "Checking garbage storage count %d\n",
491 storage->garbage_history_size);
492 for (i = 0; i < storage->garbage_history_size; i++) {
493 ObjectTrace* trace = storage->garbage_collected_objects[i];
494
495 if (trace == nullptr) {
496 continue;
497 }
498
499 if (check_sample_content(env, trace, frames, size, PRINT_OUT)) {
500 event_storage_unlock(storage);
501 return TRUE;
502 }
503 }
504 event_storage_unlock(storage);
505 return FALSE;
506 }
507
508 // No mutex here, handled by the caller.
509 static void event_storage_augment_storage(EventStorage* storage) {
510 int new_max = (storage->live_object_size * 2) + 1;
511 ObjectTrace** new_objects = reinterpret_cast<ObjectTrace**>(malloc(new_max * sizeof(*new_objects)));
512
513 int current_count = storage->live_object_count;
514 if (storage->live_objects != nullptr) {
515 memcpy(new_objects, storage->live_objects, current_count * sizeof(*new_objects));
516 }
517 free(storage->live_objects);
518 storage->live_objects = new_objects;
519 storage->live_object_size = new_max;
520 }
521
522 static void event_storage_add(EventStorage* storage,
523 JNIEnv* jni,
524 jthread thread,
525 jobject object,
526 jclass klass,
527 jlong size) {
528 jvmtiFrameInfo frames[64];
529 jint count;
530 jvmtiError err;
531
532 if (!jni->HasIdentity(object)) {
533 // weak references are prohibited for non-identity objects, skip them
534 return;
535 }
536
537 err = jvmti->GetStackTrace(thread, 0, 64, frames, &count);
538 if (err == JVMTI_ERROR_NONE && count >= 1) {
539 ObjectTrace* live_object;
540 jvmtiFrameInfo* allocated_frames = (jvmtiFrameInfo*) malloc(count * sizeof(*allocated_frames));
541 memcpy(allocated_frames, frames, count * sizeof(*allocated_frames));
542
543 live_object = (ObjectTrace*) malloc(sizeof(*live_object));
544 live_object->frames = allocated_frames;
545 live_object->frame_count = count;
546 live_object->size = size;
547 live_object->thread = thread;
548 live_object->object = jni->NewWeakGlobalRef(object);
549
550 if (jni->ExceptionOccurred()) {
551 jni->FatalError("Error in event_storage_add: Exception in jni NewWeakGlobalRef");
552 }
553
554 // Only now lock and get things done quickly.
555 event_storage_lock(storage);
556
557 storage->live_object_additions++;
558
559 if (storage->live_object_count >= storage->live_object_size) {
560 event_storage_augment_storage(storage);
561 }
562 assert(storage->live_object_count < storage->live_object_size);
563
564 if (PRINT_OUT) {
565 fprintf(stderr, "Adding trace for thread %p, frame_count %d, storage %p\n",
566 thread, count, storage);
567 print_out_frames(jni, live_object);
568 }
569 storage->live_objects[storage->live_object_count] = live_object;
570 storage->live_object_count++;
571
572 event_storage_unlock(storage);
573 }
574 }
575
576 static void event_storage_compact(EventStorage* storage, JNIEnv* jni) {
577 int max, i, dest;
578 ObjectTrace** live_objects;
579
580 event_storage_lock_compaction(storage);
581 storage->compaction_required = 0;
582 event_storage_unlock_compaction(storage);
583
584 event_storage_lock(storage);
585
586 max = storage->live_object_count;
587 live_objects = storage->live_objects;
588
589 for (i = 0, dest = 0; i < max; i++) {
590 ObjectTrace* live_object = live_objects[i];
591 jweak object = live_object->object;
592
593 if (!jni->IsSameObject(object, nullptr)) {
594 if (dest != i) {
595 live_objects[dest] = live_object;
596 dest++;
597 }
598 } else {
599 jni->DeleteWeakGlobalRef(object);
600 live_object->object = nullptr;
601
602 event_storage_add_garbage_collected_object(storage, live_object);
603 }
604 }
605
606 storage->live_object_count = dest;
607 event_storage_unlock(storage);
608 }
609
610 static void event_storage_free_objects(ObjectTrace** array, int max) {
611 int i;
612 for (i = 0; i < max; i++) {
613 free(array[i]), array[i] = nullptr;
614 }
615 }
616
617 static void event_storage_reset(EventStorage* storage) {
618 event_storage_lock(storage);
619
620 // Reset everything except the mutex and the garbage collection.
621 event_storage_free_objects(storage->live_objects,
622 storage->live_object_count);
623 storage->live_object_additions = 0;
624 storage->live_object_size = 0;
625 storage->live_object_count = 0;
626 free(storage->live_objects), storage->live_objects = nullptr;
627
628 event_storage_free_objects(storage->garbage_collected_objects,
629 storage->garbage_history_size);
630
631 storage->compaction_required = 0;
632 storage->garbage_history_index = 0;
633
634 event_storage_unlock(storage);
635 }
636
637 static int event_storage_number_additions(EventStorage* storage) {
638 int result;
639 event_storage_lock(storage);
640 result = storage->live_object_additions;
641 event_storage_unlock(storage);
642 return result;
643 }
644
645 // Start of the JVMTI agent code.
646 static const char *EXC_CNAME = "java/lang/Exception";
647
648 static int check_error(jvmtiError err, const char *s) {
649 if (err != JVMTI_ERROR_NONE) {
650 printf(" ## %s error: %d\n", s, err);
651 return 1;
652 }
653 return 0;
654 }
655
656 static int check_capability_error(jvmtiError err, const char *s) {
657 if (err != JVMTI_ERROR_NONE) {
658 if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY) {
659 return 0;
660 }
661 fprintf(stderr, " ## %s error: %d\n", s, err);
662 }
663 return 1;
664 }
665
666 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
667
668 JNIEXPORT
669 jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
670 return Agent_Initialize(jvm, options, reserved);
671 }
672
673 JNIEXPORT
674 jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
675 return Agent_Initialize(jvm, options, reserved);
676 }
677
678 JNIEXPORT
679 jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
680 return JNI_VERSION_1_8;
681 }
682
683 #define MAX_THREADS 500
684
685 typedef struct ThreadStats {
686 int thread_count;
687 int counts[MAX_THREADS];
688 char* threads[MAX_THREADS];
689 } ThreadStats;
690
691 static ThreadStats thread_stats;
692
693 JNIEXPORT jboolean JNICALL
694 Java_MyPackage_HeapMonitorThreadDisabledTest_checkThreadSamplesOnlyFrom(
695 JNIEnv* env, jclass cls, jthread thread) {
696 jvmtiThreadInfo info;
697 jvmtiError err;
698 char* expected_name;
699 int found_thread = FALSE;
700
701 err = jvmti->GetThreadInfo(thread, &info);
702 expected_name = info.name;
703
704 if (err != JVMTI_ERROR_NONE) {
705 fprintf(stderr, "Failed to get thread information\n");
706 return FALSE;
707 }
708
709 if (thread_stats.thread_count != 1) {
710 fprintf(stderr, "Wrong thread number: %d (expected 1)\n",
711 thread_stats.thread_count);
712 return FALSE;
713 }
714
715 if (strcmp(expected_name, thread_stats.threads[0]) != 0) {
716 fprintf(stderr, "Unexpected thread name: '%s' (expected '%s')\n",
717 thread_stats.threads[0], expected_name);
718 return FALSE;
719 }
720
721 return TRUE;
722 }
723
724 static void add_thread_count(jthread thread) {
725 int i;
726 jvmtiThreadInfo info;
727 jvmtiError err;
728
729 err = jvmti->GetThreadInfo(thread, &info);
730 if (err != JVMTI_ERROR_NONE) {
731 fprintf(stderr, "Thread info for %p failed, ignoring thread count\n",
732 thread);
733 return;
734 }
735
736 event_storage_lock(&global_event_storage);
737 for (i = 0; i < thread_stats.thread_count; i++) {
738 if (!strcmp(thread_stats.threads[i], info.name)) {
739 thread_stats.counts[i]++;
740 event_storage_unlock(&global_event_storage);
741 return;
742 }
743 }
744
745 thread_stats.threads[thread_stats.thread_count] = info.name;
746 thread_stats.counts[thread_stats.thread_count]++;
747 thread_stats.thread_count++;
748 event_storage_unlock(&global_event_storage);
749 }
750
751 JNIEXPORT void JNICALL
752 Java_MyPackage_HeapMonitorThreadDisabledTest_enableSamplingEvents(
753 JNIEnv* env, jclass cls, jthread thread) {
754 fprintf(stderr, "Enabling for %p\n", thread);
755 check_error(jvmti->SetEventNotificationMode(
756 JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, thread),
757 "Set event notifications for a single thread");
758 }
759
760 static void print_thread_stats() {
761 int i;
762 event_storage_lock(&global_event_storage);
763 fprintf(stderr, "Thread count:\n");
764 for (i = 0; i < thread_stats.thread_count; i++) {
765 fprintf(stderr, "\t%s: %d\n", thread_stats.threads[i], thread_stats.counts[i]);
766 }
767 event_storage_unlock(&global_event_storage);
768 }
769
770 JNIEXPORT
771 void JNICALL SampledObjectAlloc(jvmtiEnv *jvmti_env,
772 JNIEnv* jni_env,
773 jthread thread,
774 jobject object,
775 jclass object_klass,
776 jlong size) {
777 add_thread_count(thread);
778
779 if (event_storage_get_compaction_required(&global_event_storage)) {
780 event_storage_compact(&global_event_storage, jni_env);
781 }
782
783 event_storage_add(&global_event_storage, jni_env, thread, object,
784 object_klass, size);
785 }
786
787 JNIEXPORT
788 void JNICALL VMObjectAlloc(jvmtiEnv *jvmti_env,
789 JNIEnv* jni_env,
790 jthread thread,
791 jobject object,
792 jclass object_klass,
793 jlong size) {
794 event_storage_add(&second_global_event_storage, jni_env, thread, object,
795 object_klass, size);
796 }
797
798 JNIEXPORT
799 void JNICALL GarbageCollectionFinish(jvmtiEnv *jvmti_env) {
800 event_storage_set_compaction_required(&global_event_storage);
801 }
802
803 static int enable_notifications() {
804 if (check_error(jvmti->SetEventNotificationMode(
805 JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, nullptr),
806 "Set event notifications")) {
807 return 1;
808 }
809
810 return check_error(jvmti->SetEventNotificationMode(
811 JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, nullptr),
812 "Set event notifications");
813 }
814
815 static
816 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
817 jint res;
818 jvmtiEventCallbacks callbacks;
819 jvmtiCapabilities caps;
820
821 res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION);
822 if (res != JNI_OK || jvmti == nullptr) {
823 fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n");
824 return JNI_ERR;
825 }
826
827 // Get second jvmti environment.
828 res = jvm->GetEnv((void **) &second_jvmti, JVMTI_VERSION);
829 if (res != JNI_OK || second_jvmti == nullptr) {
830 fprintf(stderr, "Error: wrong result of a valid second call to GetEnv!\n");
831 return JNI_ERR;
832 }
833
834 if (PRINT_OUT) {
835 fprintf(stderr, "Storage is at %p, secondary is at %p\n",
836 &global_event_storage, &second_global_event_storage);
837 }
838
839 jvmti->CreateRawMonitor("storage_monitor",
840 &global_event_storage.storage_monitor);
841 jvmti->CreateRawMonitor("second_storage_monitor",
842 &second_global_event_storage.storage_monitor);
843
844 jvmti->CreateRawMonitor("compaction_monitor",
845 &global_event_storage.compaction_monitor);
846 jvmti->CreateRawMonitor("second_compaction_monitor",
847 &second_global_event_storage.compaction_monitor);
848
849 event_storage_set_garbage_history(&global_event_storage, 200);
850 event_storage_set_garbage_history(&second_global_event_storage, 200);
851
852 memset(&callbacks, 0, sizeof(callbacks));
853 callbacks.SampledObjectAlloc = &SampledObjectAlloc;
854 callbacks.VMObjectAlloc = &VMObjectAlloc;
855 callbacks.GarbageCollectionFinish = &GarbageCollectionFinish;
856
857 memset(&caps, 0, sizeof(caps));
858 // Get line numbers, sample events, filename, and gc events for the tests.
859 caps.can_get_line_numbers = 1;
860 caps.can_get_source_file_name = 1;
861 caps.can_generate_garbage_collection_events = 1;
862 caps.can_generate_sampled_object_alloc_events = 1;
863 caps.can_generate_vm_object_alloc_events = 1;
864 if (check_error(jvmti->AddCapabilities(&caps), "Add capabilities")) {
865 return JNI_ERR;
866 }
867
868 if (check_error(jvmti->SetEventCallbacks(&callbacks,
869 sizeof(jvmtiEventCallbacks)),
870 " Set Event Callbacks")) {
871 return JNI_ERR;
872 }
873 return JNI_OK;
874 }
875
876 JNIEXPORT void JNICALL
877 Java_MyPackage_HeapMonitor_setSamplingInterval(JNIEnv* env, jclass cls, jint value) {
878 jvmti->SetHeapSamplingInterval(value);
879 }
880
881 JNIEXPORT jboolean JNICALL
882 Java_MyPackage_HeapMonitor_eventStorageIsEmpty(JNIEnv* env, jclass cls) {
883 return event_storage_get_count(&global_event_storage) == 0;
884 }
885
886 JNIEXPORT jint JNICALL
887 Java_MyPackage_HeapMonitor_getEventStorageElementCount(JNIEnv* env, jclass cls) {
888 return event_storage_get_count(&global_event_storage);
889 }
890
891 JNIEXPORT void JNICALL
892 Java_MyPackage_HeapMonitor_enableSamplingEvents(JNIEnv* env, jclass cls) {
893 enable_notifications();
894 }
895
896 JNIEXPORT void JNICALL
897 Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) {
898 check_error(jvmti->SetEventNotificationMode(
899 JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, nullptr),
900 "Set event notifications");
901
902 check_error(jvmti->SetEventNotificationMode(
903 JVMTI_DISABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, nullptr),
904 "Garbage Collection Finish");
905 }
906
907 static ExpectedContentFrame *get_native_frames(JNIEnv* env, jclass cls,
908 jobjectArray frames) {
909 ExpectedContentFrame *native_frames;
910 jsize size = env->GetArrayLength(frames);
911
912 if (env->ExceptionOccurred()) {
913 env->FatalError("get_native_frames failed with the GetArrayLength call");
914 }
915
916 native_frames = reinterpret_cast<ExpectedContentFrame*> (malloc(size * sizeof(*native_frames)));
917
918 if (native_frames == nullptr) {
919 env->FatalError("Error in get_native_frames: malloc returned null\n");
920 }
921
922 if (fill_native_frames(env, frames, native_frames, size) != 0) {
923 env->FatalError("Error in get_native_frames: fill_native_frames returned failed status\n");
924 }
925
926 return native_frames;
927 }
928
929 JNIEXPORT jboolean JNICALL
930 Java_MyPackage_HeapMonitor_obtainedEvents(JNIEnv* env, jclass cls,
931 jobjectArray frames) {
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);
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 result;
947 jsize size = env->GetArrayLength(frames);
948 ExpectedContentFrame *native_frames = get_native_frames(env, cls, frames);
949
950 result = event_storage_garbage_contains(env, &global_event_storage,
951 native_frames, size);
952
953 free(native_frames), native_frames = nullptr;
954 return result;
955 }
956
957 JNIEXPORT jlong JNICALL
958 Java_MyPackage_HeapMonitor_getSize(JNIEnv* env, jclass cls,
959 jobjectArray frames) {
960 jlong result = 0;
961 jsize size = env->GetArrayLength(frames);
962 ExpectedContentFrame *native_frames = get_native_frames(env, cls, frames);
963
964 result = event_storage_get_size(env, &global_event_storage,
965 native_frames, size);
966
967 free(native_frames), native_frames = nullptr;
968 return result;
969 }
970
971 JNIEXPORT void JNICALL
972 Java_MyPackage_HeapMonitor_forceGarbageCollection(JNIEnv* env, jclass cls) {
973 check_error(jvmti->ForceGarbageCollection(),
974 "Forced Garbage Collection");
975 }
976
977 JNIEXPORT void JNICALL
978 Java_MyPackage_HeapMonitor_resetEventStorage(JNIEnv* env, jclass cls) {
979 event_storage_reset(&global_event_storage);
980 event_storage_reset(&second_global_event_storage);
981 }
982
983 JNIEXPORT jboolean JNICALL
984 Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env,
985 jclass cls) {
986 jvmtiCapabilities caps;
987 memset(&caps, 0, sizeof(caps));
988 caps.can_generate_sampled_object_alloc_events = 1;
989 if (check_error(jvmti->RelinquishCapabilities(&caps),
990 "Add capabilities\n")){
991 return FALSE;
992 }
993
994 if (check_capability_error(jvmti->SetHeapSamplingInterval(1<<19),
995 "Set Heap Sampling Interval")) {
996 return FALSE;
997 }
998 return TRUE;
999 }
1000
1001 JNIEXPORT jboolean JNICALL
1002 Java_MyPackage_HeapMonitorIllegalArgumentTest_testIllegalArgument(JNIEnv *env,
1003 jclass cls) {
1004 if (check_error(jvmti->SetHeapSamplingInterval(0),
1005 "Sampling interval 0 failed\n")){
1006 return FALSE;
1007 }
1008
1009 if (check_error(jvmti->SetHeapSamplingInterval(1024),
1010 "Sampling interval 1024 failed\n")){
1011 return FALSE;
1012 }
1013
1014 if (!check_error(jvmti->SetHeapSamplingInterval(-1),
1015 "Sampling interval -1 passed\n")){
1016 return FALSE;
1017 }
1018
1019 if (!check_error(jvmti->SetHeapSamplingInterval(-1024),
1020 "Sampling interval -1024 passed\n")){
1021 return FALSE;
1022 }
1023
1024 return TRUE;
1025 }
1026
1027 JNIEXPORT jdouble JNICALL
1028 Java_MyPackage_HeapMonitor_getAverageSize(JNIEnv *env, jclass cls) {
1029 return event_storage_get_average_size(&global_event_storage);
1030 }
1031
1032 typedef struct sThreadsFound {
1033 jthread* threads;
1034 int num_threads;
1035 } ThreadsFound;
1036
1037 JNIEXPORT jboolean JNICALL
1038 Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls,
1039 jint num_threads) {
1040 print_thread_stats();
1041 // Ensure we got stacks from at least num_threads.
1042 return thread_stats.thread_count >= num_threads;
1043 }
1044
1045 JNIEXPORT
1046 void JNICALL SampledObjectAlloc2(jvmtiEnv *jvmti_env,
1047 JNIEnv* jni_env,
1048 jthread thread,
1049 jobject object,
1050 jclass object_klass,
1051 jlong size) {
1052 // Nop for now, two agents are not yet implemented.
1053 assert(0);
1054 }
1055
1056 JNIEXPORT jboolean JNICALL
1057 Java_MyPackage_HeapMonitorTwoAgentsTest_enablingSamplingInSecondaryAgent(
1058 JNIEnv* env, jclass cls) {
1059 // Currently this method should be failing directly at the AddCapability step
1060 // but the implementation is correct for when multi-agent support is enabled.
1061 jvmtiCapabilities caps;
1062 jvmtiEventCallbacks callbacks;
1063
1064 memset(&caps, 0, sizeof(caps));
1065 caps.can_generate_sampled_object_alloc_events = 1;
1066 if (check_error(second_jvmti->AddCapabilities(&caps),
1067 "Set the capability for second agent")) {
1068 return FALSE;
1069 }
1070
1071 memset(&callbacks, 0, sizeof(callbacks));
1072 callbacks.SampledObjectAlloc = &SampledObjectAlloc2;
1073
1074 if (check_error(second_jvmti->SetEventCallbacks(&callbacks,
1075 sizeof(jvmtiEventCallbacks)),
1076 " Set Event Callbacks for second agent")) {
1077 return FALSE;
1078 }
1079
1080 return TRUE;
1081 }
1082
1083 JNIEXPORT void JNICALL
1084 Java_MyPackage_HeapMonitor_enableVMEvents(JNIEnv* env, jclass cls) {
1085 check_error(jvmti->SetEventNotificationMode(
1086 JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, nullptr),
1087 "Set vm event notifications");
1088 }
1089
1090 JNIEXPORT jint JNICALL
1091 Java_MyPackage_HeapMonitorVMEventsTest_vmEvents(JNIEnv* env, jclass cls) {
1092 return event_storage_number_additions(&second_global_event_storage);
1093 }
1094
1095 JNIEXPORT jint JNICALL
1096 Java_MyPackage_HeapMonitor_sampledEvents(JNIEnv* env, jclass cls) {
1097 return event_storage_number_additions(&global_event_storage);
1098 }
1099
1100 static jobject allocate_object(JNIEnv* env) {
1101 // Construct an Object.
1102 jclass cls = env->FindClass("java/lang/Object");
1103 jmethodID constructor;
1104 jobject result;
1105
1106 if (env->ExceptionOccurred() || cls == nullptr) {
1107 env->FatalError("Error in jni FindClass: Cannot find Object class\n");
1108 }
1109
1110 constructor = env->GetMethodID(cls, "<init>", "()V");
1111 if (env->ExceptionOccurred() || constructor == nullptr) {
1112 env->FatalError("Error in jni GetMethodID: Cannot find Object class constructor\n");
1113 }
1114
1115 // Call back constructor to allocate a new instance, with an int argument
1116 result = env->NewObject(cls, constructor);
1117
1118 if (env->ExceptionOccurred() || result == nullptr) {
1119 env->FatalError("Error in jni NewObject: Cannot allocate an object\n");
1120 }
1121 return result;
1122 }
1123
1124 // Ensure we got a callback for the test.
1125 static int did_recursive_callback_test;
1126
1127 JNIEXPORT
1128 void JNICALL RecursiveSampledObjectAlloc(jvmtiEnv *jvmti_env,
1129 JNIEnv* jni_env,
1130 jthread thread,
1131 jobject object,
1132 jclass object_klass,
1133 jlong size) {
1134 // Basically ensure that if we were to allocate objects, we would not have an
1135 // infinite recursion here.
1136 int i;
1137 for (i = 0; i < 1000; i++) {
1138 if (allocate_object(jni_env) == nullptr) {
1139 jni_env->FatalError("allocate_object returned null\n");
1140 }
1141 }
1142
1143 did_recursive_callback_test = 1;
1144 }
1145
1146 JNIEXPORT jboolean JNICALL
1147 Java_MyPackage_HeapMonitorRecursiveTest_didCallback(JNIEnv* env, jclass cls) {
1148 return did_recursive_callback_test != 0;
1149 }
1150
1151 JNIEXPORT void JNICALL
1152 Java_MyPackage_HeapMonitorRecursiveTest_setCallbackToCallAllocateSomeMore(JNIEnv* env, jclass cls) {
1153 jvmtiEventCallbacks callbacks;
1154
1155 memset(&callbacks, 0, sizeof(callbacks));
1156 callbacks.SampledObjectAlloc = &RecursiveSampledObjectAlloc;
1157
1158 if (check_error(jvmti->SetEventCallbacks(&callbacks,
1159 sizeof(jvmtiEventCallbacks)),
1160 "Set Event Callbacks")) {
1161 env->FatalError("Cannot reset the callback.");
1162 }
1163 }
1164
1165 }