Ver Fonte

Memory profiler: add filters

lviguier há 1 ano atrás
pai
commit
0347b3dcde
4 ficheiros alterados com 250 adições e 172 exclusões
  1. 27 26
      bin/style.css
  2. 66 66
      bin/style.less
  3. 8 1
      hide/tools/memory/Memory.hx
  4. 149 79
      hide/view/Profiler.hx

+ 27 - 26
bin/style.css

@@ -2960,6 +2960,7 @@ div.gradient-box {
   height: 100%;
   width: 80%;
   display: flex;
+  background-color: #303030;
   flex-direction: column;
 }
 .profiler .left-panel .tree-map {
@@ -2967,46 +2968,40 @@ div.gradient-box {
   width: 100%;
   flex-grow: 1;
 }
-.profiler .left-panel .hierarchy {
-  background-color: #303030;
-  width: 100%;
-  max-height: 1200px;
-  height: 30%;
-}
-.profiler .left-panel .hierarchy table {
+.profiler .left-panel table {
   overflow-y: scroll;
 }
-.profiler .left-panel .hierarchy table thead {
+.profiler .left-panel table thead {
   position: sticky;
   top: 0;
   background-color: #151515;
 }
-.profiler .left-panel .hierarchy table thead td {
+.profiler .left-panel table thead td {
   font-weight: bold;
 }
-.profiler .left-panel .hierarchy table thead .sort-count,
-.profiler .left-panel .hierarchy table thead .sort-size {
+.profiler .left-panel table thead .sort-count,
+.profiler .left-panel table thead .sort-size {
   cursor: pointer;
 }
-.profiler .left-panel .hierarchy table tr:hover {
+.profiler .left-panel table tr:hover {
   background-color: #272727;
 }
-.profiler .left-panel .hierarchy table thead tr:hover {
+.profiler .left-panel table thead tr:hover {
   background-color: #151515;
 }
-.profiler .left-panel .hierarchy table td:last-child {
+.profiler .left-panel table td:last-child {
   width: 100%;
 }
-.profiler .left-panel .hierarchy table td:nth-child(3) {
+.profiler .left-panel table td:nth-child(3) {
   width: 70%;
   max-width: 700px;
 }
-.profiler .left-panel .hierarchy table td {
+.profiler .left-panel table td {
   padding: 3px 30px 3px 1px;
   overflow: hidden;
   white-space: nowrap;
 }
-.profiler .left-panel .hierarchy table td .folder {
+.profiler .left-panel table td .folder {
   cursor: pointer;
   margin-right: 10px;
   margin-left: 5px;
@@ -3014,19 +3009,19 @@ div.gradient-box {
   text-align: center;
   vertical-align: text-top;
 }
-.profiler .left-panel .hierarchy table td .outer-gauge {
+.profiler .left-panel table td .outer-gauge {
   background-color: #535353;
   width: 100%;
   height: 15px;
 }
-.profiler .left-panel .hierarchy table td .inner-gauge {
+.profiler .left-panel table td .inner-gauge {
   background-color: #d6d6d6;
   height: 15px;
 }
-.profiler .left-panel .hierarchy table td .icon {
+.profiler .left-panel table td .icon {
   padding-left: 10px;
 }
-.profiler .left-panel .hierarchy table .inspect-row td {
+.profiler .left-panel table .inspect-row td {
   width: 100px;
 }
 .profiler .right-panel {
@@ -3072,19 +3067,24 @@ div.gradient-box {
   background-color: #d6d6d6;
   height: 20px;
 }
-.profiler .right-panel h4 {
+.profiler .right-panel h4,
+.profiler .right-panel h5 {
   margin-bottom: 2px;
 }
+.profiler .right-panel h5 {
+  margin-top: 2px;
+}
+.profiler .right-panel hr {
+  margin-top: 25px;
+  border-top: 1px, solid, #666666;
+}
 .profiler .right-panel .title {
   text-align: center;
   font-size: 9pt;
   background-color: #151515;
   margin-bottom: 5px;
-  margin-top: 20px;
-  vertical-align: middle;
-}
-.profiler .right-panel .title:first-child {
   margin-top: 5px;
+  vertical-align: middle;
 }
 .profiler .right-panel .drop-zone {
   background-color: #111111;
@@ -3115,4 +3115,5 @@ div.gradient-box {
 .profiler .right-panel .files-input #process-btn {
   width: 100%;
   margin-top: 15px;
+  margin-bottom: 15px;
 }

+ 66 - 66
bin/style.less

@@ -3381,6 +3381,7 @@ div.gradient-box {
 		height: 100%;
 		width: 80%;
 		display: flex;
+		background-color: #303030;
 		flex-direction: column;
 
 		.tree-map {
@@ -3389,82 +3390,75 @@ div.gradient-box {
 			flex-grow: 1;
 		}
 
-		.hierarchy {
-			background-color: #303030;
-			width: 100%;
-			max-height: 1200px;
-			height: 30%;
-
-			table {
-				overflow-y: scroll;
+		table {
+			overflow-y: scroll;
 
-				thead {
-					position: sticky;
-					top: 0;
-					background-color: #151515;
-
-					td {
-						font-weight: bold;
-					}
+			thead {
+				position: sticky;
+				top: 0;
+				background-color: #151515;
 
-					.sort-count, .sort-size {
-						cursor: pointer;
-					}
+				td {
+					font-weight: bold;
 				}
 
-				tr:hover{
-					background-color: #272727;
+				.sort-count, .sort-size {
+					cursor: pointer;
 				}
+			}
 
-				thead {
-					tr:hover {
-						background-color: #151515;
-					}
-				}
+			tr:hover{
+				background-color: #272727;
+			}
 
-				td:last-child {
-					width: 100%;
+			thead {
+				tr:hover {
+					background-color: #151515;
 				}
+			}
 
-				td:nth-child(3) {
-					width: 70%;
-					max-width: 700px;
-				}
+			td:last-child {
+				width: 100%;
+			}
 
-				td {
-					padding: 3px 30px 3px 1px;
-					overflow: hidden;
-					white-space: nowrap;
+			td:nth-child(3) {
+				width: 70%;
+				max-width: 700px;
+			}
 
-					.folder {
-						cursor: pointer;
-						margin-right: 10px;
-						margin-left: 5px;
-						font-size: 12pt;
-						text-align: center;
-						vertical-align: text-top;
-					}
+			td {
+				padding: 3px 30px 3px 1px;
+				overflow: hidden;
+				white-space: nowrap;
 
-					.outer-gauge {
-						background-color: #535353;
-						width: 100%;
-						height: 15px;
-					}
+				.folder {
+					cursor: pointer;
+					margin-right: 10px;
+					margin-left: 5px;
+					font-size: 12pt;
+					text-align: center;
+					vertical-align: text-top;
+				}
 
-					.inner-gauge {
-						background-color: #d6d6d6;
-						height: 15px;
-					}
+				.outer-gauge {
+					background-color: #535353;
+					width: 100%;
+					height: 15px;
+				}
 
-					.icon {
-						padding-left:10px;
-					}
+				.inner-gauge {
+					background-color: #d6d6d6;
+					height: 15px;
 				}
 
-				.inspect-row {
-					td {
-						width: 100px;
-					}
+				.icon {
+					padding-left:10px;
+				}
+			}
+
+			.inspect-row {
+				td {
+					width: 100px;
 				}
 			}
 		}
@@ -3518,21 +3512,26 @@ div.gradient-box {
 			height: 20px;
 		}
 
-		h4 {
+		h4, h5 {
 			margin-bottom: 2px;
 		}
 
+		h5 {
+			margin-top:2px;
+		}
+
+		hr {
+			margin-top: 25px;
+			border-top: 1px, solid, #666666;
+		}
+
 		.title {
 			text-align: center;
 			font-size: 9pt;
 			background-color: #151515;
 			margin-bottom: 5px;
-			margin-top: 20px;
-			vertical-align: middle;
-		}
-
-		.title:first-child {
 			margin-top: 5px;
+			vertical-align: middle;
 		}
 
 		.drop-zone {
@@ -3569,6 +3568,7 @@ div.gradient-box {
 			#process-btn {
 				width: 100%;
 				margin-top: 15px;
+				margin-bottom: 15px;
 			}
 		}
 	}

+ 8 - 1
hide/tools/memory/Memory.hx

@@ -349,7 +349,9 @@ class Memory {
 		for (b in filteredBlocks)
 			fUsed += b.size;
 
-		return {
+		var objs = [];
+		var obj = {
+			memFile : memFile,
 			free: pagesSize - used - reserved,
 			used: used,
 			totalAllocated: pagesSize - reserved,
@@ -362,6 +364,11 @@ class Memory {
 			closuresCount : closuresPointers.length,
 			blockCount : blocks.length
 		};
+
+		objs.push(obj);
+		for (m in otherMems??[]) objs = objs.concat(m.getStatsObj());
+
+		return objs;
 	}
 
 	function printStats() {

+ 149 - 79
hide/view/Profiler.hx

@@ -49,16 +49,18 @@ class Profiler extends hide.ui.View<{}> {
 	public var lines(default, null) : Array<LineData> = [];
 	public var locationData(default, null) : Map<String, Array<LineData>> = [];
 
-	var hlPath = "";//"C:/Projects/wartales/trunk/wartales.hl";
-	var dumpPath = "";//"C:/Projects/wartales/trunk/capture.dump";
-
 	var error : String = "";
 
+	// Params
 	var sort : SortType = ByCount;
 	var sortOrderAscending = true;
 	var currentFilter : Filter = None;
-	var stats : Array<String> = [];
-	var statsObj : Dynamic;
+	var hlPath = "";
+	var dumpPaths : Array<String> = [];
+
+	// Cached values
+	var statsObj : Array<Dynamic> = [];
+	var fileSelects : Array<hide.comp.FileSelect> = [];
 
 	public function new( ?state ) {
 		super(state);
@@ -67,26 +69,25 @@ class Profiler extends hide.ui.View<{}> {
 	override function onDisplay() {
 		new Element('
 		<div class="profiler">
-			<div class="left-panel">
-				<div class="tree-map"></div>
-				<div class="hierarchy"></div>
-			</div>
+			<div class="left-panel"></div>
 			<div class="right-panel">
 				<div class="title">Files input</div>
-					<div class="files-input">
-						<div class="drop-zone hidden">
-							<p class="icon">+</p>
-							<p class="label">Drop .hl and .dump files here</p>
-						</div>
-						<div class="inputs">
-							<dl>
-								<dt>HL file</dt><dd><input class="hl-fileselect" type="fileselect" extensions="hl"/></dd>
-								<dt>Dump file</dt><dd><input class="dump-fileselect" type="fileselect" extension="dump"/></dd>
-							</dl>
-						</div>
+				<div class="files-input">
+					<div class="drop-zone hidden">
+						<p class="icon">+</p>
+						<p class="label">Drop .hl and .dump files here</p>
+					</div>
+					<div class="inputs">
+						<dl>
+							<dt>HL file</dt><dd><input class="hl-fileselect" type="fileselect" extensions="hl"/></dd>
+							<dt>Dump files</dt><dd><input class="dump-fileselect" type="fileselect" extension="dump"/></dd>
+							<dt></dt><dd><input class="dump-fileselect" type="fileselect" extension="dump"/></dd>
+						</dl>
 						<input type="button" value="Process Files" id="process-btn"/>
 					</div>
 				</div>
+				<div class="filters">
+				</div>
 			</div>
 		</div>'
 		).appendTo(element);
@@ -94,8 +95,18 @@ class Profiler extends hide.ui.View<{}> {
 		var hlSelect = new hide.comp.FileSelect(["hl"], null, element.find(".hl-fileselect"));
 		hlSelect.onChange = function() { hlPath = Ide.inst.getPath(hlSelect.path); };
 
-		var dumpSelect = new hide.comp.FileSelect(["dump"], null, element.find(".dump-fileselect"));
-		dumpSelect.onChange = function() { dumpPath = Ide.inst.getPath(dumpSelect.path); };
+		for (el in element.find(".dump-fileselect")) {
+			var dumpSelect = new hide.comp.FileSelect(["dump"], null, new Element(el));
+			fileSelects.push(dumpSelect);
+
+			dumpSelect.onChange = function() {
+				dumpPaths = [];
+				for (fs in fileSelects) {
+					if (fs.path != null && fs.path != "")
+						dumpPaths.push(Ide.inst.getPath(fs.path));
+				}
+			};
+		}
 
 		var dropZone = element.find(".drop-zone");
 		dropZone.css({display:'none'});
@@ -125,6 +136,7 @@ class Profiler extends hide.ui.View<{}> {
 				inputs.css({display:'block'});
 				isDragging = false;
 
+				var tmpDumpPaths = [];
 				for (f in dt.files) {
 					var arrSplit = Reflect.getProperty(f, "name").split('.');
 					var ext = arrSplit[arrSplit.length - 1];
@@ -138,13 +150,20 @@ class Profiler extends hide.ui.View<{}> {
 					}
 
 					if (ext == "dump") {
-						dumpPath = p;
-						dumpSelect.path = p;
+						tmpDumpPaths.push(p);
 						continue;
 					}
 
 					Ide.inst.error('File ${p} is not supported, please provide .dump file or .hl file');
 				}
+
+				if (tmpDumpPaths.length > 0) dumpPaths = [];
+				for (idx => p in tmpDumpPaths) {
+					dumpPaths.push(p);
+
+					if (idx < fileSelects.length)
+						fileSelects[idx].path = p;
+				}
 			}
 		});
 
@@ -160,7 +179,7 @@ class Profiler extends hide.ui.View<{}> {
 
 		var processBtn = element.find("#process-btn");
 		processBtn.on('click', function() {
-			if (hlPath == null || hlPath == '' || dumpPath == null || dumpPath == '') {
+			if (hlPath == null || hlPath == '' || dumpPaths == null || dumpPaths.length <= 0) {
 				Ide.inst.quickMessage('.hl or/and .dump files are missing. Please provide both files before hit the process button');
 				return;
 			}
@@ -170,8 +189,7 @@ class Profiler extends hide.ui.View<{}> {
 			refresh();
 		});
 
-		var hierarchyPanel = new hide.comp.ResizablePanel(Vertical, element.find(".hierarchy"));
-		hierarchyPanel.saveDisplayKey = "hierarchyPanel";
+		refreshFilters();
 	}
 
 	override function getTitle() {
@@ -179,7 +197,7 @@ class Profiler extends hide.ui.View<{}> {
 	}
 
 	function load() {
-		names = [ dumpPath ];
+		names = dumpPaths;
 
 		var result = loadAll();
 		if ( result != null) {
@@ -187,9 +205,8 @@ class Profiler extends hide.ui.View<{}> {
 		} else {
 			error = "";
 			if (names.length > 0) {
-				filter(None);
+				this.currentFilter = None;
 				displayTypes(sort, sortOrderAscending);
-				stats = mainMemory?.getStats();
 				statsObj = mainMemory?.getStatsObj();
 			}
 		}
@@ -231,36 +248,7 @@ class Profiler extends hide.ui.View<{}> {
 		mainMemory = currentMemory = null;
 		lines = [];
 		locationData.clear();
-	}
-
-	function filter(f : Filter) {
-		switch (f) {
-			case None :
-				currentMemory = mainMemory;
-				mainMemory.setFilterMode(None);
-			/*case Unique :
-				currentMemory = mainMemory;
-				mainMemory.setFilterMode(Unique);
-			case Difference :
-				mainMemory.setFilterMode(None);
-				if (mainMemory.otherMems.length > 0){
-					var other = mainMemory.otherMems[0];
-					other.otherMems = [mainMemory];
-					other.setFilterMode(Unique);
-					other.otherMems = [];
-					currentMemory = other;
-				}
-			case Intersected :
-				var other = mainMemory.otherMems[0];
-				other.setFilterMode(None);
-				currentMemory = mainMemory;
-				mainMemory.setFilterMode(Intersect);*/
-			default:
-				currentMemory = mainMemory;
-				mainMemory.setFilterMode(None);
-		}
-
-		locationData.clear();
+		statsObj = null;
 	}
 
 	public function displayTypes(sort : SortType = ByCount, asc : Bool = true) @:privateAccess{
@@ -290,32 +278,77 @@ class Profiler extends hide.ui.View<{}> {
 
 	public function refresh() {
 		refreshStats();
+		refreshFilters();
 		refreshHierarchicalView();
 	}
 
+	public function refreshFilters() {
+		var filters = element.find('.filters');
+		filters.empty();
+
+		var fileNames = [];
+		for (p in dumpPaths) {
+			var arr = p.split('/');
+			fileNames.push(arr[arr.length - 1]);
+		}
+
+		new Element('
+			<div class="title">Filters</div>
+			<dt>Filter</dt><dd>
+				<select class="dd-filters">
+					<option value="0">None</option>
+					<option value="1">Show ${fileNames[0]}</option>
+					<option value="2">Show ${fileNames[1]}</option>
+					<option value="3">Intersected</option>
+				</select>
+			</dd>
+		').appendTo(filters);
+
+		var ddFilters = filters.find('.dd-filters');
+		ddFilters.on('change', function(e) {
+			var enumVal = Filter.None;
+			var val : Int = Std.parseInt(ddFilters.val());
+			switch (val) {
+				case 0: enumVal = Filter.None;
+				case 1: enumVal = Filter.Unique;
+				case 2: enumVal = Filter.Difference;
+				case 3: enumVal = Filter.Intersected;
+			}
+
+			this.filterDatas(enumVal);
+		});
+
+		if (dumpPaths.length >= 2)
+			filters.css({ display:'block' });
+		else
+			filters.css({ display:'none' });
+	}
+
 	public function refreshStats() {
 		element.find('.stats').remove();
 
-		new Element ('
-		<div class="stats">
-			<div class="title">Stats</div>
-			<h4>Memory usage on device</h4>
-			<div class="outer-gauge"><div class="inner-gauge" title="${hide.tools.memory.Memory.MB(statsObj?.used)} used (${ 100 * statsObj?.used / statsObj?.totalAllocated}% of total)" style="width:${ 100 * statsObj?.used / statsObj?.totalAllocated}%;"></div></div>
+		var stats = new Element ('<div class="stats"><div class="title">Stats</div></div>').appendTo(element.find('.right-panel'));
+		for (idx => s in statsObj) {
+			new Element('
+			<h4>Memory usage</h4>
+			<h5>${s.memFile}</h5>
+			<div class="outer-gauge"><div class="inner-gauge" title="${hide.tools.memory.Memory.MB(s.used)} used (${ 100 * s.used / s.totalAllocated}% of total)" style="width:${ 100 * s.used / s.totalAllocated}%;"></div></div>
 			<dl>
-				<dt>Allocated</dt><dd>${hide.tools.memory.Memory.MB(statsObj?.totalAllocated)}</dd>
-				<dt>Used</dt><dd>${hide.tools.memory.Memory.MB(statsObj?.used)}</dd>
-				<dt>Free</dt><dd>${hide.tools.memory.Memory.MB(statsObj?.free)}</dd>
-				<dt>GC</dt><dd>${hide.tools.memory.Memory.MB(statsObj?.gc)}</dd>
+				<dt>Allocated</dt><dd>${hide.tools.memory.Memory.MB(s.totalAllocated)}</dd>
+				<dt>Used</dt><dd>${hide.tools.memory.Memory.MB(s.used)}</dd>
+				<dt>Free</dt><dd>${hide.tools.memory.Memory.MB(s.free)}</dd>
+				<dt>GC</dt><dd>${hide.tools.memory.Memory.MB(s.gc)}</dd>
 				<dt>&nbsp</dt><dd></dd>
-				<dt>Pages</dt><dd>${statsObj?.pagesCount} (${hide.tools.memory.Memory.MB(statsObj?.pagesSize)})</dd>
-				<dt>Roots</dt><dd>${statsObj?.rootsCount}</dd>
-				<dt>Stacks</dt><dd>${statsObj?.stackCount}</dd>
-				<dt>Types</dt><dd>${statsObj?.typesCount}</dd>
-				<dt>Closures</dt><dd>${statsObj?.closuresCount}</dd>
-				<dt>Live blocks</dt><dd>${statsObj?.blockCount}</dd>
+				<dt>Pages</dt><dd>${s.pagesCount} (${hide.tools.memory.Memory.MB(s.pagesSize)})</dd>
+				<dt>Roots</dt><dd>${s.rootsCount}</dd>
+				<dt>Stacks</dt><dd>${s.stackCount}</dd>
+				<dt>Types</dt><dd>${s.typesCount}</dd>
+				<dt>Closures</dt><dd>${s.closuresCount}</dd>
+				<dt>Live blocks</dt><dd>${s.blockCount}</dd>
 			</dl>
-		</div>
-		').appendTo(element.find('.right-panel'));
+			${idx < statsObj.length - 1 ? '<hr class="solid"></hr>' : ''}
+			').appendTo(stats);
+		}
 	}
 
 	public function refreshHierarchicalView() {
@@ -333,7 +366,7 @@ class Profiler extends hide.ui.View<{}> {
 				</tbody>
 			</table>
 		</div>'
-		).appendTo(element.find(".hierarchy"));
+		).appendTo(element.find(".left-panel"));
 
 		tab.find('.sort-count').on('click', function(e) { sortDatas(SortType.ByCount, sort.match(SortType.ByCount) ? !sortOrderAscending : false); });
 		tab.find('.sort-size').on('click', function(e) { sortDatas(SortType.ByMemory, sort.match(SortType.ByMemory) ? !sortOrderAscending : false); });
@@ -391,6 +424,43 @@ class Profiler extends hide.ui.View<{}> {
 		refreshHierarchicalView();
 	}
 
+	public function filterDatas(filter: Filter) @:privateAccess{
+		this.currentFilter = filter;
+
+		switch (currentFilter) {
+			case None :
+				currentMemory = mainMemory;
+				mainMemory.setFilterMode(None);
+			case Unique :
+				currentMemory = mainMemory;
+				mainMemory.setFilterMode(Unique);
+			case Difference :
+				mainMemory.setFilterMode(None);
+				if (mainMemory.otherMems.length > 0){
+					var other = mainMemory.otherMems[0];
+					other.otherMems = [mainMemory];
+					other.setFilterMode(Unique);
+					other.otherMems = [];
+					currentMemory = other;
+				}
+			case Intersected :
+				var other = mainMemory.otherMems[0];
+				other.setFilterMode(None);
+				currentMemory = mainMemory;
+				mainMemory.setFilterMode(Intersect);
+			default:
+				currentMemory = mainMemory;
+				mainMemory.setFilterMode(None);
+		}
+
+		locationData.clear();
+
+		displayTypes(sort, sortOrderAscending);
+		statsObj = mainMemory?.getStatsObj();
+
+		refreshHierarchicalView();
+	}
+
 	static var _ = hide.ui.View.register(Profiler);
 
 }
@@ -420,7 +490,7 @@ class ProfilerElement extends hide.comp.Component{
 		var count = path == null ? line.count : path.total.count;
 		var mem = path == null ? line.size : path.total.mem;
 
-		this.element = new Element('<tr><td><div class="folder icon ico ico-caret-right"></div>${count}</td><td>${hide.tools.memory.Memory.MB(mem)}</td><td title="${name}">${name}</td><td><div title="Allocated ${mem} (${100 * mem / Reflect.getProperty(profiler.statsObj, "totalAllocated")}% of total)" class="outer-gauge"><div class="inner-gauge" style="width:${100 * mem / Reflect.getProperty(profiler.statsObj, "totalAllocated")}%;"></div></div></td></tr>');
+		this.element = new Element('<tr><td><div class="folder icon ico ico-caret-right"></div>${count}</td><td>${hide.tools.memory.Memory.MB(mem)}</td><td title="${name}">${name}</td><td><div title="Allocated ${mem} (${100 * mem / Reflect.getProperty(profiler.statsObj[0], "totalAllocated")}% of total)" class="outer-gauge"><div class="inner-gauge" style="width:${100 * mem / Reflect.getProperty(profiler.statsObj[0], "totalAllocated")}%;"></div></div></td></tr>');
 		this.element.find('td').first().css({'padding-left':'${10 * depth}px'});
 
 		this.foldBtn = this.element.find('.folder');