libjvmti.c 7.7 KB

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