1 /*
2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 #include <string.h>
25 #include "jvmti.h"
26 
27 extern "C" {
28 
29 #define AGENT_NAME "agent2"
30 
31 static JavaVM *java_vm = NULL;
32 static jthread exp_thread = NULL;
33 static jvmtiEnv *jvmti2 = NULL;
34 static jint agent2_event_count = 0;
35 static bool fail_status = false;
36 
37 static void
check_jvmti_status(JNIEnv * env,jvmtiError err,const char * msg)38 check_jvmti_status(JNIEnv* env, jvmtiError err, const char* msg) {
39   if (err != JVMTI_ERROR_NONE) {
40     printf("check_jvmti_status: JVMTI function returned error: %d\n", err);
41     fail_status = true;
42     env->FatalError(msg);
43   }
44 }
45 
46 static void JNICALL
CompiledMethodLoad(jvmtiEnv * jvmti,jmethodID method,jint code_size,const void * code_addr,jint map_length,const jvmtiAddrLocationMap * map,const void * compile_info)47 CompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method,
48                    jint code_size, const void* code_addr,
49                    jint map_length, const jvmtiAddrLocationMap* map,
50                    const void* compile_info) {
51   JNIEnv* env = NULL;
52   jthread thread = NULL;
53   char* name = NULL;
54   char* sign = NULL;
55   jvmtiError err;
56 
57   // Posted on JavaThread's, so it is legal to obtain JNIEnv*
58   if (java_vm->GetEnv((void **) (&env), JNI_VERSION_9) != JNI_OK) {
59     fail_status = true;
60     return;
61   }
62 
63   err = jvmti->GetCurrentThread(&thread);
64   check_jvmti_status(env, err, "CompiledMethodLoad: Error in JVMTI GetCurrentThread");
65   if (!env->IsSameObject(thread, exp_thread)) {
66     return; // skip events from unexpected threads
67   }
68   agent2_event_count++;
69 
70   err = jvmti->GetMethodName(method, &name, &sign, NULL);
71   check_jvmti_status(env, err, "CompiledMethodLoad: Error in JVMTI GetMethodName");
72 
73   printf("%s: CompiledMethodLoad: %s%s\n", AGENT_NAME, name, sign);
74   fflush(0);
75 }
76 
77 JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM * jvm,char * options,void * reserved)78 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
79   jvmtiEventCallbacks callbacks;
80   jvmtiCapabilities caps;
81   jvmtiError err;
82 
83   java_vm = jvm;
84   if (jvm->GetEnv((void **) (&jvmti2), JVMTI_VERSION_9) != JNI_OK) {
85     return JNI_ERR;
86   }
87 
88   memset(&callbacks, 0, sizeof(callbacks));
89   callbacks.CompiledMethodLoad = &CompiledMethodLoad;
90 
91   err = jvmti2->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
92   if (err != JVMTI_ERROR_NONE) {
93     printf("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err);
94     fail_status = true;
95     return JNI_ERR;
96   }
97 
98   memset(&caps, 0, sizeof(caps));
99   caps.can_generate_compiled_method_load_events = 1;
100 
101   err = jvmti2->AddCapabilities(&caps);
102   if (err != JVMTI_ERROR_NONE) {
103     printf("Agent_OnLoad: Error in JVMTI AddCapabilities: %d\n", err);
104     fail_status = true;
105     return JNI_ERR;
106   }
107   return JNI_OK;
108 }
109 
110 JNIEXPORT void JNICALL
Java_MyPackage_GenerateEventsTest_agent2SetThread(JNIEnv * env,jclass cls,jthread thread)111 Java_MyPackage_GenerateEventsTest_agent2SetThread(JNIEnv *env, jclass cls, jthread thread) {
112   jvmtiError err;
113 
114   exp_thread = (jthread)env->NewGlobalRef(thread);
115 
116   err = jvmti2->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
117   check_jvmti_status(env, err, "setThread2: Error in JVMTI SetEventNotificationMode: JVMTI_ENABLE");
118 }
119 
120 JNIEXPORT jboolean JNICALL
Java_MyPackage_GenerateEventsTest_agent2FailStatus(JNIEnv * env,jclass cls)121 Java_MyPackage_GenerateEventsTest_agent2FailStatus(JNIEnv *env, jclass cls) {
122   jvmtiError err;
123 
124   err = jvmti2->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
125   check_jvmti_status(env, err, "check2: Error in JVMTI SetEventNotificationMode: JVMTI_DISABLE");
126 
127   printf("\n");
128   if (agent2_event_count == 0) {
129     printf("check2: Zero events in agent2 as expected\n");
130   } else {
131     fail_status = true;
132     printf("check2: Unexpected non-zero event count in agent2: %d\n", agent2_event_count);
133   }
134   printf("\n");
135   fflush(0);
136 
137   return fail_status;
138 }
139 
140 } // extern "C"
141