perf tools: add JVMTI agent library
[cascardo/linux.git] / tools / perf / jvmti / libjvmti.c
1 #include <sys/types.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <err.h>
6 #include <jvmti.h>
7 #include <limits.h>
8
9 #include "jvmti_agent.h"
10
11 static int has_line_numbers;
12 void *jvmti_agent;
13
14 static void JNICALL
15 compiled_method_load_cb(jvmtiEnv *jvmti,
16                         jmethodID method,
17                         jint code_size,
18                         void const *code_addr,
19                         jint map_length,
20                         jvmtiAddrLocationMap const *map,
21                         void const *compile_info __unused)
22 {
23         jvmtiLineNumberEntry *tab = NULL;
24         jclass decl_class;
25         char *class_sign = NULL;
26         char *func_name = NULL;
27         char *func_sign = NULL;
28         char *file_name= NULL;
29         char fn[PATH_MAX];
30         uint64_t addr = (uint64_t)(uintptr_t)code_addr;
31         jvmtiError ret;
32         jint nr_lines = 0;
33         size_t len;
34
35         ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method,
36                                                 &decl_class);
37         if (ret != JVMTI_ERROR_NONE) {
38                 warnx("jvmti: cannot get declaring class");
39                 return;
40         }
41
42         if (has_line_numbers && map && map_length) {
43
44                 ret = (*jvmti)->GetLineNumberTable(jvmti, method, &nr_lines, &tab);
45                 if (ret != JVMTI_ERROR_NONE) {
46                         warnx("jvmti: cannot get line table for method");
47                 } else {
48                         ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name);
49                         if (ret != JVMTI_ERROR_NONE) {
50                                 warnx("jvmti: cannot get source filename ret=%d", ret);
51                                 nr_lines = 0;
52                         }
53                 }
54         }
55
56         ret = (*jvmti)->GetClassSignature(jvmti, decl_class,
57                                           &class_sign, NULL);
58         if (ret != JVMTI_ERROR_NONE) {
59                 warnx("jvmti: getclassignature failed");
60                 goto error;
61         }
62
63         ret = (*jvmti)->GetMethodName(jvmti, method, &func_name,
64                                       &func_sign, NULL);
65         if (ret != JVMTI_ERROR_NONE) {
66                 warnx("jvmti: failed getmethodname");
67                 goto error;
68         }
69
70         /*
71          * Assume path name is class hierarchy, this is a common practice with Java programs
72          */
73         if (*class_sign == 'L') {
74                 int j, i = 0;
75                 char *p = strrchr(class_sign, '/');
76                 if (p) {
77                         /* drop the 'L' prefix and copy up to the final '/' */
78                         for (i = 0; i < (p - class_sign); i++)
79                                 fn[i] = class_sign[i+1];
80                 }
81                 /*
82                  * append file name, we use loops and not string ops to avoid modifying
83                  * class_sign which is used later for the symbol name
84                  */
85                 for (j = 0; i < (PATH_MAX - 1) && file_name && j < strlen(file_name); j++, i++)
86                         fn[i] = file_name[j];
87                 fn[i] = '\0';
88         } else {
89                 /* fallback case */
90                 strcpy(fn, file_name);
91         }
92         /*
93          * write source line info record if we have it
94          */
95         if (jvmti_write_debug_info(jvmti_agent, addr, fn, map, tab, nr_lines))
96                 warnx("jvmti: write_debug_info() failed");
97
98         len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2;
99         {
100                 char str[len];
101                 snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign);
102                 if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size))
103                         warnx("jvmti: write_code() failed");
104         }
105 error:
106         (*jvmti)->Deallocate(jvmti, (unsigned char *)func_name);
107         (*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign);
108         (*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign);
109         (*jvmti)->Deallocate(jvmti, (unsigned char *)tab);
110         (*jvmti)->Deallocate(jvmti, (unsigned char *)file_name);
111 }
112
113 static void JNICALL
114 code_generated_cb(jvmtiEnv *jvmti,
115                   char const *name,
116                   void const *code_addr,
117                   jint code_size)
118 {
119         uint64_t addr = (uint64_t)(unsigned long)code_addr;
120         int ret;
121
122         ret = jvmti_write_code(jvmti_agent, name, addr, code_addr, code_size);
123         if (ret)
124                 warnx("jvmti: write_code() failed for code_generated");
125 }
126
127 JNIEXPORT jint JNICALL
128 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved __unused)
129 {
130         jvmtiEventCallbacks cb;
131         jvmtiCapabilities caps1;
132         jvmtiJlocationFormat format;
133         jvmtiEnv *jvmti = NULL;
134         jint ret;
135
136         jvmti_agent = jvmti_open();
137         if (!jvmti_agent) {
138                 warnx("jvmti: open_agent failed");
139                 return -1;
140         }
141
142         /*
143          * Request a JVMTI interface version 1 environment
144          */
145         ret = (*jvm)->GetEnv(jvm, (void *)&jvmti, JVMTI_VERSION_1);
146         if (ret != JNI_OK) {
147                 warnx("jvmti: jvmti version 1 not supported");
148                 return -1;
149         }
150
151         /*
152          * acquire method_load capability, we require it
153          * request line numbers (optional)
154          */
155         memset(&caps1, 0, sizeof(caps1));
156         caps1.can_generate_compiled_method_load_events = 1;
157
158         ret = (*jvmti)->AddCapabilities(jvmti, &caps1);
159         if (ret != JVMTI_ERROR_NONE) {
160                 warnx("jvmti: acquire compiled_method capability failed");
161                 return -1;
162         }
163         ret = (*jvmti)->GetJLocationFormat(jvmti, &format);
164         if (ret == JVMTI_ERROR_NONE && format == JVMTI_JLOCATION_JVMBCI) {
165                 memset(&caps1, 0, sizeof(caps1));
166                 caps1.can_get_line_numbers = 1;
167                 caps1.can_get_source_file_name = 1;
168                 ret = (*jvmti)->AddCapabilities(jvmti, &caps1);
169                 if (ret == JVMTI_ERROR_NONE)
170                         has_line_numbers = 1;
171         }
172
173         memset(&cb, 0, sizeof(cb));
174
175         cb.CompiledMethodLoad   = compiled_method_load_cb;
176         cb.DynamicCodeGenerated = code_generated_cb;
177
178         ret = (*jvmti)->SetEventCallbacks(jvmti, &cb, sizeof(cb));
179         if (ret != JVMTI_ERROR_NONE) {
180                 warnx("jvmti: cannot set event callbacks");
181                 return -1;
182         }
183
184         ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
185                         JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
186         if (ret != JVMTI_ERROR_NONE) {
187                 warnx("jvmti: setnotification failed for method_load");
188                 return -1;
189         }
190
191         ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
192                         JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);
193         if (ret != JVMTI_ERROR_NONE) {
194                 warnx("jvmti: setnotification failed on code_generated");
195                 return -1;
196         }
197         return 0;
198 }
199
200 JNIEXPORT void JNICALL
201 Agent_OnUnload(JavaVM *jvm __unused)
202 {
203         int ret;
204
205         ret = jvmti_close(jvmti_agent);
206         if (ret)
207                 errx(1, "Error: op_close_agent()");
208 }