Analyzer.java 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*
  2. * Copyright The OpenTelemetry Authors
  3. * SPDX-License-Identifier: Apache-2.0
  4. */
  5. package io.opentelemetry.javaagent.benchmark.jfr;
  6. import java.io.File;
  7. import java.util.Comparator;
  8. import java.util.HashMap;
  9. import java.util.List;
  10. import java.util.Map;
  11. import java.util.Objects;
  12. import java.util.Set;
  13. import java.util.stream.Collectors;
  14. import javax.annotation.Nullable;
  15. import jdk.jfr.consumer.RecordedEvent;
  16. import jdk.jfr.consumer.RecordedFrame;
  17. import jdk.jfr.consumer.RecordedMethod;
  18. import jdk.jfr.consumer.RecordedStackTrace;
  19. import jdk.jfr.consumer.RecordingFile;
  20. @SuppressWarnings("SystemOut")
  21. public class Analyzer {
  22. private static final Node syntheticRootNode = new Node("");
  23. private static int totalSamples = 0;
  24. public static void main(String[] args) throws Exception {
  25. File jfrFile = new File(args[0]);
  26. List<RecordedEvent> events =
  27. RecordingFile.readAllEvents(jfrFile.toPath()).stream()
  28. .filter(e -> e.getEventType().getName().equals("jdk.ExecutionSample"))
  29. .collect(Collectors.toList());
  30. Set<String> agentCallers = getAgentCallers(events);
  31. for (RecordedEvent event : events) {
  32. totalSamples++;
  33. processStackTrace(event.getStackTrace(), agentCallers);
  34. }
  35. int totalAgentSamples = 0;
  36. for (Node rootNode : syntheticRootNode.getOrderedChildNodes()) {
  37. totalAgentSamples += rootNode.count;
  38. }
  39. System.out.println("Total samples: " + totalSamples);
  40. System.out.print("Total agent samples: " + totalAgentSamples);
  41. System.out.format(" (%.2f%%)%n", 100 * totalAgentSamples / (double) totalSamples);
  42. System.out.println();
  43. for (Node rootNode : syntheticRootNode.getOrderedChildNodes()) {
  44. printNode(rootNode, 0);
  45. }
  46. }
  47. // getting direct callers since those are likely the instrumented methods
  48. private static Set<String> getAgentCallers(List<RecordedEvent> events) {
  49. return events.stream()
  50. .map(e -> getAgentCaller(e.getStackTrace()))
  51. .filter(Objects::nonNull)
  52. .collect(Collectors.toSet());
  53. }
  54. @Nullable
  55. private static String getAgentCaller(RecordedStackTrace stackTrace) {
  56. List<RecordedFrame> frames = stackTrace.getFrames();
  57. for (int i = frames.size() - 1; i >= 0; i--) {
  58. RecordedFrame frame = frames.get(i);
  59. RecordedMethod method = frame.getMethod();
  60. if (isAgentMethod(method)) {
  61. RecordedFrame callerFrame = frames.get(i + 1);
  62. RecordedMethod callerMethod = callerFrame.getMethod();
  63. return getStackTraceElement(callerMethod, callerFrame);
  64. }
  65. }
  66. return null;
  67. }
  68. private static void printNode(Node node, int indent) {
  69. for (int i = 0; i < indent; i++) {
  70. System.out.print(" ");
  71. }
  72. System.out.format("%3d %s%n", node.count, node.frame);
  73. for (Node childNode : node.getOrderedChildNodes()) {
  74. printNode(childNode, indent + 1);
  75. }
  76. }
  77. private static void processStackTrace(RecordedStackTrace stackTrace, Set<String> agentCallers) {
  78. boolean analyze = false;
  79. int analyzeFromIndex = 0;
  80. List<RecordedFrame> frames = stackTrace.getFrames();
  81. for (int i = frames.size() - 1; i >= 0; i--) {
  82. RecordedFrame frame = frames.get(i);
  83. RecordedMethod method = frame.getMethod();
  84. String stackTraceElement = getStackTraceElement(method, frame);
  85. if (agentCallers.contains(stackTraceElement)) {
  86. if (i == 0) {
  87. analyze = true;
  88. analyzeFromIndex = i;
  89. break;
  90. }
  91. RecordedMethod nextMethod = frames.get(i - 1).getMethod();
  92. String nextClassName = nextMethod.getType().getName();
  93. // calls to java.* inside of the agent caller (likely an instrumented method) are
  94. // potentially part of the injected agent code
  95. if (nextClassName.startsWith("java.") || isAgentMethod(nextMethod)) {
  96. analyze = true;
  97. analyzeFromIndex = Math.min(i + 2, frames.size() - 1);
  98. break;
  99. }
  100. }
  101. if (isAgentMethod(method)) {
  102. analyze = true;
  103. analyzeFromIndex = Math.min(i + 1, frames.size() - 1);
  104. break;
  105. }
  106. }
  107. if (!analyze) {
  108. return;
  109. }
  110. Node node = syntheticRootNode;
  111. for (int i = analyzeFromIndex; i >= 0; i--) {
  112. RecordedFrame frame = frames.get(i);
  113. RecordedMethod method = frame.getMethod();
  114. String stackTraceElement = getStackTraceElement(method, frame);
  115. node = node.recordChildSample(stackTraceElement);
  116. }
  117. }
  118. private static boolean isAgentMethod(RecordedMethod method) {
  119. String className = method.getType().getName();
  120. String methodName = method.getName();
  121. return className.startsWith("io.opentelemetry.javaagent.")
  122. && !className.startsWith("io.opentelemetry.javaagent.benchmark.")
  123. // this shows up in stack traces because it's part of the filter chain
  124. && !(className.equals(
  125. "io.opentelemetry.javaagent.instrumentation.springwebmvc.HandlerMappingResourceNameFilter")
  126. && methodName.equals("doFilter"));
  127. }
  128. private static String getStackTraceElement(RecordedMethod method, RecordedFrame frame) {
  129. return method.getType().getName()
  130. + "."
  131. + method.getName()
  132. + "() line: "
  133. + frame.getLineNumber();
  134. }
  135. private static class Node {
  136. private final String frame;
  137. private final Map<String, Node> childNodes = new HashMap<>();
  138. private int count;
  139. private Node(String frame) {
  140. this.frame = frame;
  141. }
  142. private Node recordChildSample(String stackTraceElement) {
  143. Node childNode = childNodes.get(stackTraceElement);
  144. if (childNode == null) {
  145. childNode = new Node(stackTraceElement);
  146. childNodes.put(stackTraceElement, childNode);
  147. }
  148. childNode.count++;
  149. return childNode;
  150. }
  151. private List<Node> getOrderedChildNodes() {
  152. return childNodes.values().stream()
  153. .sorted(Comparator.comparingInt(Node::getCount).reversed())
  154. .collect(Collectors.toList());
  155. }
  156. private int getCount() {
  157. return count;
  158. }
  159. }
  160. private Analyzer() {}
  161. }