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