libjvmti.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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 <jvmticmlr.h>
  8. #include <limits.h>
  9. #include "jvmti_agent.h"
  10. static int has_line_numbers;
  11. void *jvmti_agent;
  12. static void print_error(jvmtiEnv *jvmti, const char *msg, jvmtiError ret)
  13. {
  14. char *err_msg = NULL;
  15. jvmtiError err;
  16. err = (*jvmti)->GetErrorName(jvmti, ret, &err_msg);
  17. if (err == JVMTI_ERROR_NONE) {
  18. warnx("%s failed with %s", msg, err_msg);
  19. (*jvmti)->Deallocate(jvmti, (unsigned char *)err_msg);
  20. } else {
  21. warnx("%s failed with an unknown error %d", msg, ret);
  22. }
  23. }
  24. static jvmtiError
  25. do_get_line_numbers(jvmtiEnv *jvmti, void *pc, jmethodID m, jint bci,
  26. jvmti_line_info_t *tab, jint *nr)
  27. {
  28. jint i, lines = 0;
  29. jint nr_lines = 0;
  30. jvmtiLineNumberEntry *loc_tab = NULL;
  31. jvmtiError ret;
  32. ret = (*jvmti)->GetLineNumberTable(jvmti, m, &nr_lines, &loc_tab);
  33. if (ret != JVMTI_ERROR_NONE) {
  34. print_error(jvmti, "GetLineNumberTable", ret);
  35. return ret;
  36. }
  37. for (i = 0; i < nr_lines; i++) {
  38. if (loc_tab[i].start_location < bci) {
  39. tab[lines].pc = (unsigned long)pc;
  40. tab[lines].line_number = loc_tab[i].line_number;
  41. tab[lines].discrim = 0; /* not yet used */
  42. lines++;
  43. } else {
  44. break;
  45. }
  46. }
  47. (*jvmti)->Deallocate(jvmti, (unsigned char *)loc_tab);
  48. *nr = lines;
  49. return JVMTI_ERROR_NONE;
  50. }
  51. static jvmtiError
  52. get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t **tab, int *nr_lines)
  53. {
  54. const jvmtiCompiledMethodLoadRecordHeader *hdr;
  55. jvmtiCompiledMethodLoadInlineRecord *rec;
  56. jvmtiLineNumberEntry *lne = NULL;
  57. PCStackInfo *c;
  58. jint nr, ret;
  59. int nr_total = 0;
  60. int i, lines_total = 0;
  61. if (!(tab && nr_lines))
  62. return JVMTI_ERROR_NULL_POINTER;
  63. /*
  64. * Phase 1 -- get the number of lines necessary
  65. */
  66. for (hdr = compile_info; hdr != NULL; hdr = hdr->next) {
  67. if (hdr->kind == JVMTI_CMLR_INLINE_INFO) {
  68. rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr;
  69. for (i = 0; i < rec->numpcs; i++) {
  70. c = rec->pcinfo + i;
  71. nr = 0;
  72. /*
  73. * unfortunately, need a tab to get the number of lines!
  74. */
  75. ret = (*jvmti)->GetLineNumberTable(jvmti, c->methods[0], &nr, &lne);
  76. if (ret == JVMTI_ERROR_NONE) {
  77. /* free what was allocated for nothing */
  78. (*jvmti)->Deallocate(jvmti, (unsigned char *)lne);
  79. nr_total += (int)nr;
  80. } else {
  81. print_error(jvmti, "GetLineNumberTable", ret);
  82. }
  83. }
  84. }
  85. }
  86. if (nr_total == 0)
  87. return JVMTI_ERROR_NOT_FOUND;
  88. /*
  89. * Phase 2 -- allocate big enough line table
  90. */
  91. *tab = malloc(nr_total * sizeof(**tab));
  92. if (!*tab)
  93. return JVMTI_ERROR_OUT_OF_MEMORY;
  94. for (hdr = compile_info; hdr != NULL; hdr = hdr->next) {
  95. if (hdr->kind == JVMTI_CMLR_INLINE_INFO) {
  96. rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr;
  97. for (i = 0; i < rec->numpcs; i++) {
  98. c = rec->pcinfo + i;
  99. nr = 0;
  100. ret = do_get_line_numbers(jvmti, c->pc,
  101. c->methods[0],
  102. c->bcis[0],
  103. *tab + lines_total,
  104. &nr);
  105. if (ret == JVMTI_ERROR_NONE)
  106. lines_total += nr;
  107. }
  108. }
  109. }
  110. *nr_lines = lines_total;
  111. return JVMTI_ERROR_NONE;
  112. }
  113. static void JNICALL
  114. compiled_method_load_cb(jvmtiEnv *jvmti,
  115. jmethodID method,
  116. jint code_size,
  117. void const *code_addr,
  118. jint map_length,
  119. jvmtiAddrLocationMap const *map,
  120. const void *compile_info)
  121. {
  122. jvmti_line_info_t *line_tab = NULL;
  123. jclass decl_class;
  124. char *class_sign = NULL;
  125. char *func_name = NULL;
  126. char *func_sign = NULL;
  127. char *file_name= NULL;
  128. char fn[PATH_MAX];
  129. uint64_t addr = (uint64_t)(uintptr_t)code_addr;
  130. jvmtiError ret;
  131. int nr_lines = 0; /* in line_tab[] */
  132. size_t len;
  133. ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method,
  134. &decl_class);
  135. if (ret != JVMTI_ERROR_NONE) {
  136. print_error(jvmti, "GetMethodDeclaringClass", ret);
  137. return;
  138. }
  139. if (has_line_numbers && map && map_length) {
  140. ret = get_line_numbers(jvmti, compile_info, &line_tab, &nr_lines);
  141. if (ret != JVMTI_ERROR_NONE) {
  142. warnx("jvmti: cannot get line table for method");
  143. nr_lines = 0;
  144. }
  145. }
  146. ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name);
  147. if (ret != JVMTI_ERROR_NONE) {
  148. print_error(jvmti, "GetSourceFileName", ret);
  149. goto error;
  150. }
  151. ret = (*jvmti)->GetClassSignature(jvmti, decl_class,
  152. &class_sign, NULL);
  153. if (ret != JVMTI_ERROR_NONE) {
  154. print_error(jvmti, "GetClassSignature", ret);
  155. goto error;
  156. }
  157. ret = (*jvmti)->GetMethodName(jvmti, method, &func_name,
  158. &func_sign, NULL);
  159. if (ret != JVMTI_ERROR_NONE) {
  160. print_error(jvmti, "GetMethodName", ret);
  161. goto error;
  162. }
  163. /*
  164. * Assume path name is class hierarchy, this is a common practice with Java programs
  165. */
  166. if (*class_sign == 'L') {
  167. int j, i = 0;
  168. char *p = strrchr(class_sign, '/');
  169. if (p) {
  170. /* drop the 'L' prefix and copy up to the final '/' */
  171. for (i = 0; i < (p - class_sign); i++)
  172. fn[i] = class_sign[i+1];
  173. }
  174. /*
  175. * append file name, we use loops and not string ops to avoid modifying
  176. * class_sign which is used later for the symbol name
  177. */
  178. for (j = 0; i < (PATH_MAX - 1) && file_name && j < strlen(file_name); j++, i++)
  179. fn[i] = file_name[j];
  180. fn[i] = '\0';
  181. } else {
  182. /* fallback case */
  183. strcpy(fn, file_name);
  184. }
  185. /*
  186. * write source line info record if we have it
  187. */
  188. if (jvmti_write_debug_info(jvmti_agent, addr, fn, line_tab, nr_lines))
  189. warnx("jvmti: write_debug_info() failed");
  190. len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2;
  191. {
  192. char str[len];
  193. snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign);
  194. if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size))
  195. warnx("jvmti: write_code() failed");
  196. }
  197. error:
  198. (*jvmti)->Deallocate(jvmti, (unsigned char *)func_name);
  199. (*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign);
  200. (*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign);
  201. (*jvmti)->Deallocate(jvmti, (unsigned char *)file_name);
  202. free(line_tab);
  203. }
  204. static void JNICALL
  205. code_generated_cb(jvmtiEnv *jvmti,
  206. char const *name,
  207. void const *code_addr,
  208. jint code_size)
  209. {
  210. uint64_t addr = (uint64_t)(unsigned long)code_addr;
  211. int ret;
  212. ret = jvmti_write_code(jvmti_agent, name, addr, code_addr, code_size);
  213. if (ret)
  214. warnx("jvmti: write_code() failed for code_generated");
  215. }
  216. JNIEXPORT jint JNICALL
  217. Agent_OnLoad(JavaVM *jvm, char *options, void *reserved __unused)
  218. {
  219. jvmtiEventCallbacks cb;
  220. jvmtiCapabilities caps1;
  221. jvmtiJlocationFormat format;
  222. jvmtiEnv *jvmti = NULL;
  223. jint ret;
  224. jvmti_agent = jvmti_open();
  225. if (!jvmti_agent) {
  226. warnx("jvmti: open_agent failed");
  227. return -1;
  228. }
  229. /*
  230. * Request a JVMTI interface version 1 environment
  231. */
  232. ret = (*jvm)->GetEnv(jvm, (void *)&jvmti, JVMTI_VERSION_1);
  233. if (ret != JNI_OK) {
  234. warnx("jvmti: jvmti version 1 not supported");
  235. return -1;
  236. }
  237. /*
  238. * acquire method_load capability, we require it
  239. * request line numbers (optional)
  240. */
  241. memset(&caps1, 0, sizeof(caps1));
  242. caps1.can_generate_compiled_method_load_events = 1;
  243. ret = (*jvmti)->AddCapabilities(jvmti, &caps1);
  244. if (ret != JVMTI_ERROR_NONE) {
  245. print_error(jvmti, "AddCapabilities", ret);
  246. return -1;
  247. }
  248. ret = (*jvmti)->GetJLocationFormat(jvmti, &format);
  249. if (ret == JVMTI_ERROR_NONE && format == JVMTI_JLOCATION_JVMBCI) {
  250. memset(&caps1, 0, sizeof(caps1));
  251. caps1.can_get_line_numbers = 1;
  252. caps1.can_get_source_file_name = 1;
  253. ret = (*jvmti)->AddCapabilities(jvmti, &caps1);
  254. if (ret == JVMTI_ERROR_NONE)
  255. has_line_numbers = 1;
  256. } else if (ret != JVMTI_ERROR_NONE)
  257. print_error(jvmti, "GetJLocationFormat", ret);
  258. memset(&cb, 0, sizeof(cb));
  259. cb.CompiledMethodLoad = compiled_method_load_cb;
  260. cb.DynamicCodeGenerated = code_generated_cb;
  261. ret = (*jvmti)->SetEventCallbacks(jvmti, &cb, sizeof(cb));
  262. if (ret != JVMTI_ERROR_NONE) {
  263. print_error(jvmti, "SetEventCallbacks", ret);
  264. return -1;
  265. }
  266. ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
  267. JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
  268. if (ret != JVMTI_ERROR_NONE) {
  269. print_error(jvmti, "SetEventNotificationMode(METHOD_LOAD)", ret);
  270. return -1;
  271. }
  272. ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
  273. JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);
  274. if (ret != JVMTI_ERROR_NONE) {
  275. print_error(jvmti, "SetEventNotificationMode(CODE_GENERATED)", ret);
  276. return -1;
  277. }
  278. return 0;
  279. }
  280. JNIEXPORT void JNICALL
  281. Agent_OnUnload(JavaVM *jvm __unused)
  282. {
  283. int ret;
  284. ret = jvmti_close(jvmti_agent);
  285. if (ret)
  286. errx(1, "Error: op_close_agent()");
  287. }