You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ProfilerSection.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.debug.internal;
  17. import java.util.Collection;
  18. import java.util.Collections;
  19. import java.util.LinkedHashMap;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.Map.Entry;
  23. import java.util.Set;
  24. import com.google.gwt.user.client.ui.FlowPanel;
  25. import com.google.gwt.user.client.ui.HorizontalPanel;
  26. import com.google.gwt.user.client.ui.Label;
  27. import com.google.gwt.user.client.ui.Widget;
  28. import com.vaadin.client.ApplicationConnection;
  29. import com.vaadin.client.Profiler;
  30. import com.vaadin.client.SimpleTree;
  31. import com.vaadin.client.ValueMap;
  32. /**
  33. * Debug window section for investigating {@link Profiler} data. This section is
  34. * only visible if the profiler is enabled ({@link Profiler#isEnabled()}).
  35. *
  36. * @since 7.1
  37. * @author Vaadin Ltd
  38. *
  39. * @see Profiler
  40. */
  41. public class ProfilerSection implements Section {
  42. /**
  43. * Interface for getting data from the {@link Profiler}.
  44. * <p>
  45. * <b>Warning!</b> This interface is most likely to change in the future and
  46. * is therefore defined in this class in an internal package instead of
  47. * Profiler where it might seem more logical.
  48. *
  49. * @since 7.1
  50. * @author Vaadin Ltd
  51. */
  52. public interface ProfilerResultConsumer {
  53. public void addProfilerData(Node rootNode, List<Node> totals);
  54. public void addBootstrapData(LinkedHashMap<String, Double> timings);
  55. }
  56. /**
  57. * A hierarchical representation of the time spent running a named block of
  58. * code.
  59. * <p>
  60. * <b>Warning!</b> This class is most likely to change in the future and is
  61. * therefore defined in this class in an internal package instead of
  62. * Profiler where it might seem more logical.
  63. */
  64. public static class Node {
  65. private final String name;
  66. private final LinkedHashMap<String, Node> children = new LinkedHashMap<String, Node>();
  67. private double time = 0;
  68. private int count = 0;
  69. private double enterTime = 0;
  70. private double minTime = 1000000000;
  71. private double maxTime = 0;
  72. /**
  73. * Create a new node with the given name.
  74. *
  75. * @param name
  76. */
  77. public Node(String name) {
  78. this.name = name;
  79. }
  80. /**
  81. * Gets the name of the node
  82. *
  83. * @return the name of the node
  84. */
  85. public String getName() {
  86. return name;
  87. }
  88. /**
  89. * Creates a new child node or retrieves and existing child and updates
  90. * its total time and hit count.
  91. *
  92. * @param name
  93. * the name of the child
  94. * @param timestamp
  95. * the timestamp for when the node is entered
  96. * @return the child node object
  97. */
  98. public Node enterChild(String name, double timestamp) {
  99. Node child = children.get(name);
  100. if (child == null) {
  101. child = new Node(name);
  102. children.put(name, child);
  103. }
  104. child.enterTime = timestamp;
  105. child.count++;
  106. return child;
  107. }
  108. /**
  109. * Gets the total time spent in this node, including time spent in sub
  110. * nodes
  111. *
  112. * @return the total time spent, in milliseconds
  113. */
  114. public double getTimeSpent() {
  115. return time;
  116. }
  117. /**
  118. * Gets the minimum time spent for one invocation of this node,
  119. * including time spent in sub nodes
  120. *
  121. * @return the time spent for the fastest invocation, in milliseconds
  122. */
  123. public double getMinTimeSpent() {
  124. return minTime;
  125. }
  126. /**
  127. * Gets the maximum time spent for one invocation of this node,
  128. * including time spent in sub nodes
  129. *
  130. * @return the time spent for the slowest invocation, in milliseconds
  131. */
  132. public double getMaxTimeSpent() {
  133. return maxTime;
  134. }
  135. /**
  136. * Gets the number of times this node has been entered
  137. *
  138. * @return the number of times the node has been entered
  139. */
  140. public int getCount() {
  141. return count;
  142. }
  143. /**
  144. * Gets the total time spent in this node, excluding time spent in sub
  145. * nodes
  146. *
  147. * @return the total time spent, in milliseconds
  148. */
  149. public double getOwnTime() {
  150. double time = getTimeSpent();
  151. for (Node node : children.values()) {
  152. time -= node.getTimeSpent();
  153. }
  154. return time;
  155. }
  156. /**
  157. * Gets the child nodes of this node
  158. *
  159. * @return a collection of child nodes
  160. */
  161. public Collection<Node> getChildren() {
  162. return Collections.unmodifiableCollection(children.values());
  163. }
  164. private void buildRecursiveString(StringBuilder builder, String prefix) {
  165. if (getName() != null) {
  166. String msg = getStringRepresentation(prefix);
  167. builder.append(msg + '\n');
  168. }
  169. String childPrefix = prefix + "*";
  170. for (Node node : children.values()) {
  171. node.buildRecursiveString(builder, childPrefix);
  172. }
  173. }
  174. @Override
  175. public String toString() {
  176. return getStringRepresentation("");
  177. }
  178. public String getStringRepresentation(String prefix) {
  179. if (getName() == null) {
  180. return "";
  181. }
  182. String msg = prefix + " " + getName() + " in " + getTimeSpent()
  183. + " ms.";
  184. if (getCount() > 1) {
  185. msg += " Invoked "
  186. + getCount()
  187. + " times ("
  188. + roundToSignificantFigures(getTimeSpent() / getCount())
  189. + " ms per time, min "
  190. + roundToSignificantFigures(getMinTimeSpent())
  191. + " ms, max "
  192. + roundToSignificantFigures(getMaxTimeSpent())
  193. + " ms).";
  194. }
  195. if (!children.isEmpty()) {
  196. double ownTime = getOwnTime();
  197. msg += " " + ownTime + " ms spent in own code";
  198. if (getCount() > 1) {
  199. msg += " ("
  200. + roundToSignificantFigures(ownTime / getCount())
  201. + " ms per time)";
  202. }
  203. msg += '.';
  204. }
  205. return msg;
  206. }
  207. private static double roundToSignificantFigures(double num) {
  208. // Number of significant digits
  209. int n = 3;
  210. if (num == 0) {
  211. return 0;
  212. }
  213. final double d = Math.ceil(Math.log10(num < 0 ? -num : num));
  214. final int power = n - (int) d;
  215. final double magnitude = Math.pow(10, power);
  216. final long shifted = Math.round(num * magnitude);
  217. return shifted / magnitude;
  218. }
  219. public void sumUpTotals(Map<String, Node> totals) {
  220. String name = getName();
  221. if (name != null) {
  222. Node totalNode = totals.get(name);
  223. if (totalNode == null) {
  224. totalNode = new Node(name);
  225. totals.put(name, totalNode);
  226. }
  227. totalNode.time += getOwnTime();
  228. totalNode.count += getCount();
  229. totalNode.minTime = Math.min(totalNode.minTime,
  230. getMinTimeSpent());
  231. totalNode.maxTime = Math.max(totalNode.maxTime,
  232. getMaxTimeSpent());
  233. }
  234. for (Node node : children.values()) {
  235. node.sumUpTotals(totals);
  236. }
  237. }
  238. /**
  239. * @param timestamp
  240. */
  241. public void leave(double timestamp) {
  242. double elapsed = (timestamp - enterTime);
  243. time += elapsed;
  244. enterTime = 0;
  245. if (elapsed < minTime) {
  246. minTime = elapsed;
  247. }
  248. if (elapsed > maxTime) {
  249. maxTime = elapsed;
  250. }
  251. }
  252. }
  253. private static final int MAX_ROWS = 10;
  254. private final DebugButton tabButton = new DebugButton(Icon.RESET_TIMER,
  255. "Profiler");
  256. private final HorizontalPanel controls = new HorizontalPanel();
  257. private final FlowPanel content = new FlowPanel();
  258. public ProfilerSection() {
  259. Profiler.setProfilerResultConsumer(new ProfilerResultConsumer() {
  260. @Override
  261. public void addProfilerData(Node rootNode, List<Node> totals) {
  262. double totalTime = 0;
  263. int eventCount = 0;
  264. for (Node node : totals) {
  265. totalTime += node.getTimeSpent();
  266. eventCount += node.getCount();
  267. }
  268. SimpleTree drillDownTree = (SimpleTree) buildTree(rootNode);
  269. drillDownTree.setText("Drill down");
  270. SimpleTree offendersTree = new SimpleTree("Longest events");
  271. for (int i = 0; i < totals.size() && i < 20; i++) {
  272. Node node = totals.get(i);
  273. offendersTree.add(new Label(node
  274. .getStringRepresentation("")));
  275. }
  276. SimpleTree root = new SimpleTree(eventCount
  277. + " profiler events using " + totalTime + " ms");
  278. root.add(drillDownTree);
  279. root.add(offendersTree);
  280. root.open(false);
  281. content.add(root);
  282. applyLimit();
  283. }
  284. @Override
  285. public void addBootstrapData(LinkedHashMap<String, Double> timings) {
  286. SimpleTree tree = new SimpleTree(
  287. "Time since window.performance.timing events");
  288. Set<Entry<String, Double>> entrySet = timings.entrySet();
  289. for (Entry<String, Double> entry : entrySet) {
  290. tree.add(new Label(entry.getValue() + " " + entry.getKey()));
  291. }
  292. tree.open(false);
  293. content.add(tree);
  294. applyLimit();
  295. }
  296. });
  297. }
  298. private Widget buildTree(Node node) {
  299. String message = node.getStringRepresentation("");
  300. Collection<Node> children = node.getChildren();
  301. if (node.getName() == null || !children.isEmpty()) {
  302. SimpleTree tree = new SimpleTree(message);
  303. for (Node childNode : children) {
  304. Widget child = buildTree(childNode);
  305. tree.add(child);
  306. }
  307. return tree;
  308. } else {
  309. return new Label(message);
  310. }
  311. }
  312. private void applyLimit() {
  313. while (content.getWidgetCount() > MAX_ROWS) {
  314. content.remove(0);
  315. }
  316. }
  317. @Override
  318. public DebugButton getTabButton() {
  319. return tabButton;
  320. }
  321. @Override
  322. public Widget getControls() {
  323. return controls;
  324. }
  325. @Override
  326. public Widget getContent() {
  327. return content;
  328. }
  329. @Override
  330. public void show() {
  331. // Nothing to do
  332. }
  333. @Override
  334. public void hide() {
  335. // Nothing to do
  336. }
  337. @Override
  338. public void meta(ApplicationConnection ac, ValueMap meta) {
  339. // Nothing to do
  340. }
  341. @Override
  342. public void uidl(ApplicationConnection ac, ValueMap uidl) {
  343. // Nothing to do
  344. }
  345. }