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 err = jvmti->GetStackTrace(thread, 0, 64, frames, &count);
533 if (err == JVMTI_ERROR_NONE && count >= 1) {
534 ObjectTrace* live_object;
535 jvmtiFrameInfo* allocated_frames = (jvmtiFrameInfo*) malloc(count * sizeof(*allocated_frames));
536 memcpy(allocated_frames, frames, count * sizeof(*allocated_frames));
537
538 live_object = (ObjectTrace*) malloc(sizeof(*live_object));
539 live_object->frames = allocated_frames;
540 live_object->frame_count = count;
541 live_object->size = size;
542 live_object->thread = thread;
543 live_object->object = jni->NewWeakGlobalRef(object);
544
545 if (jni->ExceptionOccurred()) {
546 jni->FatalError("Error in event_storage_add: Exception in jni NewWeakGlobalRef");
547 }
548
549 // Only now lock and get things done quickly.
550 event_storage_lock(storage);
551
552 storage->live_object_additions++;
553
554 if (storage->live_object_count >= storage->live_object_size) {
555 event_storage_augment_storage(storage);
556 }
557 assert(storage->live_object_count < storage->live_object_size);
558
559 if (PRINT_OUT) {
560 fprintf(stderr, "Adding trace for thread %p, frame_count %d, storage %p\n",
561 thread, count, storage);
562 print_out_frames(jni, live_object);
563 }
564 storage->live_objects[storage->live_object_count] = live_object;
565 storage->live_object_count++;
566
567 event_storage_unlock(storage);
568 }
569 }
570
571 static void event_storage_compact(EventStorage* storage, JNIEnv* jni) {
572 int max, i, dest;
573 ObjectTrace** live_objects;
574
575 event_storage_lock_compaction(storage);
576 storage->compaction_required = 0;
577 event_storage_unlock_compaction(storage);
578
579 event_storage_lock(storage);
580
581 max = storage->live_object_count;
582 live_objects = storage->live_objects;
583
584 for (i = 0, dest = 0; i < max; i++) {
585 ObjectTrace* live_object = live_objects[i];
586 jweak object = live_object->object;
587
588 if (!jni->IsSameObject(object, nullptr)) {
589 if (dest != i) {
590 live_objects[dest] = live_object;
591 dest++;
592 }
593 } else {
594 jni->DeleteWeakGlobalRef(object);
595 live_object->object = nullptr;
596
597 event_storage_add_garbage_collected_object(storage, live_object);
598 }
599 }
600
601 storage->live_object_count = dest;
602 event_storage_unlock(storage);
603 }
604
605 static void event_storage_free_objects(ObjectTrace** array, int max) {
606 int i;
607 for (i = 0; i < max; i++) {
608 free(array[i]), array[i] = nullptr;
609 }
610 }
611
612 static void event_storage_reset(EventStorage* storage) {
613 event_storage_lock(storage);
614
615 // Reset everything except the mutex and the garbage collection.
616 event_storage_free_objects(storage->live_objects,
617 storage->live_object_count);
618 storage->live_object_additions = 0;
619 storage->live_object_size = 0;
620 storage->live_object_count = 0;
621 free(storage->live_objects), storage->live_objects = nullptr;
622
623 event_storage_free_objects(storage->garbage_collected_objects,
624 storage->garbage_history_size);
625
626 storage->compaction_required = 0;
627 storage->garbage_history_index = 0;
628
629 event_storage_unlock(storage);
630 }
631
632 static int event_storage_number_additions(EventStorage* storage) {
633 int result;
634 event_storage_lock(storage);
635 result = storage->live_object_additions;
636 event_storage_unlock(storage);
637 return result;
638 }
639
640 // Start of the JVMTI agent code.
641 static const char *EXC_CNAME = "java/lang/Exception";
642
643 static int check_error(jvmtiError err, const char *s) {
644 if (err != JVMTI_ERROR_NONE) {
645 printf(" ## %s error: %d\n", s, err);
646 return 1;
647 }
648 return 0;
649 }
650
651 static int check_capability_error(jvmtiError err, const char *s) {
652 if (err != JVMTI_ERROR_NONE) {
653 if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY) {
654 return 0;
655 }
656 fprintf(stderr, " ## %s error: %d\n", s, err);
657 }
658 return 1;
659 }
660
661 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
662
663 JNIEXPORT
664 jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
665 return Agent_Initialize(jvm, options, reserved);
666 }
667
668 JNIEXPORT
669 jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
670 return Agent_Initialize(jvm, options, reserved);
671 }
672
673 JNIEXPORT
674 jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
675 return JNI_VERSION_1_8;
676 }
677
678 #define MAX_THREADS 500
679
680 typedef struct ThreadStats {
681 int thread_count;
682 int counts[MAX_THREADS];
683 char* threads[MAX_THREADS];
684 } ThreadStats;
685
686 static ThreadStats thread_stats;
687
688 JNIEXPORT jboolean JNICALL
689 Java_MyPackage_HeapMonitorThreadDisabledTest_checkThreadSamplesOnlyFrom(
690 JNIEnv* env, jclass cls, jthread thread) {
691 jvmtiThreadInfo info;
692 jvmtiError err;
693 char* expected_name;
694 int found_thread = FALSE;
695
696 err = jvmti->GetThreadInfo(thread, &info);
697 expected_name = info.name;
698
699 if (err != JVMTI_ERROR_NONE) {
700 fprintf(stderr, "Failed to get thread information\n");
701 return FALSE;
702 }
703
704 if (thread_stats.thread_count != 1) {
705 fprintf(stderr, "Wrong thread number: %d (expected 1)\n",
706 thread_stats.thread_count);
707 return FALSE;
708 }
709
710 if (strcmp(expected_name, thread_stats.threads[0]) != 0) {
711 fprintf(stderr, "Unexpected thread name: '%s' (expected '%s')\n",
712 thread_stats.threads[0], expected_name);
713 return FALSE;
714 }
715
716 return TRUE;
717 }
718
719 static void add_thread_count(jthread thread) {
720 int i;
721 jvmtiThreadInfo info;
722 jvmtiError err;
723
724 err = jvmti->GetThreadInfo(thread, &info);
725 if (err != JVMTI_ERROR_NONE) {
726 fprintf(stderr, "Thread info for %p failed, ignoring thread count\n",
727 thread);
728 return;
729 }
730
731 event_storage_lock(&global_event_storage);
732 for (i = 0; i < thread_stats.thread_count; i++) {
733 if (!strcmp(thread_stats.threads[i], info.name)) {
734 thread_stats.counts[i]++;
735 event_storage_unlock(&global_event_storage);
736 return;
737 }
738 }
739
740 thread_stats.threads[thread_stats.thread_count] = info.name;
741 thread_stats.counts[thread_stats.thread_count]++;
742 thread_stats.thread_count++;
743 event_storage_unlock(&global_event_storage);
744 }
745
746 JNIEXPORT void JNICALL
747 Java_MyPackage_HeapMonitorThreadDisabledTest_enableSamplingEvents(
748 JNIEnv* env, jclass cls, jthread thread) {
749 fprintf(stderr, "Enabling for %p\n", thread);
750 check_error(jvmti->SetEventNotificationMode(
751 JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, thread),
752 "Set event notifications for a single thread");
753 }
754
755 static void print_thread_stats() {
756 int i;
757 event_storage_lock(&global_event_storage);
758 fprintf(stderr, "Thread count:\n");
759 for (i = 0; i < thread_stats.thread_count; i++) {
760 fprintf(stderr, "\t%s: %d\n", thread_stats.threads[i], thread_stats.counts[i]);
761 }
762 event_storage_unlock(&global_event_storage);
763 }
764
765 JNIEXPORT
766 void JNICALL SampledObjectAlloc(jvmtiEnv *jvmti_env,
767 JNIEnv* jni_env,
768 jthread thread,
769 jobject object,
770 jclass object_klass,
771 jlong size) {
772 add_thread_count(thread);
773
774 if (event_storage_get_compaction_required(&global_event_storage)) {
775 event_storage_compact(&global_event_storage, jni_env);
776 }
777
778 event_storage_add(&global_event_storage, jni_env, thread, object,
779 object_klass, size);
780 }
781
782 JNIEXPORT
783 void JNICALL VMObjectAlloc(jvmtiEnv *jvmti_env,
784 JNIEnv* jni_env,
785 jthread thread,
786 jobject object,
787 jclass object_klass,
788 jlong size) {
789 event_storage_add(&second_global_event_storage, jni_env, thread, object,
790 object_klass, size);
791 }
792
793 JNIEXPORT
794 void JNICALL GarbageCollectionFinish(jvmtiEnv *jvmti_env) {
795 event_storage_set_compaction_required(&global_event_storage);
796 }
797
798 static int enable_notifications() {
799 if (check_error(jvmti->SetEventNotificationMode(
800 JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, nullptr),
801 "Set event notifications")) {
802 return 1;
803 }
804
805 return check_error(jvmti->SetEventNotificationMode(
806 JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, nullptr),
807 "Set event notifications");
808 }
809
810 static
811 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
812 jint res;
813 jvmtiEventCallbacks callbacks;
814 jvmtiCapabilities caps;
815
816 res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION);
817 if (res != JNI_OK || jvmti == nullptr) {
818 fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n");
819 return JNI_ERR;
820 }
821
822 // Get second jvmti environment.
823 res = jvm->GetEnv((void **) &second_jvmti, JVMTI_VERSION);
824 if (res != JNI_OK || second_jvmti == nullptr) {
825 fprintf(stderr, "Error: wrong result of a valid second call to GetEnv!\n");
826 return JNI_ERR;
827 }
828
829 if (PRINT_OUT) {
830 fprintf(stderr, "Storage is at %p, secondary is at %p\n",
831 &global_event_storage, &second_global_event_storage);
832 }
833
834 jvmti->CreateRawMonitor("storage_monitor",
835 &global_event_storage.storage_monitor);
836 jvmti->CreateRawMonitor("second_storage_monitor",
837 &second_global_event_storage.storage_monitor);
838
839 jvmti->CreateRawMonitor("compaction_monitor",
840 &global_event_storage.compaction_monitor);
841 jvmti->CreateRawMonitor("second_compaction_monitor",
842 &second_global_event_storage.compaction_monitor);
843
844 event_storage_set_garbage_history(&global_event_storage, 200);
845 event_storage_set_garbage_history(&second_global_event_storage, 200);
846
847 memset(&callbacks, 0, sizeof(callbacks));
848 callbacks.SampledObjectAlloc = &SampledObjectAlloc;
849 callbacks.VMObjectAlloc = &VMObjectAlloc;
850 callbacks.GarbageCollectionFinish = &GarbageCollectionFinish;
851
852 memset(&caps, 0, sizeof(caps));
853 // Get line numbers, sample events, filename, and gc events for the tests.
854 caps.can_get_line_numbers = 1;
855 caps.can_get_source_file_name = 1;
856 caps.can_generate_garbage_collection_events = 1;
857 caps.can_generate_sampled_object_alloc_events = 1;
858 caps.can_generate_vm_object_alloc_events = 1;
859 if (check_error(jvmti->AddCapabilities(&caps), "Add capabilities")) {
860 return JNI_ERR;
861 }
862
863 if (check_error(jvmti->SetEventCallbacks(&callbacks,
864 sizeof(jvmtiEventCallbacks)),
865 " Set Event Callbacks")) {
866 return JNI_ERR;
867 }
868 return JNI_OK;
869 }
870
871 JNIEXPORT void JNICALL
872 Java_MyPackage_HeapMonitor_setSamplingInterval(JNIEnv* env, jclass cls, jint value) {
873 jvmti->SetHeapSamplingInterval(value);
874 }
875
876 JNIEXPORT jboolean JNICALL
877 Java_MyPackage_HeapMonitor_eventStorageIsEmpty(JNIEnv* env, jclass cls) {
878 return event_storage_get_count(&global_event_storage) == 0;
879 }
880
881 JNIEXPORT jint JNICALL
882 Java_MyPackage_HeapMonitor_getEventStorageElementCount(JNIEnv* env, jclass cls) {
883 return event_storage_get_count(&global_event_storage);
884 }
885
886 JNIEXPORT void JNICALL
887 Java_MyPackage_HeapMonitor_enableSamplingEvents(JNIEnv* env, jclass cls) {
888 enable_notifications();
889 }
890
891 JNIEXPORT void JNICALL
892 Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) {
893 check_error(jvmti->SetEventNotificationMode(
894 JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, nullptr),
895 "Set event notifications");
896
897 check_error(jvmti->SetEventNotificationMode(
898 JVMTI_DISABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, nullptr),
899 "Garbage Collection Finish");
900 }
901
902 static ExpectedContentFrame *get_native_frames(JNIEnv* env, jclass cls,
903 jobjectArray frames) {
904 ExpectedContentFrame *native_frames;
905 jsize size = env->GetArrayLength(frames);
906
907 if (env->ExceptionOccurred()) {
908 env->FatalError("get_native_frames failed with the GetArrayLength call");
909 }
910
911 native_frames = reinterpret_cast<ExpectedContentFrame*> (malloc(size * sizeof(*native_frames)));
912
913 if (native_frames == nullptr) {
914 env->FatalError("Error in get_native_frames: malloc returned null\n");
915 }
916
917 if (fill_native_frames(env, frames, native_frames, size) != 0) {
918 env->FatalError("Error in get_native_frames: fill_native_frames returned failed status\n");
919 }
920
921 return native_frames;
922 }
923
924 JNIEXPORT jboolean JNICALL
925 Java_MyPackage_HeapMonitor_obtainedEvents(JNIEnv* env, jclass cls,
926 jobjectArray frames) {
927 jboolean result;
928 jsize size = env->GetArrayLength(frames);
929 ExpectedContentFrame *native_frames = get_native_frames(env, cls, frames);
930
931 result = event_storage_contains(env, &global_event_storage, native_frames,
932 size);
933
934 free(native_frames), native_frames = nullptr;
935 return result;
936 }
937
938 JNIEXPORT jboolean JNICALL
939 Java_MyPackage_HeapMonitor_garbageContains(JNIEnv* env, jclass cls,
940 jobjectArray frames) {
941 jboolean result;
942 jsize size = env->GetArrayLength(frames);
943 ExpectedContentFrame *native_frames = get_native_frames(env, cls, frames);
944
945 result = event_storage_garbage_contains(env, &global_event_storage,
946 native_frames, size);
947
948 free(native_frames), native_frames = nullptr;
949 return result;
950 }
951
952 JNIEXPORT jlong JNICALL
953 Java_MyPackage_HeapMonitor_getSize(JNIEnv* env, jclass cls,
954 jobjectArray frames) {
955 jlong result = 0;
956 jsize size = env->GetArrayLength(frames);
957 ExpectedContentFrame *native_frames = get_native_frames(env, cls, frames);
958
959 result = event_storage_get_size(env, &global_event_storage,
960 native_frames, size);
961
962 free(native_frames), native_frames = nullptr;
963 return result;
964 }
965
966 JNIEXPORT void JNICALL
967 Java_MyPackage_HeapMonitor_forceGarbageCollection(JNIEnv* env, jclass cls) {
968 check_error(jvmti->ForceGarbageCollection(),
969 "Forced Garbage Collection");
970 }
971
972 JNIEXPORT void JNICALL
973 Java_MyPackage_HeapMonitor_resetEventStorage(JNIEnv* env, jclass cls) {
974 event_storage_reset(&global_event_storage);
975 event_storage_reset(&second_global_event_storage);
976 }
977
978 JNIEXPORT jboolean JNICALL
979 Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env,
980 jclass cls) {
981 jvmtiCapabilities caps;
982 memset(&caps, 0, sizeof(caps));
983 caps.can_generate_sampled_object_alloc_events = 1;
984 if (check_error(jvmti->RelinquishCapabilities(&caps),
985 "Add capabilities\n")){
986 return FALSE;
987 }
988
989 if (check_capability_error(jvmti->SetHeapSamplingInterval(1<<19),
990 "Set Heap Sampling Interval")) {
991 return FALSE;
992 }
993 return TRUE;
994 }
995
996 JNIEXPORT jboolean JNICALL
997 Java_MyPackage_HeapMonitorIllegalArgumentTest_testIllegalArgument(JNIEnv *env,
998 jclass cls) {
999 if (check_error(jvmti->SetHeapSamplingInterval(0),
1000 "Sampling interval 0 failed\n")){
1001 return FALSE;
1002 }
1003
1004 if (check_error(jvmti->SetHeapSamplingInterval(1024),
1005 "Sampling interval 1024 failed\n")){
1006 return FALSE;
1007 }
1008
1009 if (!check_error(jvmti->SetHeapSamplingInterval(-1),
1010 "Sampling interval -1 passed\n")){
1011 return FALSE;
1012 }
1013
1014 if (!check_error(jvmti->SetHeapSamplingInterval(-1024),
1015 "Sampling interval -1024 passed\n")){
1016 return FALSE;
1017 }
1018
1019 return TRUE;
1020 }
1021
1022 JNIEXPORT jdouble JNICALL
1023 Java_MyPackage_HeapMonitor_getAverageSize(JNIEnv *env, jclass cls) {
1024 return event_storage_get_average_size(&global_event_storage);
1025 }
1026
1027 typedef struct sThreadsFound {
1028 jthread* threads;
1029 int num_threads;
1030 } ThreadsFound;
1031
1032 JNIEXPORT jboolean JNICALL
1033 Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls,
1034 jint num_threads) {
1035 print_thread_stats();
1036 // Ensure we got stacks from at least num_threads.
1037 return thread_stats.thread_count >= num_threads;
1038 }
1039
1040 JNIEXPORT
1041 void JNICALL SampledObjectAlloc2(jvmtiEnv *jvmti_env,
1042 JNIEnv* jni_env,
1043 jthread thread,
1044 jobject object,
1045 jclass object_klass,
1046 jlong size) {
1047 // Nop for now, two agents are not yet implemented.
1048 assert(0);
1049 }
1050
1051 JNIEXPORT jboolean JNICALL
1052 Java_MyPackage_HeapMonitorTwoAgentsTest_enablingSamplingInSecondaryAgent(
1053 JNIEnv* env, jclass cls) {
1054 // Currently this method should be failing directly at the AddCapability step
1055 // but the implementation is correct for when multi-agent support is enabled.
1056 jvmtiCapabilities caps;
1057 jvmtiEventCallbacks callbacks;
1058
1059 memset(&caps, 0, sizeof(caps));
1060 caps.can_generate_sampled_object_alloc_events = 1;
1061 if (check_error(second_jvmti->AddCapabilities(&caps),
1062 "Set the capability for second agent")) {
1063 return FALSE;
1064 }
1065
1066 memset(&callbacks, 0, sizeof(callbacks));
1067 callbacks.SampledObjectAlloc = &SampledObjectAlloc2;
1068
1069 if (check_error(second_jvmti->SetEventCallbacks(&callbacks,
1070 sizeof(jvmtiEventCallbacks)),
1071 " Set Event Callbacks for second agent")) {
1072 return FALSE;
1073 }
1074
1075 return TRUE;
1076 }
1077
1078 JNIEXPORT void JNICALL
1079 Java_MyPackage_HeapMonitor_enableVMEvents(JNIEnv* env, jclass cls) {
1080 check_error(jvmti->SetEventNotificationMode(
1081 JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, nullptr),
1082 "Set vm event notifications");
1083 }
1084
1085 JNIEXPORT jint JNICALL
1086 Java_MyPackage_HeapMonitorVMEventsTest_vmEvents(JNIEnv* env, jclass cls) {
1087 return event_storage_number_additions(&second_global_event_storage);
1088 }
1089
1090 JNIEXPORT jint JNICALL
1091 Java_MyPackage_HeapMonitor_sampledEvents(JNIEnv* env, jclass cls) {
1092 return event_storage_number_additions(&global_event_storage);
1093 }
1094
1095 static jobject allocate_object(JNIEnv* env) {
1096 // Construct an Object.
1097 jclass cls = env->FindClass("java/lang/Object");
1098 jmethodID constructor;
1099 jobject result;
1100
1101 if (env->ExceptionOccurred() || cls == nullptr) {
1102 env->FatalError("Error in jni FindClass: Cannot find Object class\n");
1103 }
1104
1105 constructor = env->GetMethodID(cls, "<init>", "()V");
1106 if (env->ExceptionOccurred() || constructor == nullptr) {
1107 env->FatalError("Error in jni GetMethodID: Cannot find Object class constructor\n");
1108 }
1109
1110 // Call back constructor to allocate a new instance, with an int argument
1111 result = env->NewObject(cls, constructor);
1112
1113 if (env->ExceptionOccurred() || result == nullptr) {
1114 env->FatalError("Error in jni NewObject: Cannot allocate an object\n");
1115 }
1116 return result;
1117 }
1118
1119 // Ensure we got a callback for the test.
1120 static int did_recursive_callback_test;
1121
1122 JNIEXPORT
1123 void JNICALL RecursiveSampledObjectAlloc(jvmtiEnv *jvmti_env,
1124 JNIEnv* jni_env,
1125 jthread thread,
1126 jobject object,
1127 jclass object_klass,
1128 jlong size) {
1129 // Basically ensure that if we were to allocate objects, we would not have an
1130 // infinite recursion here.
1131 int i;
1132 for (i = 0; i < 1000; i++) {
1133 if (allocate_object(jni_env) == nullptr) {
1134 jni_env->FatalError("allocate_object returned null\n");
1135 }
1136 }
1137
1138 did_recursive_callback_test = 1;
1139 }
1140
1141 JNIEXPORT jboolean JNICALL
1142 Java_MyPackage_HeapMonitorRecursiveTest_didCallback(JNIEnv* env, jclass cls) {
1143 return did_recursive_callback_test != 0;
1144 }
1145
1146 JNIEXPORT void JNICALL
1147 Java_MyPackage_HeapMonitorRecursiveTest_setCallbackToCallAllocateSomeMore(JNIEnv* env, jclass cls) {
1148 jvmtiEventCallbacks callbacks;
1149
1150 memset(&callbacks, 0, sizeof(callbacks));
1151 callbacks.SampledObjectAlloc = &RecursiveSampledObjectAlloc;
1152
1153 if (check_error(jvmti->SetEventCallbacks(&callbacks,
1154 sizeof(jvmtiEventCallbacks)),
1155 "Set Event Callbacks")) {
1156 env->FatalError("Cannot reset the callback.");
1157 }
1158 }
1159
1160 }