<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[192056] trunk/Source/WebCore</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/192056">192056</a></dd>
<dt>Author</dt> <dd>akling@apple.com</dd>
<dt>Date</dt> <dd>2015-11-05 03:36:15 -0800 (Thu, 05 Nov 2015)</dd>
</dl>
<h3>Log Message</h3>
<pre>Give ResourceUsageOverlay a stacked chart for dirty memory per category.
<https://webkit.org/b/150905>
Reviewed by Antti Koivisto.
Refactored the data gathering to operate on "memory categories", a memory category is at
the top level a VM tag, e.g the VM tag for our bmalloc allocator. It can in turn have
sub-categories, e.g one for the GC heap, which allocates all of its blocks through bmalloc
and thus end up in the same tag.
Each category also has a hard-coded color, which is used consistently in labels and charts.
Also went back to drawing everything with CGContext directly instead of GraphicsContext
since the latter is not thread safe.
* page/ResourceUsageOverlay.h:
* page/cocoa/ResourceUsageOverlayCocoa.mm:
(-[WebOverlayLayer drawInContext:]):
(WebCore::RingBuffer::last):
(WebCore::MemoryCategoryInfo::MemoryCategoryInfo):
(WebCore::ResourceUsageData::ResourceUsageData):
(WebCore::showText):
(WebCore::drawGraphLabel):
(WebCore::drawCpuHistory):
(WebCore::drawGCHistory):
(WebCore::drawMemHistory):
(WebCore::drawSlice):
(WebCore::drawMemoryPie):
(WebCore::ResourceUsageOverlay::platformDraw):
(WebCore::categoryForVMTag):
(WebCore::runSamplerThread):
(WebCore::drawPlate): Deleted.
(WebCore::fontCascade): Deleted.
(WebCore::ResourceUsageOverlay::draw): Deleted.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorepageResourceUsageOverlayh">trunk/Source/WebCore/page/ResourceUsageOverlay.h</a></li>
<li><a href="#trunkSourceWebCorepagecocoaResourceUsageOverlayCocoamm">trunk/Source/WebCore/page/cocoa/ResourceUsageOverlayCocoa.mm</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (192055 => 192056)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-11-05 09:54:13 UTC (rev 192055)
+++ trunk/Source/WebCore/ChangeLog        2015-11-05 11:36:15 UTC (rev 192056)
</span><span class="lines">@@ -1,3 +1,40 @@
</span><ins>+2015-11-05 Andreas Kling <akling@apple.com>
+
+ Give ResourceUsageOverlay a stacked chart for dirty memory per category.
+ <https://webkit.org/b/150905>
+
+ Reviewed by Antti Koivisto.
+
+ Refactored the data gathering to operate on "memory categories", a memory category is at
+ the top level a VM tag, e.g the VM tag for our bmalloc allocator. It can in turn have
+ sub-categories, e.g one for the GC heap, which allocates all of its blocks through bmalloc
+ and thus end up in the same tag.
+
+ Each category also has a hard-coded color, which is used consistently in labels and charts.
+
+ Also went back to drawing everything with CGContext directly instead of GraphicsContext
+ since the latter is not thread safe.
+
+ * page/ResourceUsageOverlay.h:
+ * page/cocoa/ResourceUsageOverlayCocoa.mm:
+ (-[WebOverlayLayer drawInContext:]):
+ (WebCore::RingBuffer::last):
+ (WebCore::MemoryCategoryInfo::MemoryCategoryInfo):
+ (WebCore::ResourceUsageData::ResourceUsageData):
+ (WebCore::showText):
+ (WebCore::drawGraphLabel):
+ (WebCore::drawCpuHistory):
+ (WebCore::drawGCHistory):
+ (WebCore::drawMemHistory):
+ (WebCore::drawSlice):
+ (WebCore::drawMemoryPie):
+ (WebCore::ResourceUsageOverlay::platformDraw):
+ (WebCore::categoryForVMTag):
+ (WebCore::runSamplerThread):
+ (WebCore::drawPlate): Deleted.
+ (WebCore::fontCascade): Deleted.
+ (WebCore::ResourceUsageOverlay::draw): Deleted.
+
</ins><span class="cx"> 2015-11-05 Simon Fraser <simon.fraser@apple.com>
</span><span class="cx">
</span><span class="cx"> Having page overlays causes iframe to get composited
</span></span></pre></div>
<a id="trunkSourceWebCorepageResourceUsageOverlayh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/ResourceUsageOverlay.h (192055 => 192056)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/ResourceUsageOverlay.h        2015-11-05 09:54:13 UTC (rev 192055)
+++ trunk/Source/WebCore/page/ResourceUsageOverlay.h        2015-11-05 11:36:15 UTC (rev 192056)
</span><span class="lines">@@ -54,7 +54,9 @@
</span><span class="cx">
</span><span class="cx"> PageOverlay& overlay() { return *m_overlay; }
</span><span class="cx">
</span><del>- void draw(GraphicsContext&);
</del><ins>+#if PLATFORM(COCOA)
+ void platformDraw(CGContextRef);
+#endif
</ins><span class="cx">
</span><span class="cx"> private:
</span><span class="cx"> void pageOverlayDestroyed(PageOverlay&) override { }
</span><span class="lines">@@ -79,7 +81,7 @@
</span><span class="cx"> RetainPtr<CALayer> m_layer;
</span><span class="cx"> #endif
</span><span class="cx">
</span><del>- static const int normalWidth = 500;
</del><ins>+ static const int normalWidth = 550;
</ins><span class="cx"> static const int normalHeight = 130;
</span><span class="cx"> };
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCorepagecocoaResourceUsageOverlayCocoamm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/cocoa/ResourceUsageOverlayCocoa.mm (192055 => 192056)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/cocoa/ResourceUsageOverlayCocoa.mm        2015-11-05 09:54:13 UTC (rev 192055)
+++ trunk/Source/WebCore/page/cocoa/ResourceUsageOverlayCocoa.mm        2015-11-05 11:36:15 UTC (rev 192056)
</span><span class="lines">@@ -28,10 +28,10 @@
</span><span class="cx">
</span><span class="cx"> #if ENABLE(RESOURCE_USAGE_OVERLAY)
</span><span class="cx">
</span><del>-#include "GraphicsContext.h"
</del><span class="cx"> #include "JSDOMWindow.h"
</span><span class="cx"> #include "MachVMSPI.h"
</span><span class="cx"> #include "PlatformCALayer.h"
</span><ins>+#include <CoreGraphics/CGContext.h>
</ins><span class="cx"> #include <QuartzCore/CALayer.h>
</span><span class="cx"> #include <QuartzCore/CATransaction.h>
</span><span class="cx"> #include <array>
</span><span class="lines">@@ -62,25 +62,14 @@
</span><span class="cx">
</span><span class="cx"> - (void)drawInContext:(CGContextRef)context
</span><span class="cx"> {
</span><del>- GraphicsContext gc(context);
- m_overlay->draw(gc);
</del><ins>+ m_overlay->platformDraw(context);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> @end
</span><span class="cx">
</span><span class="cx"> namespace WebCore {
</span><span class="cx">
</span><del>-static const RGBA32 colorForJITCode = 0xFFFF60FF;
-static const RGBA32 colorForImages = 0xFFFFFF00;
-static const RGBA32 colorForLayers = 0xFF00FFFF;
-static const RGBA32 colorForGCHeap = 0xFFA0A0FF;
-static const RGBA32 colorForLibcMalloc = 0xFF00FF00;
-static const RGBA32 colorForFastMalloc = 0xFFFF6060;
-static const RGBA32 colorForOther = 0xFFC0FF00;
-static const RGBA32 colorForGCOwned = 0xFFFFC060;
-static const RGBA32 colorForLabels = 0xFFE0E0E0;
-
-template<typename T, size_t size = 50>
</del><ins>+template<typename T, size_t size = 70>
</ins><span class="cx"> class RingBuffer {
</span><span class="cx"> public:
</span><span class="cx"> RingBuffer()
</span><span class="lines">@@ -94,7 +83,7 @@
</span><span class="cx"> incrementIndex(m_current);
</span><span class="cx"> }
</span><span class="cx">
</span><del>- T last() const
</del><ins>+ T& last()
</ins><span class="cx"> {
</span><span class="cx"> unsigned index = m_current;
</span><span class="cx"> decrementIndex(index);
</span><span class="lines">@@ -129,23 +118,68 @@
</span><span class="cx"> unsigned m_current { 0 };
</span><span class="cx"> };
</span><span class="cx">
</span><ins>+namespace MemoryCategory {
+static const unsigned bmalloc = 0;
+static const unsigned LibcMalloc = 1;
+static const unsigned JSJIT = 2;
+static const unsigned Images = 3;
+static const unsigned GCHeap = 4;
+static const unsigned GCOwned = 5;
+static const unsigned Other = 6;
+static const unsigned Layers = 7;
+static const unsigned NumberOfCategories = 8;
+}
+
+struct MemoryCategoryInfo {
+ MemoryCategoryInfo() { } // Needed for std::array.
+
+ MemoryCategoryInfo(unsigned category, RGBA32 rgba, String name, bool subcategory = false)
+ : name(WTF::move(name))
+ , isSubcategory(subcategory)
+ , type(category)
+ {
+ float r, g, b, a;
+ Color(rgba).getRGBA(r, g, b, a);
+ color = adoptCF(CGColorCreateGenericRGB(r, g, b, a));
+ }
+
+ String name;
+ RetainPtr<CGColorRef> color;
+ RingBuffer<size_t> history;
+ bool isSubcategory { false };
+ unsigned type { MemoryCategory::NumberOfCategories };
+};
+
</ins><span class="cx"> struct ResourceUsageData {
</span><ins>+ ResourceUsageData();
+
</ins><span class="cx"> Lock lock;
</span><ins>+
+ RingBuffer<size_t> totalDirty;
+ std::array<MemoryCategoryInfo, MemoryCategory::NumberOfCategories> categories;
+
</ins><span class="cx"> RingBuffer<float> cpuHistory;
</span><span class="cx"> RingBuffer<size_t> gcHeapSizeHistory;
</span><del>- RingBuffer<size_t> gcHeapCapacityHistory;
- size_t layers { 0 };
- size_t images { 0 };
- size_t jitCode { 0 };
- size_t gcOwned { 0 };
- size_t libcMalloc { 0 };
- size_t bmalloc { 0 };
- size_t sumDirty { 0 };
</del><span class="cx">
</span><span class="cx"> HashSet<CALayer *> overlayLayers;
</span><span class="cx"> JSC::VM* vm { nullptr };
</span><span class="cx"> };
</span><span class="cx">
</span><ins>+ResourceUsageData::ResourceUsageData()
+{
+ // VM tag categories.
+ categories[MemoryCategory::JSJIT] = MemoryCategoryInfo(MemoryCategory::JSJIT, 0xFFFF60FF, "JS JIT");
+ categories[MemoryCategory::Images] = MemoryCategoryInfo(MemoryCategory::Images, 0xFFFFFF00, "Images");
+ categories[MemoryCategory::Layers] = MemoryCategoryInfo(MemoryCategory::Layers, 0xFF00FFFF, "Layers");
+ categories[MemoryCategory::LibcMalloc] = MemoryCategoryInfo(MemoryCategory::LibcMalloc, 0xFF00FF00, "libc malloc");
+ categories[MemoryCategory::bmalloc] = MemoryCategoryInfo(MemoryCategory::bmalloc, 0xFFFF6060, "bmalloc");
+ categories[MemoryCategory::Other] = MemoryCategoryInfo(MemoryCategory::Other, 0xFFC0FF00, "Other");
+
+ // Sub categories (e.g breakdown of bmalloc tag.)
+ categories[MemoryCategory::GCHeap] = MemoryCategoryInfo(MemoryCategory::GCHeap, 0xFFA0A0FF, "GC heap", true);
+ categories[MemoryCategory::GCOwned] = MemoryCategoryInfo(MemoryCategory::GCOwned, 0xFFFFC060, "GC owned", true);
+}
+
</ins><span class="cx"> static ResourceUsageData& sharedData()
</span><span class="cx"> {
</span><span class="cx"> static NeverDestroyed<ResourceUsageData> data;
</span><span class="lines">@@ -184,119 +218,169 @@
</span><span class="cx"> data.overlayLayers.remove(m_layer.get());
</span><span class="cx"> }
</span><span class="cx">
</span><del>-static void drawCpuHistory(GraphicsContext& gc, float x1, float y1, float y2, RingBuffer<float>& history)
</del><ins>+static void showText(CGContextRef context, float x, float y, CGColorRef color, const String& text)
</ins><span class="cx"> {
</span><del>- Ref<Gradient> gradient = Gradient::create(FloatPoint(0, y1), FloatPoint(0, y2));
- gradient->addColorStop(0.0, Color(255, 0, 0));
- gradient->addColorStop(0.5, Color(255, 255, 0));
- gradient->addColorStop(1.0, Color(0, 255, 0));
- gc.setStrokeGradient(WTF::move(gradient));
- gc.setStrokeThickness(1);
</del><ins>+ CGContextSetTextDrawingMode(context, kCGTextFill);
+ CGContextSetFillColorWithColor(context, color);
</ins><span class="cx">
</span><ins>+ CGContextSaveGState(context);
+
+ CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1, -1));
+
+ // FIXME: Don't use deprecated APIs.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ CGContextSelectFont(context, "Menlo", 11, kCGEncodingMacRoman);
+ CString cstr = text.ascii();
+ CGContextShowTextAtPoint(context, x, y, cstr.data(), cstr.length());
+#pragma clang diagnostic pop
+
+ CGContextRestoreGState(context);
+}
+
+static void drawGraphLabel(CGContextRef context, float x, float y, const String& text)
+{
+ static CGColorRef black = CGColorCreateGenericRGB(0, 0, 0, 1);
+ showText(context, x + 5, y - 3, black, text);
+ static CGColorRef white = CGColorCreateGenericRGB(1, 1, 1, 1);
+ showText(context, x + 4, y - 4, white, text);
+}
+
+static void drawCpuHistory(CGContextRef context, float x1, float y1, float y2, RingBuffer<float>& history)
+{
+ static CGColorRef cpuColor = CGColorCreateGenericRGB(0, 1, 0, 1);
+
+ CGContextSetStrokeColorWithColor(context, cpuColor);
+ CGContextSetLineWidth(context, 1);
+
</ins><span class="cx"> int i = 0;
</span><span class="cx">
</span><span class="cx"> history.forEach([&](float c) {
</span><span class="cx"> float cpu = c / 100;
</span><span class="cx"> float yScale = y2 - y1;
</span><span class="cx">
</span><del>- Path path;
- path.moveTo(FloatPoint(x1 + i, y2));
- path.addLineTo(FloatPoint(x1 + i, y2 - (yScale * cpu)));
- gc.strokePath(path);
</del><ins>+ CGContextBeginPath(context);
+ CGContextMoveToPoint(context, x1 + i, y2);
+ CGContextAddLineToPoint(context, x1 + i, y2 - (yScale * cpu));
+ CGContextStrokePath(context);
</ins><span class="cx"> i++;
</span><span class="cx"> });
</span><ins>+
+ drawGraphLabel(context, x1, y2, "CPU");
</ins><span class="cx"> }
</span><span class="cx">
</span><del>-static void drawGCHistory(GraphicsContext& gc, float x1, float y1, float y2, RingBuffer<size_t>& sizeHistory, RingBuffer<size_t>& capacityHistory)
</del><ins>+static void drawGCHistory(CGContextRef context, float x1, float y1, float y2, RingBuffer<size_t>& sizeHistory, RingBuffer<size_t>& capacityHistory)
</ins><span class="cx"> {
</span><ins>+ float yScale = y2 - y1;
+
</ins><span class="cx"> size_t peak = 0;
</span><span class="cx"> capacityHistory.forEach([&](size_t m) {
</span><span class="cx"> if (m > peak)
</span><span class="cx"> peak = m;
</span><span class="cx"> });
</span><span class="cx">
</span><del>- gc.setStrokeThickness(1);
</del><ins>+ CGContextSetLineWidth(context, 1);
</ins><span class="cx">
</span><del>- Ref<Gradient> capacityGradient = Gradient::create(FloatPoint(0, y1), FloatPoint(0, y2));
- capacityGradient->addColorStop(0.0, Color(0xFF, 0x00, 0x00));
- capacityGradient->addColorStop(0.5, Color(0xFF, 0x00, 0x33));
- capacityGradient->addColorStop(1.0, Color(0xCC, 0x00, 0x33));
- gc.setStrokeGradient(WTF::move(capacityGradient));
</del><ins>+ static CGColorRef capacityColor = CGColorCreateGenericRGB(1, 0, 0.3, 1);
+ CGContextSetStrokeColorWithColor(context, capacityColor);
</ins><span class="cx">
</span><span class="cx"> size_t i = 0;
</span><span class="cx">
</span><span class="cx"> capacityHistory.forEach([&](size_t m) {
</span><span class="cx"> float mem = (float)m / (float)peak;
</span><del>- float yScale = y2 - y1;
-
- Path path;
- path.moveTo(FloatPoint(x1 + i, y2));
- path.addLineTo(FloatPoint(x1 + i, y2 - (yScale * mem)));
- gc.strokePath(path);
</del><ins>+ CGContextBeginPath(context);
+ CGContextMoveToPoint(context, x1 + i, y2);
+ CGContextAddLineToPoint(context, x1 + i, y2 - (yScale * mem));
+ CGContextStrokePath(context);
</ins><span class="cx"> i++;
</span><span class="cx"> });
</span><span class="cx">
</span><del>- Ref<Gradient> sizeGradient = Gradient::create(FloatPoint(0, y1), FloatPoint(0, y2));
- sizeGradient->addColorStop(0.0, Color(0x96, 0xB1, 0xD2));
- sizeGradient->addColorStop(0.5, Color(0x47, 0x71, 0xA5));
- sizeGradient->addColorStop(1.0, Color(0x29, 0x56, 0x8F));
- gc.setStrokeGradient(WTF::move(sizeGradient));
</del><ins>+ static CGColorRef sizeColor = CGColorCreateGenericRGB(0.6, 0.5, 0.9, 1);
+ CGContextSetStrokeColorWithColor(context, sizeColor);
</ins><span class="cx">
</span><span class="cx"> i = 0;
</span><span class="cx">
</span><span class="cx"> sizeHistory.forEach([&](size_t m) {
</span><span class="cx"> float mem = (float)m / (float)peak;
</span><del>- float yScale = y2 - y1;
-
- Path path;
- path.moveTo(FloatPoint(x1 + i, y2));
- path.addLineTo(FloatPoint(x1 + i, y2 - (yScale * mem)));
- gc.strokePath(path);
</del><ins>+ CGContextBeginPath(context);
+ CGContextMoveToPoint(context, x1 + i, y2);
+ CGContextAddLineToPoint(context, x1 + i, y2 - (yScale * mem));
+ CGContextStrokePath(context);
</ins><span class="cx"> i++;
</span><span class="cx"> });
</span><ins>+
+ drawGraphLabel(context, x1, y2, "GC");
</ins><span class="cx"> }
</span><span class="cx">
</span><ins>+static void drawMemHistory(CGContextRef context, float x1, float y1, float y2, ResourceUsageData& data)
+{
+ float yScale = y2 - y1;
+
+ size_t peak = 0;
+ data.totalDirty.forEach([&](size_t m) {
+ if (m > peak)
+ peak = m;
+ });
+
+ CGContextSetLineWidth(context, 1);
+
+ struct ColorAndSize {
+ CGColorRef color;
+ size_t size;
+ };
+
+ std::array<std::array<ColorAndSize, MemoryCategory::NumberOfCategories>, 70> columns;
+
+ for (auto& category : data.categories) {
+ unsigned x = 0;
+ category.history.forEach([&](size_t m) {
+ columns[x][category.type] = { category.color.get(), m };
+ ++x;
+ });
+ }
+
+ unsigned i = 0;
+ for (auto& column : columns) {
+ float currentY2 = y2;
+ for (auto& colorAndSize : column) {
+ float chunk = (float)colorAndSize.size / (float)peak;
+ float nextY2 = currentY2 - (yScale * chunk);
+ CGContextBeginPath(context);
+ CGContextMoveToPoint(context, x1 + i, currentY2);
+ CGContextAddLineToPoint(context, x1 + i, nextY2);
+ CGContextSetStrokeColorWithColor(context, colorAndSize.color);
+ CGContextStrokePath(context);
+ currentY2 = nextY2;
+ }
+ ++i;
+ }
+
+ drawGraphLabel(context, x1, y2, "Mem");
+}
+
</ins><span class="cx"> static const float fullCircleInRadians = piFloat * 2;
</span><span class="cx">
</span><del>-static void drawSlice(GraphicsContext& context, FloatPoint center, float& angle, size_t sliceSize, size_t totalSize, Color color)
</del><ins>+static void drawSlice(CGContextRef context, FloatPoint center, float& angle, float radius, size_t sliceSize, size_t totalSize, CGColorRef color)
</ins><span class="cx"> {
</span><del>- Path path;
- path.moveTo(center);
</del><span class="cx"> float part = (float)sliceSize / (float)totalSize;
</span><del>- path.addArc(center, 30, angle, angle + part * fullCircleInRadians, false);
- context.setFillColor(color, ColorSpaceDeviceRGB);
- context.fillPath(path);
</del><ins>+
+ CGContextBeginPath(context);
+ CGContextMoveToPoint(context, center.x(), center.y());
+ CGContextAddArc(context, center.x(), center.y(), radius, angle, angle + part * fullCircleInRadians, false);
+ CGContextSetFillColorWithColor(context, color);
+ CGContextFillPath(context);
</ins><span class="cx"> angle += part * fullCircleInRadians;
</span><span class="cx"> }
</span><span class="cx">
</span><del>-static void drawPlate(GraphicsContext& context, FloatPoint center, float& angle, Color color)
</del><ins>+static void drawMemoryPie(CGContextRef context, FloatRect& rect, ResourceUsageData& data)
</ins><span class="cx"> {
</span><del>- Path path;
- path.moveTo(center);
- path.addArc(center, 30, angle, fullCircleInRadians, false);
- context.setFillColor(color, ColorSpaceDeviceRGB);
- context.fillPath(path);
-}
</del><ins>+ float radius = rect.width() / 2;
+ FloatPoint center = rect.center();
+ size_t totalDirty = data.totalDirty.last();
</ins><span class="cx">
</span><del>-static void drawMemoryPie(GraphicsContext& context, float x, float y, ResourceUsageData& data)
-{
- GraphicsContextStateSaver saver(context);
-
- context.setShouldAntialias(true);
-
- FloatPoint center(x - 15, y + 60);
-
- size_t bmallocWithDeductions = data.bmalloc - data.gcHeapCapacityHistory.last() - data.gcOwned;
-
</del><span class="cx"> float angle = 0;
</span><del>- drawSlice(context, center, angle, bmallocWithDeductions, data.sumDirty, colorForFastMalloc);
- drawSlice(context, center, angle, data.libcMalloc, data.sumDirty, colorForLibcMalloc);
- drawSlice(context, center, angle, data.gcHeapCapacityHistory.last(), data.sumDirty, colorForGCHeap);
- drawSlice(context, center, angle, data.gcOwned, data.sumDirty, colorForGCOwned);
- drawSlice(context, center, angle, data.layers, data.sumDirty, colorForLayers);
- drawSlice(context, center, angle, data.images, data.sumDirty, colorForImages);
- drawSlice(context, center, angle, data.jitCode, data.sumDirty, colorForJITCode);
- drawPlate(context, center, angle, colorForOther);
</del><ins>+ for (auto& category : data.categories)
+ drawSlice(context, center, angle, radius, category.history.last(), totalDirty, category.color.get());
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> static String formatByteNumber(size_t number)
</span><span class="lines">@@ -310,61 +394,34 @@
</span><span class="cx"> return String::format("%lu", number);
</span><span class="cx"> }
</span><span class="cx">
</span><del>-static FontCascade& fontCascade()
</del><ins>+void ResourceUsageOverlay::platformDraw(CGContextRef context)
</ins><span class="cx"> {
</span><del>- static NeverDestroyed<FontCascade> font;
- static std::once_flag onceFlag;
- std::call_once(onceFlag, [] {
- FontCascadeDescription fontDescription;
- fontDescription.setFamilies({ "Menlo" });
- fontDescription.setSpecifiedSize(11);
- fontDescription.setComputedSize(11);
- font.get() = FontCascade(fontDescription, 0, 0);
- font.get().update(nullptr);
- });
- return font;
-}
-
-static void showText(GraphicsContext& gc, float x, float y, Color color, const String& text)
-{
- gc.setShouldSmoothFonts(false);
- gc.setTextDrawingMode(TextModeFill);
- gc.setFillColor(color, ColorSpaceDeviceRGB);
- gc.drawText(fontCascade(), TextRun(text), FloatPoint(x, y));
-}
-
-void ResourceUsageOverlay::draw(GraphicsContext& context)
-{
</del><span class="cx"> auto& data = sharedData();
</span><span class="cx"> LockHolder locker(data.lock);
</span><span class="cx">
</span><del>- size_t gcHeapSize = data.gcHeapSizeHistory.last();
- size_t gcHeapCapacity = data.gcHeapCapacityHistory.last();
</del><ins>+ CGContextSetShouldAntialias(context, false);
+ CGContextSetShouldSmoothFonts(context, false);
</ins><span class="cx">
</span><del>- context.setShouldAntialias(false);
- context.setShouldSmoothFonts(false);
-
- context.clearRect(m_overlay->bounds());
</del><span class="cx"> CGRect viewBounds = m_overlay->bounds();
</span><ins>+ CGContextClearRect(context, viewBounds);
</ins><span class="cx">
</span><del>- size_t bmallocWithDeductions = data.bmalloc - gcHeapCapacity - data.gcOwned;
- size_t footprintWithDeductions = data.sumDirty - data.bmalloc - data.layers - data.images - data.libcMalloc - data.jitCode;
</del><ins>+ static CGColorRef colorForLabels = CGColorCreateGenericRGB(0.9, 0.9, 0.9, 1);
+ showText(context, 10, 20, colorForLabels, String::format(" CPU: %g", data.cpuHistory.last()));
+ showText(context, 10, 30, colorForLabels, " Footprint: " + formatByteNumber(data.totalDirty.last()));
</ins><span class="cx">
</span><del>- showText(context, 10, 20, colorForLabels, String::format(" CPU: %g", data.cpuHistory.last()));
- showText(context, 10, 30, colorForLabels, " Footprint: " + formatByteNumber(data.sumDirty));
</del><ins>+ float y = 50;
+ for (auto& category : data.categories) {
+ // FIXME: Show size/capacity of GC heap.
+ showText(context, 10, y, category.color.get(), String::format("% 11s: %s", category.name.ascii().data(), formatByteNumber(category.history.last()).ascii().data()));
+ y += 10;
+ }
</ins><span class="cx">
</span><del>- showText(context, 10, 50, colorForFastMalloc, " bmalloc: " + formatByteNumber(bmallocWithDeductions));
- showText(context, 10, 60, colorForLibcMalloc, "libc malloc: " + formatByteNumber(data.libcMalloc));
- showText(context, 10, 70, colorForImages, " Images: " + formatByteNumber(data.images));
- showText(context, 10, 80, colorForLayers, " Layers: " + formatByteNumber(data.layers));
- showText(context, 10, 90, colorForJITCode, " JS JIT: " + formatByteNumber(data.jitCode));
- showText(context, 10, 100, colorForGCHeap, " GC heap: " + formatByteNumber(gcHeapSize) + " (" + formatByteNumber(gcHeapCapacity) + ")");
- showText(context, 10, 110, colorForGCOwned, " GC owned: " + formatByteNumber(data.gcOwned));
- showText(context, 10, 120, colorForOther, " Other: " + formatByteNumber(footprintWithDeductions));
</del><ins>+ drawCpuHistory(context, viewBounds.size.width - 70, 0, viewBounds.size.height, data.cpuHistory);
+ drawGCHistory(context, viewBounds.size.width - 140, 0, viewBounds.size.height, data.gcHeapSizeHistory, data.categories[MemoryCategory::GCHeap].history);
+ drawMemHistory(context, viewBounds.size.width - 210, 0, viewBounds.size.height, data);
</ins><span class="cx">
</span><del>- drawCpuHistory(context, m_overlay->frame().width() - 50, 0, viewBounds.size.height, data.cpuHistory);
- drawGCHistory(context, m_overlay->frame().width() - 100, 0, viewBounds.size.height, data.gcHeapSizeHistory, data.gcHeapCapacityHistory);
- drawMemoryPie(context, m_overlay->frame().width() - 150, 0, data);
</del><ins>+ FloatRect pieRect(viewBounds.size.width - 330, 0, 100, viewBounds.size.height);
+ drawMemoryPie(context, pieRect, data);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> static std::array<size_t, 256> dirtyPagesPerVMTag()
</span><span class="lines">@@ -416,6 +473,31 @@
</span><span class="cx"> return usage;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+static unsigned categoryForVMTag(unsigned tag)
+{
+ switch (tag) {
+ case VM_MEMORY_IOKIT:
+ case VM_MEMORY_LAYERKIT:
+ return MemoryCategory::Layers;
+ case VM_MEMORY_IMAGEIO:
+ case VM_MEMORY_CGIMAGE:
+ return MemoryCategory::Images;
+ case VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR:
+ return MemoryCategory::JSJIT;
+ case VM_MEMORY_MALLOC:
+ case VM_MEMORY_MALLOC_HUGE:
+ case VM_MEMORY_MALLOC_LARGE:
+ case VM_MEMORY_MALLOC_SMALL:
+ case VM_MEMORY_MALLOC_TINY:
+ case VM_MEMORY_MALLOC_NANO:
+ return MemoryCategory::LibcMalloc;
+ case VM_MEMORY_TCMALLOC:
+ return MemoryCategory::bmalloc;
+ default:
+ return MemoryCategory::Other;
+ }
+};
+
</ins><span class="cx"> NO_RETURN void runSamplerThread(void*)
</span><span class="cx"> {
</span><span class="cx"> static size_t vmPageSize = getpagesize();
</span><span class="lines">@@ -428,27 +510,34 @@
</span><span class="cx"> {
</span><span class="cx"> LockHolder locker(data.lock);
</span><span class="cx"> data.cpuHistory.append(cpu);
</span><del>- data.layers = (dirtyPages[VM_MEMORY_IOKIT] + dirtyPages[VM_MEMORY_LAYERKIT]) * vmPageSize;
- data.images = (dirtyPages[VM_MEMORY_IMAGEIO] + dirtyPages[VM_MEMORY_CGIMAGE]) * vmPageSize;
- data.jitCode = dirtyPages[VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR] * vmPageSize;
- data.libcMalloc = vmPageSize *
- (dirtyPages[VM_MEMORY_MALLOC]
- + dirtyPages[VM_MEMORY_MALLOC_HUGE]
- + dirtyPages[VM_MEMORY_MALLOC_LARGE]
- + dirtyPages[VM_MEMORY_MALLOC_SMALL]
- + dirtyPages[VM_MEMORY_MALLOC_TINY]
- + dirtyPages[VM_MEMORY_MALLOC_NANO]);
- data.bmalloc = vmPageSize * dirtyPages[VM_MEMORY_TCMALLOC];
</del><span class="cx">
</span><del>- data.sumDirty = 0;
- for (auto dirty : dirtyPages)
- data.sumDirty += dirty;
- data.sumDirty *= vmPageSize;
</del><ins>+ std::array<size_t, MemoryCategory::NumberOfCategories> dirtyPagesPerCategory;
+ dirtyPagesPerCategory.fill(0);
</ins><span class="cx">
</span><ins>+ size_t totalDirtyPages = 0;
+ for (unsigned i = 0; i < 256; ++i) {
+ dirtyPagesPerCategory[categoryForVMTag(i)] += dirtyPages[i];
+ totalDirtyPages += dirtyPages[i];
+ }
+
+ for (auto& category : data.categories) {
+ if (category.isSubcategory) // Only do automatic tallying for top-level categories.
+ continue;
+ category.history.append(dirtyPagesPerCategory[category.type] * vmPageSize);
+ }
+ data.totalDirty.append(totalDirtyPages * vmPageSize);
+
</ins><span class="cx"> copyToVector(data.overlayLayers, layers);
</span><span class="cx">
</span><del>- data.gcHeapCapacityHistory.append(data.vm->heap.blockBytesAllocated());
- data.gcOwned = data.vm->heap.extraMemorySize();
</del><ins>+ size_t currentGCHeapCapacity = data.vm->heap.blockBytesAllocated();
+ size_t currentGCOwned = data.vm->heap.extraMemorySize();
+
+ data.categories[MemoryCategory::GCHeap].history.append(currentGCHeapCapacity);
+ data.categories[MemoryCategory::GCOwned].history.append(currentGCOwned);
+
+ // Subtract known subchunks from the bmalloc bucket.
+ // FIXME: Handle running with bmalloc disabled.
+ data.categories[MemoryCategory::bmalloc].history.last() -= currentGCHeapCapacity + currentGCOwned;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> [CATransaction begin];
</span></span></pre>
</div>
</div>
</body>
</html>