NativeMemoryGraph.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. /*
  2. * Copyright (C) 2013 Google Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are
  6. * met:
  7. *
  8. * * Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above
  11. * copyright notice, this list of conditions and the following disclaimer
  12. * in the documentation and/or other materials provided with the
  13. * distribution.
  14. * * Neither the name of Google Inc. nor the names of its
  15. * contributors may be used to endorse or promote products derived from
  16. * this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. /**
  31. * @param {WebInspector.TimelinePanel} timelinePanel
  32. * @param {WebInspector.TimelineModel} model
  33. * @param {number} sidebarWidth
  34. * @constructor
  35. * @extends {WebInspector.MemoryStatistics}
  36. */
  37. WebInspector.NativeMemoryGraph = function(timelinePanel, model, sidebarWidth)
  38. {
  39. WebInspector.MemoryStatistics.call(this, timelinePanel, model, sidebarWidth);
  40. }
  41. /**
  42. * @constructor
  43. * @extends {WebInspector.MemoryStatistics.Counter}
  44. */
  45. WebInspector.NativeMemoryGraph.Counter = function(time, nativeCounters)
  46. {
  47. WebInspector.MemoryStatistics.Counter.call(this, time);
  48. this.nativeCounters = nativeCounters;
  49. }
  50. /**
  51. * @constructor
  52. * @extends {WebInspector.CounterUIBase}
  53. * @param {WebInspector.NativeMemoryGraph} memoryCountersPane
  54. * @param {string} title
  55. * @param {Array.<number>} hsl
  56. * @param {function(WebInspector.NativeMemoryGraph.Counter):number} valueGetter
  57. */
  58. WebInspector.NativeMemoryCounterUI = function(memoryCountersPane, title, hsl, valueGetter)
  59. {
  60. var swatchColor = this._hslToString(hsl);
  61. WebInspector.CounterUIBase.call(this, memoryCountersPane, title, swatchColor, valueGetter);
  62. this._value = this._swatch.element.createChild("span", "memory-category-value");
  63. const borderLightnessDifference = 3;
  64. hsl[2] -= borderLightnessDifference;
  65. this.strokeColor = this._hslToString(hsl);
  66. this.graphYValues = [];
  67. }
  68. WebInspector.NativeMemoryCounterUI.prototype = {
  69. _hslToString: function(hsl)
  70. {
  71. return "hsl(" + hsl[0] + "," + hsl[1] + "%," + hsl[2] + "%)";
  72. },
  73. updateCurrentValue: function(countersEntry)
  74. {
  75. var bytes = this.valueGetter(countersEntry);
  76. var megabytes = bytes / (1024 * 1024);
  77. this._value.textContent = WebInspector.UIString("%.1f\u2009MB", megabytes);
  78. },
  79. clearCurrentValueAndMarker: function(ctx)
  80. {
  81. this._value.textContent = "";
  82. },
  83. __proto__: WebInspector.CounterUIBase.prototype
  84. }
  85. WebInspector.NativeMemoryGraph.prototype = {
  86. _createCurrentValuesBar: function()
  87. {
  88. },
  89. _createCounterUIList: function()
  90. {
  91. var nativeCounters = [
  92. "JSExternalResources",
  93. "CSS",
  94. "GlyphCache",
  95. "Image",
  96. "Resources",
  97. "DOM",
  98. "Rendering",
  99. "Audio",
  100. "WebInspector",
  101. "JSHeap.Used",
  102. "JSHeap.Unused",
  103. "MallocWaste",
  104. "Other",
  105. "PrivateBytes",
  106. ];
  107. /**
  108. * @param {string} name
  109. * @return {number}
  110. */
  111. function getCounterValue(name, entry)
  112. {
  113. return (entry.nativeCounters && entry.nativeCounters[name]) || 0;
  114. }
  115. var list = [];
  116. for (var i = nativeCounters.length - 1; i >= 0; i--) {
  117. var name = nativeCounters[i];
  118. if ("PrivateBytes" === name) {
  119. var counterUI = new WebInspector.NativeMemoryCounterUI(this, "Total", [0, 0, 0], getCounterValue.bind(this, name))
  120. this._privateBytesCounter = counterUI;
  121. } else {
  122. var counterUI = new WebInspector.NativeMemoryCounterUI(this, name, [i * 20, 65, 63], getCounterValue.bind(this, name))
  123. list.push(counterUI);
  124. }
  125. }
  126. return list.reverse();
  127. },
  128. _canvasHeight: function()
  129. {
  130. return this._canvasContainer.offsetHeight;
  131. },
  132. /**
  133. * @param {WebInspector.Event} event
  134. */
  135. _onRecordAdded: function(event)
  136. {
  137. var statistics = this._counters;
  138. function addStatistics(record)
  139. {
  140. var nativeCounters = record["nativeHeapStatistics"];
  141. if (!nativeCounters)
  142. return;
  143. var knownSize = 0;
  144. for (var name in nativeCounters) {
  145. if (name === "PrivateBytes")
  146. continue;
  147. knownSize += nativeCounters[name];
  148. }
  149. nativeCounters["Other"] = nativeCounters["PrivateBytes"] - knownSize;
  150. statistics.push(new WebInspector.NativeMemoryGraph.Counter(
  151. record.endTime || record.startTime,
  152. nativeCounters
  153. ));
  154. }
  155. WebInspector.TimelinePresentationModel.forAllRecords([event.data], null, addStatistics);
  156. },
  157. _draw: function()
  158. {
  159. WebInspector.MemoryStatistics.prototype._draw.call(this);
  160. var maxValue = this._maxCounterValue();
  161. this._resetTotalValues();
  162. var previousCounterUI;
  163. for (var i = 0; i < this._counterUI.length; i++) {
  164. this._drawGraph(this._counterUI[i], previousCounterUI, maxValue);
  165. if (this._counterUI[i].visible)
  166. previousCounterUI = this._counterUI[i];
  167. }
  168. },
  169. /**
  170. * @param {CanvasRenderingContext2D} ctx
  171. */
  172. _clearCurrentValueAndMarker: function(ctx)
  173. {
  174. WebInspector.MemoryStatistics.prototype._clearCurrentValueAndMarker.call(this, ctx);
  175. this._privateBytesCounter.clearCurrentValueAndMarker(ctx);
  176. },
  177. _updateCurrentValue: function(counterEntry)
  178. {
  179. WebInspector.MemoryStatistics.prototype._updateCurrentValue.call(this, counterEntry);
  180. this._privateBytesCounter.updateCurrentValue(counterEntry);
  181. },
  182. /**
  183. * @param {CanvasRenderingContext2D} ctx
  184. */
  185. _restoreImageUnderMarker: function(ctx)
  186. {
  187. if (this._imageUnderMarker)
  188. ctx.putImageData(this._imageUnderMarker.imageData, this._imageUnderMarker.x, this._imageUnderMarker.y);
  189. this._discardImageUnderMarker();
  190. },
  191. /**
  192. * @param {CanvasRenderingContext2D} ctx
  193. * @param {number} left
  194. * @param {number} top
  195. * @param {number} right
  196. * @param {number} bottom
  197. */
  198. _saveImageUnderMarker: function(ctx, left, top, right, bottom)
  199. {
  200. var imageData = ctx.getImageData(left, top, right, bottom);
  201. this._imageUnderMarker = {
  202. x: left,
  203. y: top,
  204. imageData: imageData
  205. };
  206. },
  207. /**
  208. * @param {CanvasRenderingContext2D} ctx
  209. * @param {number} x
  210. * @param {number} index
  211. */
  212. _drawMarker: function(ctx, x, index)
  213. {
  214. var left = this._counters[index].x;
  215. var right = index + 1 < this._counters.length ? this._counters[index + 1].x : left;
  216. var top = this._originY;
  217. top = 0;
  218. var bottom = top + this._clippedHeight;
  219. bottom += this._originY;
  220. this._saveImageUnderMarker(ctx, left, top, right, bottom);
  221. ctx.beginPath();
  222. ctx.moveTo(left, top);
  223. ctx.lineTo(right, top);
  224. ctx.lineTo(right, bottom);
  225. ctx.lineTo(left, bottom);
  226. ctx.lineWidth = 1;
  227. ctx.closePath();
  228. ctx.fillStyle = "rgba(220,220,220,0.3)";
  229. ctx.fill();
  230. },
  231. /**
  232. * @return {number}
  233. */
  234. _maxCounterValue: function()
  235. {
  236. if (!this._counters.length)
  237. return 0;
  238. var valueGetter = this._privateBytesCounter.valueGetter;
  239. var result = 0;
  240. for (var i = this._minimumIndex; i < this._maximumIndex; i++) {
  241. var counter = this._counters[i];
  242. var value = valueGetter(counter);
  243. if (value > result)
  244. result = value;
  245. }
  246. return result;
  247. },
  248. _resetTotalValues: function()
  249. {
  250. for (var i = this._minimumIndex; i <= this._maximumIndex; i++) {
  251. var counter = this._counters[i];
  252. counter.total = 0;
  253. }
  254. },
  255. /**
  256. * @param {WebInspector.CounterUIBase} counterUI
  257. * @param {WebInspector.CounterUIBase} previousCounterUI
  258. * @param {number} maxTotalValue
  259. */
  260. _drawGraph: function(counterUI, previousCounterUI, maxTotalValue)
  261. {
  262. var canvas = this._canvas;
  263. var ctx = canvas.getContext("2d");
  264. var width = canvas.width;
  265. var height = this._clippedHeight;
  266. var originY = this._originY;
  267. var valueGetter = counterUI.valueGetter;
  268. if (!this._counters.length)
  269. return;
  270. if (!counterUI.visible)
  271. return;
  272. for (var i = this._minimumIndex; i <= this._maximumIndex; i++) {
  273. var counter = this._counters[i];
  274. var value = valueGetter(counter);
  275. counter.total += value;
  276. }
  277. var yValues = counterUI.graphYValues;
  278. yValues.length = this._counters.length;
  279. var maxYRange = maxTotalValue;
  280. var yFactor = maxYRange ? height / (maxYRange) : 1;
  281. ctx.beginPath();
  282. if (previousCounterUI) {
  283. var prevYValues = previousCounterUI.graphYValues;
  284. var currentY = prevYValues[this._maximumIndex];
  285. ctx.moveTo(width, currentY);
  286. var currentX = width;
  287. for (var i = this._maximumIndex - 1; i >= this._minimumIndex; i--) {
  288. currentY = prevYValues[i];
  289. currentX = this._counters[i].x;
  290. ctx.lineTo(currentX, currentY);
  291. }
  292. } else {
  293. var lastY = originY + height;
  294. ctx.moveTo(width, lastY);
  295. ctx.lineTo(0, lastY);
  296. }
  297. var currentY = originY + (height - this._counters[this._minimumIndex].total * yFactor);
  298. ctx.lineTo(0, currentY);
  299. for (var i = this._minimumIndex; i <= this._maximumIndex; i++) {
  300. var counter = this._counters[i];
  301. var x = counter.x;
  302. currentY = originY + (height - counter.total * yFactor);
  303. ctx.lineTo(x, currentY);
  304. yValues[i] = currentY;
  305. }
  306. ctx.lineTo(width, currentY);
  307. ctx.closePath();
  308. ctx.lineWidth = 1;
  309. ctx.strokeStyle = counterUI.strokeColor;
  310. ctx.fillStyle = counterUI.graphColor;
  311. ctx.fill();
  312. ctx.stroke();
  313. },
  314. _discardImageUnderMarker: function()
  315. {
  316. delete this._imageUnderMarker;
  317. },
  318. __proto__: WebInspector.MemoryStatistics.prototype
  319. }