Repository navigation
Expand file tree
/
Copy pathcount_instructions.c
More file actions
302 lines (253 loc) · 9.45 KB
/
Copy pathcount_instructions.c
File metadata and controls
302 lines (253 loc) · 9.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
#include "jni.h"
#include "jvmti.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"
#define DETAILED_RESULTS
/*
* Agent for Java 1.6+ virtual machine. Counts #bytecode instructions executed.
* Some code stems from the example agent of the jvmi development package.
* @author Juergen Graf <juergen.graf@gmail.com>
*/
#define NEW(type) (type *)h_alloc(sizeof(type))
static inline void *
h_alloc(const int size)
{
void *addr = calloc(1, size);
if (addr != NULL) {
return addr;
} else {
fprintf(stderr, "Could not allocate %d bytes.\n", size);
exit(EXIT_FAILURE);
}
}
/* Global agent data structure */
typedef struct {
/* JVMTI Environment */
jvmtiEnv *jvmti;
jboolean vm_is_started;
/* Data access Lock */
jrawMonitorID lock;
} global_agent_data_t;
typedef struct {
unsigned long counter;
jmethodID id;
} method_stat_t;
static global_agent_data_t *gdata;
static jvmtiEnv *jvmti = NULL;
static jvmtiCapabilities capa;
static unsigned long long num_instructions_proccessed;
#ifdef DETAILED_RESULTS
static map_t *map;
static jmethodID last_jmethod;
static method_stat_t *cur_method_stat = NULL;
#endif
/* Every JVMTI interface returns an error code, which should be checked
* to avoid any cascading errors down the line.
* The interface GetErrorName() returns the actual enumeration constant
* name, making the error messages much easier to understand.
*/
static inline void
check_jvmti_error(jvmtiEnv * const jvmti, const jvmtiError errnum, const char * const str)
{
char *errnum_str = NULL;
if (errnum == JVMTI_ERROR_NONE)
return;
(void)(*jvmti)->GetErrorName(jvmti, errnum, &errnum_str);
printf("ERROR: JVMTI: %d(%s): %s\n", errnum, (errnum_str == NULL ? "Unknown" : errnum_str),
(str == NULL ? "" : str));
}
/* Enter a critical section by doing a JVMTI Raw Monitor Enter */
static inline void
enter_critical_section(jvmtiEnv * const jvmti)
{
const jvmtiError error = (*jvmti)->RawMonitorEnter(jvmti, gdata->lock);
check_jvmti_error(jvmti, error, "Cannot enter with raw monitor");
}
/* Exit a critical section by doing a JVMTI Raw Monitor Exit */
static inline void
exit_critical_section(jvmtiEnv * const jvmti)
{
const jvmtiError error = (*jvmti)->RawMonitorExit(jvmti, gdata->lock);
check_jvmti_error(jvmti, error, "Cannot exit with raw monitor");
}
void
describe(const jvmtiError err)
{
char *descr;
const jvmtiError error = (*jvmti)->GetErrorName(jvmti, err, &descr);
if (error == JVMTI_ERROR_NONE) {
printf("%s", descr);
} else {
printf("error [%d]", err);
}
}
#ifdef DETAILED_RESULTS
static void
jvmti_dealloc(unsigned char * const ptr)
{
const jvmtiError error = (*jvmti)->Deallocate(jvmti, ptr);
if (error != JVMTI_ERROR_NONE) {
printf("(jvmti_dealloc) Error expected: %d, got: %d\n", JVMTI_ERROR_NONE, error);
describe(error);
printf("\n");
}
}
int
print_method_info(void * const value, void * const f_arg)
{
(void) f_arg;
const jmethodID method_id = ((method_stat_t *)value)->id;
const unsigned long counter = ((method_stat_t *)value)->counter;
jvmtiError error;
char *name;
char *sig;
char *ptr;
error = (*jvmti)->GetMethodName(jvmti, method_id, &name, &sig, &ptr);
check_jvmti_error(jvmti, error, "Cannot get method name");
jclass decl_class;
error = (*jvmti)->GetMethodDeclaringClass(jvmti, method_id, &decl_class);
check_jvmti_error(jvmti, error, "Cannot get declaring class");
char *class_sig;
char *class_status;
error = (*jvmti)->GetClassSignature(jvmti, decl_class, &class_sig, &class_status);
check_jvmti_error(jvmti, error, "Cannot get class signature");
printf("%ld\tclass %s -> %s(%s)\n", counter, class_sig, name, sig);
jvmti_dealloc((unsigned char *) name);
jvmti_dealloc((unsigned char *) sig);
jvmti_dealloc((unsigned char *) ptr);
jvmti_dealloc((unsigned char *) class_sig);
jvmti_dealloc((unsigned char *) class_status);
return MAP_OK;
}
#endif
// Exception callback
// VM Death callback
static void JNICALL
callbackVMDeath(jvmtiEnv * const jvmti_env, JNIEnv * const jni_env)
{
(void) jvmti_env;
(void) jni_env;
#ifdef DETAILED_RESULTS
enter_critical_section(jvmti);
hashmap_iterate(map, &print_method_info, NULL);
exit_critical_section(jvmti);
#endif
}
// VM init callback
static void JNICALL
callbackVMInit(jvmtiEnv * const jvmti_env, JNIEnv * const jni_env, const jthread thread)
{
(void) jvmti_env;
(void) jni_env;
(void) thread;
}
static void JNICALL
callbackSingleStep(jvmtiEnv * const jvmti_env, JNIEnv * const jni_env, const jthread thread, const jmethodID method,
const jlocation location)
{
(void) jvmti_env;
(void) jni_env;
(void) thread;
(void) location;
(void) method;
#ifdef DETAILED_RESULTS
enter_critical_section(jvmti);
if (last_jmethod != method) {
last_jmethod = method;
if (hashmap_get(map, (map_key_t) method, (void *) &cur_method_stat) == MAP_MISSING) {
cur_method_stat = NEW(method_stat_t);
cur_method_stat->id = method;
(void)hashmap_put(map, (map_key_t) method, cur_method_stat);
}
}
cur_method_stat->counter++;
num_instructions_proccessed++;
exit_critical_section(jvmti);
#else
__sync_fetch_and_add(&num_instructions_proccessed, 1);
#endif
}
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM * const jvm, char * const options, void * const reserved)
{
(void) options;
(void) reserved;
static global_agent_data_t data;
jvmtiError error;
jvmtiEventCallbacks callbacks;
num_instructions_proccessed = 0;
#ifdef DETAILED_RESULTS
map = hashmap_new();
last_jmethod = NULL;
cur_method_stat = NULL;
#endif
/* Setup initial global agent data area
* Use of static/extern data should be handled carefully here.
* We need to make sure that we are able to cleanup after ourselves
* so anything allocated in this library needs to be freed in
* the Agent_OnUnload() function.
*/
(void)memset((void*)&data, 0, sizeof(data));
gdata = &data;
/* We need to first get the jvmtiEnv* or JVMTI environment */
const jint res = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1_0);
if (res != JNI_OK || jvmti == NULL) {
/* This means that the VM was unable to obtain this version of the
* JVMTI interface, this is a fatal error.
*/
printf("ERROR: Unable to access JVMTI Version 1 (0x%x), is your J2SE a 1.5 or newer version? JNIEnv's "
"GetEnv() returned %d\n", JVMTI_VERSION_1, res);
}
/* Here we save the jvmtiEnv* for Agent_OnUnload(). */
gdata->jvmti = jvmti;
(void)memset(&capa, 0, sizeof(jvmtiCapabilities));
//capa.can_signal_thread = 1;
//capa.can_get_owned_monitor_info = 1;
//capa.can_generate_method_entry_events = 1;
//capa.can_generate_exception_events = 1;
//capa.can_generate_vm_object_alloc_events = 1;
//capa.can_tag_objects = 1;
capa.can_generate_single_step_events = 1;
error = (*jvmti)->AddCapabilities(jvmti, &capa);
check_jvmti_error(jvmti, error, "Unable to get necessary JVMTI capabilities.");
(void)memset(&callbacks, 0, sizeof(callbacks));
callbacks.VMInit = &callbackVMInit; /* JVMTI_EVENT_VM_INIT */
callbacks.VMDeath = &callbackVMDeath; /* JVMTI_EVENT_VM_DEATH */
callbacks.SingleStep = &callbackSingleStep; /* JVMTI_EVENT_SINGLE_STEP */
error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
check_jvmti_error(jvmti, error, "Cannot set jvmti callbacks");
/* At first the only initial events we are interested in are VM
* initialization, VM death, and Class File Loads.
* Once the VM is initialized we will request more events.
*/
error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, (jthread)NULL);
check_jvmti_error(jvmti, error, "Cannot set event notification: JVMTI_EVENT_VM_INIT");
error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, (jthread)NULL);
check_jvmti_error(jvmti, error, "Cannot set event notification: JVMTI_EVENT_VM_DEATH");
error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_SINGLE_STEP, (jthread)NULL);
check_jvmti_error(jvmti, error, "Cannot set event notification: JVMTI_EVENT_SINGLE_STEP");
/* Here we create a raw monitor for our use in this agent to
* protect critical sections of code.
*/
error = (*jvmti)->CreateRawMonitor(jvmti, "agent data", &(gdata->lock));
check_jvmti_error(jvmti, error, "Cannot create raw monitor");
/* We return JNI_OK to signify success */
return JNI_OK;
}
/* Agent_OnUnload: This is called immediately before the shared library is
* unloaded. This is the last code executed.
*/
JNIEXPORT void JNICALL
Agent_OnUnload(JavaVM * const vm)
{
(void) vm;
/* Make sure all malloc/calloc/strdup space is freed */
#ifdef DETAILED_RESULTS
printf("%llu bytecode instructions in %i methods executed.\n", num_instructions_proccessed, hashmap_length(map));
hashmap_free(map);
#else
fprintf(stderr, "%llu\n", num_instructions_proccessed);
#endif
}