1 /*
2  * Copyright (c) 2003, 2018, 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 <stdio.h>
25 #include <string.h>
26 #include <jvmti.h>
27 #include "agent_common.h"
28 #include "JVMTITools.h"
29 
30 extern "C" {
31 
32 
33 #define METH_NUM 4 /* overall number of methods */
34 
35 #define STATUS_FAILED 2
36 #define PASSED 0
37 
38 /* line number matrix of original methods */
39 static int orig_ln[METH_NUM][8] = {
40     { 34, 0, 0, 0, 0, 0, 0, 0 }, /* <init> */
41     { 40,41,43, 0, 0, 0, 0, 0 }, /* checkIt */
42     { 55, 0, 0, 0, 0, 0, 0, 0 }, /* finMethod */
43     { 48,50,51,50,52, 0, 0, 0 }  /* statMethod */
44 };
45 
46 /* line number matrix of redefined methods */
47 static int redf_ln[METH_NUM][8] = {
48     { 38,39,40,41,42,43,44,46 }, /* <init> */
49     { 51,53,55, 0, 0, 0, 0, 0 }, /* checkIt */
50     { 64,66,67,68,69,70,72, 0 }, /* finMethod */
51     { 60, 0, 0, 0, 0, 0, 0, 0 }  /* statMethod */
52 };
53 
54 typedef struct {   /* line numbers of a method */
55     int inst;      /* type of a method: 0- static; 1- instance */
56     char *m_name;  /* a method name */
57     char *m_sign;  /* JVM signature of a method */
58     int lcount;    /* line numbers quantity */
59     jmethodID mid; /* JNI's method ID */
60 } methInfo;
61 
62 /* list of original methods */
63 static methInfo origMethInfo[] = {
64     { 1, (char*) "<init>", (char*) "()V", 1, NULL },
65     { 1, (char*) "checkIt", (char*) "(Ljava/io/PrintStream;Z)I", 3, NULL },
66     { 1, (char*) "finMethod", (char*) "(CJIJ)V", 1, NULL },
67     { 0, (char*) "statMethod", (char*) "(III)D", 5, NULL }
68 };
69 
70 /* list of redefined methods */
71 static methInfo redefMethInfo[] = {
72     { 1, (char*) "<init>", (char*) "()V", 8, NULL },
73     { 1, (char*) "checkIt", (char*) "(Ljava/io/PrintStream;Z)I", 3, NULL },
74     { 1, (char*) "finMethod", (char*) "(CJIJ)V", 7, NULL },
75     { 0, (char*) "statMethod", (char*) "(III)D", 1, NULL }
76 };
77 
78 static jvmtiEnv *jvmti = NULL;
79 static jvmtiCapabilities caps;
80 
81 #ifdef STATIC_BUILD
Agent_OnLoad_redefclass010(JavaVM * jvm,char * options,void * reserved)82 JNIEXPORT jint JNICALL Agent_OnLoad_redefclass010(JavaVM *jvm, char *options, void *reserved) {
83     return Agent_Initialize(jvm, options, reserved);
84 }
Agent_OnAttach_redefclass010(JavaVM * jvm,char * options,void * reserved)85 JNIEXPORT jint JNICALL Agent_OnAttach_redefclass010(JavaVM *jvm, char *options, void *reserved) {
86     return Agent_Initialize(jvm, options, reserved);
87 }
JNI_OnLoad_redefclass010(JavaVM * jvm,char * options,void * reserved)88 JNIEXPORT jint JNI_OnLoad_redefclass010(JavaVM *jvm, char *options, void *reserved) {
89     return JNI_VERSION_1_8;
90 }
91 #endif
Agent_Initialize(JavaVM * vm,char * options,void * reserved)92 jint  Agent_Initialize(JavaVM *vm, char *options, void *reserved) {
93     jint res;
94     jvmtiError err;
95 
96     res = vm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
97     if (res != JNI_OK) {
98         printf("%s: Failed to call GetEnv: error=%d\n", __FILE__, res);
99         return JNI_ERR;
100     }
101 
102     err = jvmti->GetPotentialCapabilities(&caps);
103     if (err != JVMTI_ERROR_NONE) {
104         printf("(GetPotentialCapabilities) unexpected error: %s (%d)\n",
105                TranslateError(err), err);
106         return JNI_ERR;
107     }
108 
109     err = jvmti->AddCapabilities(&caps);
110     if (err != JVMTI_ERROR_NONE) {
111         printf("(AddCapabilities) unexpected error: %s (%d)\n",
112                TranslateError(err), err);
113         return JNI_ERR;
114     }
115 
116     err = jvmti->GetCapabilities(&caps);
117     if (err != JVMTI_ERROR_NONE) {
118         printf("(GetCapabilities) unexpected error: %s (%d)\n",
119                TranslateError(err), err);
120         return JNI_ERR;
121     }
122 
123     if (!caps.can_redefine_classes) {
124         printf("Warning: RedefineClasses is not implemented\n");
125     }
126 
127     if (!caps.can_get_line_numbers) {
128         printf("Warning: no access to line number info\n");
129     }
130 
131     return JNI_OK;
132 }
133 
checkAttr(JNIEnv * env,jclass redefCls,methInfo methodsInfo[],jint vrb)134 int checkAttr(JNIEnv *env, jclass redefCls, methInfo methodsInfo[], jint vrb) {
135     jvmtiError err;
136     int i, j, chkval = 0;
137     int totRes = PASSED;
138     jint count = -1;
139     jvmtiLineNumberEntry *ln_table;
140 
141     if (!caps.can_get_line_numbers) {
142         return PASSED;
143     }
144 
145     for (i=0; i<METH_NUM; i++) {
146         /* get the JNI method ID for a method with name m_name and
147            signature m_sign */
148         if (methodsInfo[i].inst) { /* an instance method */
149             methodsInfo[i].mid = env->GetMethodID(redefCls,
150                     methodsInfo[i].m_name, methodsInfo[i].m_sign);
151         } else {                   /* a static method */
152             methodsInfo[i].mid = env->GetStaticMethodID(redefCls,
153                     methodsInfo[i].m_name, methodsInfo[i].m_sign);
154         }
155         if (methodsInfo[i].mid == NULL) {
156             printf("%s: Failed to get the method ID for the%s%s method \"%s\", signature \"%s\"\n",
157                 __FILE__, (vrb == 2) ? " original " : " ",
158                 methodsInfo[i].inst ? "instance" : "static",
159                 methodsInfo[i].m_name, methodsInfo[i].m_sign);
160             return STATUS_FAILED;
161         }
162 
163         /* get the LineNumberTable attribute */
164         err = jvmti->GetLineNumberTable(methodsInfo[i].mid, &count, &ln_table);
165         if (err != JVMTI_ERROR_NONE) {
166             printf("%s: Failed to call GetLineNumberTable(): error=%d: %s\n",
167                 __FILE__, err, TranslateError(err));
168             printf("\tfor the%s%s method \"%s\", signature \"%s\"\n\n",
169                 (vrb == 2) ? " original " : " ",
170                 methodsInfo[i].inst ? "instance" : "static",
171                 methodsInfo[i].m_name, methodsInfo[i].m_sign);
172             return STATUS_FAILED;
173         } else {
174             if (count != methodsInfo[i].lcount) {
175                 printf(
176                     "TEST %s %s method \"%s\", signature \"%s\": found %d lines in the LineNumberTable, expected %d\n",
177                     (vrb == 2) ? "BUG: original " : "FAILED:",
178                     methodsInfo[i].inst ? "instance" : "static",
179                     methodsInfo[i].m_name, methodsInfo[i].m_sign,
180                     count, methodsInfo[i].lcount);
181                 totRes = STATUS_FAILED;
182                 continue;
183             }
184             else if (vrb == 1)
185                 printf(
186                     "\nChecking line numbers in the LineNumberTable of the %s method \"%s\", signature \"%s\" ...\n"
187                     "\toverall number of lines: %d as expected\n",
188                     methodsInfo[i].inst ? "instance" : "static",
189                     methodsInfo[i].m_name, methodsInfo[i].m_sign, count);
190 
191             for (j=0; j<count; j++) {
192                 if (vrb == 2)
193                     chkval = orig_ln[i][j];
194                 else
195                     chkval = redf_ln[i][j];
196 
197                 if (ln_table[j].line_number != chkval) {
198                     printf(
199                         "TEST %s %s method \"%s\", signature \"%s\": "
200                         "entry #%d has value %d in the LineNumberTable, expected %d\n",
201                         (vrb == 2) ? "BUG: original" : "FAILED:",
202                         methodsInfo[i].inst ? "instance" : "static",
203                         methodsInfo[i].m_name, methodsInfo[i].m_sign,
204                         j, ln_table[j].line_number, chkval);
205                     totRes = STATUS_FAILED;
206                     break;
207                 }
208                 else if (vrb == 1)
209                     printf("\tentry #%d has value %d as expected\n",
210                         j, ln_table[j].line_number);
211             }
212         }
213     }
214     return totRes;
215 }
216 
217 JNIEXPORT jint JNICALL
Java_nsk_jvmti_RedefineClasses_redefclass010_checkOrigAttr(JNIEnv * env,jclass cls,jobject redefObj)218 Java_nsk_jvmti_RedefineClasses_redefclass010_checkOrigAttr(JNIEnv *env,
219         jclass cls, jobject redefObj) {
220     jclass redefCls = env->GetObjectClass(redefObj);
221     return checkAttr(env, redefCls, origMethInfo, 2);
222 }
223 
224 JNIEXPORT jint JNICALL
Java_nsk_jvmti_RedefineClasses_redefclass010_makeRedefinition(JNIEnv * env,jclass cls,jint vrb,jclass redefCls,jbyteArray classBytes)225 Java_nsk_jvmti_RedefineClasses_redefclass010_makeRedefinition(JNIEnv *env,
226         jclass cls, jint vrb, jclass redefCls, jbyteArray classBytes) {
227     jvmtiError err;
228     jvmtiClassDefinition classDef;
229 
230     if (jvmti == NULL) {
231         printf("JVMTI client was not properly loaded!\n");
232         return STATUS_FAILED;
233     }
234 
235     if (!caps.can_redefine_classes) {
236         return PASSED;
237     }
238 
239     /* fill the structure jvmtiClassDefinition */
240     classDef.klass = redefCls;
241     classDef.class_byte_count = env->GetArrayLength(classBytes);
242     classDef.class_bytes = (unsigned char *) env->GetByteArrayElements(classBytes, NULL);
243 
244     if (vrb)
245         printf("\n>>>>>>>> Invoke RedefineClasses():\n\tnew class byte count=%d\n",
246             classDef.class_byte_count);
247     err = jvmti->RedefineClasses(1, &classDef);
248     if (err != JVMTI_ERROR_NONE) {
249         printf("%s: Failed to call RedefineClasses(): error=%d: %s\n",
250             __FILE__, err, TranslateError(err));
251         printf("\tFor more info about this error see the JVMTI spec.\n");
252         return JNI_ERR;
253     }
254     if (vrb)
255         printf("<<<<<<<< RedefineClasses() is successfully done\n\n");
256 
257     return PASSED;
258 }
259 
260 JNIEXPORT jint JNICALL
Java_nsk_jvmti_RedefineClasses_redefclass010_getResult(JNIEnv * env,jclass cls,jint vrb,jobject redefObj)261 Java_nsk_jvmti_RedefineClasses_redefclass010_getResult(JNIEnv *env,
262     jclass cls, jint vrb, jobject redefObj) {
263     jclass redefCls = env->GetObjectClass(redefObj);
264     return checkAttr(env, redefCls, redefMethInfo, vrb);
265 }
266 
267 }
268