| <!-- | |
| Copyright 2014 The Fuchsia Authors. All rights reserved. | |
| Use of this source code is governed by a BSD-style license that can be | |
| found in the LICENSE file. | |
| --> | |
| <html> | |
| <head> | |
| <title>Blob Size Analysis</title> | |
| <script src="d3_v3/d3.js" charset="utf-8"></script> | |
| <script src="D3BlobTreeMap.js" charset="utf-8"></script> | |
| <script src="data.js" charset="utf-8"></script> | |
| <style> | |
| body { | |
| margin: 0px; | |
| padding: 5px; | |
| } | |
| .swatch { | |
| border: 1px solid rgb(100,100,100); | |
| -webkit-user-select: none; | |
| cursor: default; | |
| } | |
| </style> | |
| <script> | |
| var treemap; | |
| var filterChanging = false; | |
| var savedSettings = {}; | |
| function init() { | |
| if (window.metadata !== undefined && window.metadata.subtitle) { | |
| document.getElementById('subtitle').innerHTML = ': ' + escape(metadata.subtitle); | |
| } | |
| initFilterOptions(); | |
| treemap = new D3BlobTreeMap( | |
| savedSettings.width, | |
| savedSettings.height, | |
| savedSettings.maxLevels); | |
| treemap.init(); | |
| } | |
| function getIdealSizes() { | |
| var width = window.innerWidth - 20; | |
| var height = window.innerHeight - 70; | |
| return {'width': width, 'height': height}; | |
| } | |
| function showReport(title, data, headers, dataFunction, styleFunction) { | |
| var div = d3.select('body').append('div') | |
| .style('margin', '0') | |
| .style('padding', '5px') | |
| .style('position', 'absolute') | |
| .style('top', '10%') | |
| .style('left', '10%') | |
| .style('background-color', 'rgba(255,255,255,0.9)') | |
| .style('width', '80%') | |
| .style('height', '80%') | |
| .style('z-index', '2147483647') | |
| .style('border', '3px ridge grey') | |
| .style('box-shadow', '10px 10px 5px rgba(80,80,80,0.7)') | |
| .style('text-align', 'center') | |
| .style('border-radius', '10px'); | |
| var titlebar = div.append('div') | |
| .style('margin', '0') | |
| .style('padding', '5px') | |
| .style('position', 'absolute') | |
| .style('top', '0%') | |
| .style('left', '0%') | |
| .style('width', '100%') | |
| .style('height', '10%') | |
| .style('font-size', 'x-large'); | |
| titlebar.text(title); | |
| var controls = div.append('div') | |
| .style('margin', '0') | |
| .style('padding', '5px') | |
| .style('position', 'absolute') | |
| .style('top', '90%') | |
| .style('left', '0%') | |
| .style('width', '100%') | |
| .style('height', '10%'); | |
| controls.append('input').attr('type', 'button') | |
| .attr('value', 'Dismiss') | |
| .on('click', function(){div.remove();}); | |
| var tableDiv = div.append('div') | |
| .style('overflow', 'auto') | |
| .style('position', 'absolute') | |
| .style('top', '10%') | |
| .style('left', '0%') | |
| .style('width', '100%') | |
| .style('height', '80%') | |
| .style('border-top', '1px solid rgb(230,230,230)') | |
| .style('border-bottom', '1px solid rgb(230,230,230)'); | |
| var table = tableDiv.append('table') | |
| .attr('border', '1') | |
| .attr('cellspacing', '0') | |
| .attr('cellpadding', '2') | |
| .style('margin-left', 'auto') | |
| .style('margin-right', 'auto'); | |
| var header = table.append('tr'); | |
| for (var i = 0; i < headers.length; i++) { | |
| header.append('th').text(headers[i]); | |
| } | |
| for (var i = 0; i < data.length; i++) { | |
| var row = table.append('tr'); | |
| for (j = 0; j < headers.length; j++) { | |
| var td = row.append('td'); | |
| if (styleFunction) { | |
| styleFunction.call(this, td, j); | |
| } | |
| dataFunction.call(this, data[i], j, td); | |
| } | |
| } | |
| } | |
| function bigBlobsReport() { | |
| var list = treemap.biggestblobs(100); | |
| var headers = ['Rank', 'Size (Bytes)', 'Type', 'Location']; | |
| var styleFunction = function(selection, index) { | |
| if (index === 3) { | |
| selection.style('font-family', 'monospace'); | |
| } | |
| }; | |
| var recordIndex = 1; | |
| var dataFunction = function(record, index, cell) { | |
| if (index === 0) { | |
| cell.text(recordIndex++); | |
| } else if (index === 1) { | |
| cell.text(D3BlobTreeMap._pretty(record.value)); | |
| } else if (index === 2) { | |
| cell.text(record.t); | |
| } else { | |
| cell.append('span').text(treemap.pathFor(record)); | |
| cell.append('br'); | |
| cell.append('span').text('Blob: '); | |
| cell.append('span').text(record.n); | |
| } | |
| }; | |
| showReport('100 Largest Blobs', list, headers, dataFunction, styleFunction); | |
| } | |
| function blobFilterTextChanged() { | |
| if (filterChanging) return true; | |
| filterChanging = true; | |
| var enabled = document.getElementById('blob_types_filter').value; | |
| for (var x=0; x<3; x++) { | |
| var checkBox = document.getElementById('check_' + x); | |
| checkBox.checked = (enabled.indexOf(checkBox.value) != -1); | |
| } | |
| filterChanging = false; | |
| } | |
| function updateFilterText() { | |
| if (filterChanging) return true; | |
| filterChanging = true; | |
| var text = ''; | |
| for (var x=0; x<D3BlobTreeMap._BLOB_TYPES.length; x++) { | |
| var checkBox = document.getElementById('check_' + x); | |
| if (checkBox.checked) { | |
| text += checkBox.value; | |
| } | |
| } | |
| document.getElementById('blob_types_filter').value=text; | |
| filterChanging = false; | |
| } | |
| function initFilterOptions() { | |
| updateFilterText(); | |
| for (var x=0; x<D3BlobTreeMap._BLOB_TYPES.length; x++) { | |
| var checkBox = document.getElementById('check_' + x); | |
| checkBox.onchange=updateFilterText; | |
| var swatch = document.getElementById('swatch_' + x); | |
| swatch.style.backgroundColor = D3BlobTreeMap.getColorForType(checkBox.value).toString(); | |
| } | |
| var gteCheckbox = document.getElementById('check_gte'); | |
| gteCheckbox.onchange = function() { | |
| document.getElementById('blob_filter_gte').disabled = !gteCheckbox.checked; | |
| } | |
| var regexCheckbox = document.getElementById('check_regex'); | |
| regexCheckbox.onchange = function() { | |
| document.getElementById('blob_filter_regex').disabled = !regexCheckbox.checked; | |
| } | |
| var excludeRegexCheckbox = document.getElementById('check_exclude_regex'); | |
| excludeRegexCheckbox.onchange = function() { | |
| document.getElementById('blob_filter_exclude_regex').disabled = !excludeRegexCheckbox.checked; | |
| } | |
| var idealSizes = getIdealSizes(); | |
| document.getElementById('width').value = idealSizes.width; | |
| document.getElementById('height').value = idealSizes.height; | |
| saveFilterSettings(); | |
| } | |
| function filterSetAll(enabled) { | |
| for (var x=0; x<D3BlobTreeMap._BLOB_TYPES.length; x++) { | |
| var checkBox = document.getElementById('check_' + x); | |
| checkBox.checked = enabled; | |
| } | |
| updateFilterText(); | |
| } | |
| function showOptions() { | |
| loadFilterSettings(); | |
| var container = document.getElementById('options_container'); | |
| var w = container.offsetWidth; | |
| var h = container.offsetHeight; | |
| container.style.margin = '-' + (h/2) + 'px 0 0 -' + (w/2) + 'px'; | |
| container.style.visibility = 'visible'; | |
| } | |
| function hideOptions() { | |
| var container = document.getElementById('options_container'); | |
| container.style.visibility = 'hidden'; | |
| } | |
| function applySettings() { | |
| hideOptions(); | |
| var oldWidth = savedSettings.width; | |
| var oldHeight = savedSettings.height; | |
| var oldblobs = savedSettings.blobTypes; | |
| var oldRegex = savedSettings.regex; | |
| var oldExcludeRegex = savedSettings.excludeRegex; | |
| var oldGte = savedSettings.gte; | |
| var oldMaxLevels = savedSettings.maxLevels; | |
| saveFilterSettings(); | |
| var resizeNeeded = oldWidth !== savedSettings.width || oldHeight !== savedSettings.height; | |
| var regexChanged = oldRegex !== savedSettings.regex; | |
| var excludeRegexChanged = oldExcludeRegex !== savedSettings.excludeRegex; | |
| var blobsChanged = oldblobs !== savedSettings.blobTypes; | |
| var gteChanged = oldGte !== savedSettings.gte; | |
| var filterChanged = regexChanged || excludeRegexChanged || blobsChanged || gteChanged; | |
| var maxLevelsChanged = oldMaxLevels !== savedSettings.maxLevels; | |
| if (filterChanged) { | |
| // Type filters | |
| typeFilter = function(datum) { | |
| if (datum.depth === 0) return true; // root node | |
| if (datum.t === undefined) return true; | |
| return savedSettings.blobTypes !== undefined && | |
| savedSettings.blobTypes.indexOf(datum.t) !== -1; | |
| } | |
| // Regex filter | |
| var regexFilter = undefined; | |
| if (savedSettings.regex !== undefined && savedSettings.regex.length > 0) { | |
| console.log('filter: regex is "' + savedSettings.regex + '"'); | |
| var regex = new RegExp(savedSettings.regex); | |
| regexFilter = function(datum) { | |
| if (datum.depth === 0) return true; // root node | |
| var fullName = this.pathFor(datum); | |
| if (datum.children === undefined) { // it is a leaf node (blob) | |
| fullName += ':' + datum.n; | |
| } | |
| return regex.test(fullName); | |
| } | |
| } | |
| // Exclude regex filter | |
| var excludeRegexFilter = undefined; | |
| if (savedSettings.excludeRegex !== undefined && savedSettings.excludeRegex.length > 0) { | |
| console.log('filter: exclude-regex is "' + savedSettings.excludeRegex + '"'); | |
| var excludeRegex = new RegExp(savedSettings.excludeRegex); | |
| excludeRegexFilter = function(datum) { | |
| if (datum.depth === 0) return true; // root node | |
| var fullName = this.pathFor(datum); | |
| if (datum.children === undefined) { // it is a leaf node (blob) | |
| fullName += ':' + datum.n; | |
| } | |
| return !excludeRegex.test(fullName); | |
| } | |
| } | |
| // Size filter | |
| var sizeFilter = undefined; | |
| if (savedSettings.gte !== undefined) { | |
| console.log('filter: minimum size is ' + savedSettings.gte + ' bytes'); | |
| sizeFilter = function(datum) { | |
| if (datum.children !== undefined) return true; // non-leaf | |
| if (datum.value === undefined) console.log('whoops'); | |
| return datum.value >= savedSettings.gte; | |
| } | |
| } | |
| // Make a filter to apply to the tree | |
| var filter = function(datum) { | |
| if (typeFilter && !typeFilter.call(this, datum)) return false; | |
| if (regexFilter && !regexFilter.call(this, datum)) return false; | |
| if (excludeRegexFilter && !excludeRegexFilter.call(this, datum)) return false; | |
| if (sizeFilter && !sizeFilter.call(this, datum)) return false; | |
| return true; | |
| }; | |
| treemap.filter(filter); | |
| } | |
| // Adjust levels if needed. | |
| if (maxLevelsChanged) { | |
| treemap.setMaxLevels(savedSettings.maxLevels); | |
| } | |
| // Resize map if necessary. | |
| if (resizeNeeded) { | |
| console.log('desired treemap dimensions have changed, requesting resize'); | |
| treemap.resize(savedSettings.width, savedSettings.height); | |
| } | |
| } | |
| function cancelSettings() { | |
| hideOptions(); | |
| loadFilterSettings(); | |
| } | |
| function saveFilterSettings() { | |
| savedSettings.blobTypes = document.getElementById('blob_types_filter').value; | |
| if (document.getElementById('check_regex').checked) { | |
| savedSettings.regex = document.getElementById('blob_filter_regex').value; | |
| } else { | |
| savedSettings.regex = undefined; | |
| } | |
| if (document.getElementById('check_exclude_regex').checked) { | |
| savedSettings.excludeRegex = document.getElementById('blob_filter_exclude_regex').value; | |
| } else { | |
| savedSettings.excludeRegex = undefined; | |
| } | |
| if (document.getElementById('check_gte').checked) { | |
| savedSettings.gte = parseInt(document.getElementById('blob_filter_gte').value); | |
| } else { | |
| savedSettings.gte = undefined; | |
| } | |
| savedSettings.width = parseInt(document.getElementById('width').value); | |
| savedSettings.height = parseInt(document.getElementById('height').value); | |
| savedSettings.maxLevels = parseInt(document.getElementById('max_levels').value); | |
| } | |
| function loadFilterSettings() { | |
| document.getElementById('blob_types_filter').value = savedSettings.blobTypes; | |
| blobFilterTextChanged(); | |
| if (savedSettings.regex !== undefined) { | |
| document.getElementById('check_regex').checked = true; | |
| document.getElementById('blob_filter_regex').value = savedSettings.regex; | |
| } else { | |
| document.getElementById('check_regex').checked = false; | |
| } | |
| if (savedSettings.excludeRegex !== undefined) { | |
| document.getElementById('check_exclude_regex').checked = true; | |
| document.getElementById('blob_filter_exclude_regex').value = savedSettings.excludeRegex; | |
| } else { | |
| document.getElementById('check_exclude_regex').checked = false; | |
| } | |
| if (savedSettings.gte !== undefined) { | |
| document.getElementById('check_gte').checked = true; | |
| document.getElementById('blob_filter_gte').value = savedSettings.gte; | |
| } else { | |
| document.getElementById('check_gte').checked = false; | |
| } | |
| document.getElementById('width').value = savedSettings.width; | |
| document.getElementById('height').value = savedSettings.height; | |
| document.getElementById('max_levels').value = savedSettings.maxLevels; | |
| } | |
| function escape(str) { | |
| return str.replace(/&/g, '&') | |
| .replace(/"/g, '"') | |
| .replace(/</g, '<') | |
| .replace(/>/g, '>'); | |
| } | |
| </script> | |
| </head> | |
| <body onload='init()'> | |
| <div style='position: absolute; top: 5px; left: 5px;'> | |
| <input type='button' onclick='showOptions()' value='Options & Legend...'> | |
| <span style='-webkit-user-select: none; cursor: help;' title='Click to view the blob legend or to configure filters and options for the treemap'>[?]</span> | |
| </div> | |
| <div style='position: absolute; right: 5px; top: 5px; white-space: nowrap;'> | |
| Reports: | |
| <input type='button' onclick='bigBlobsReport()' value='Large Blobs' title='Click to view a report of the largest 100 blobs that are with the bounds of the treemap that is currently displayed.'> | |
| </div> | |
| <div style='text-align: center; margin-bottom: 5px;'> | |
| <span style='font-size: x-large; font-weight: bold; font-variant: small-caps'>Blob Size Analysis<span id='subtitle'></span></span> | |
| <br><span style='font-size: small; font-style: italic;'>Double-click a box to zoom in, double-click outermost title to zoom out.</span> | |
| </div> | |
| <table id='options_container' style='visibility: hidden; border: 3px ridge grey; padding: 0px; top: 50%; left: 50%; position: fixed; z-index: 2147483646; overflow: auto; background-color: rgba(255,255,255,0.9); border-radius: 10px; box-shadow: 10px 10px 5px rgba(80,80,80,0.7);'><tr><td style='vertical-align: top'> | |
| <table cellspacing=0 cellborder=0 style='width:100%'> | |
| <tr><th colspan=3 style='padding-bottom: .25em; text-decoration: underline;'>blob Types To Show</th></tr> | |
| <tr> | |
| <td style='white-space: nowrap; vertical-align: top;'> | |
| <span class='swatch' id='swatch_0'> </span><input checked type='checkbox' id='check_0' value='dart'>Shared Dart | |
| <br><span class='swatch' id='swatch_1'> </span><input checked type='checkbox' id='check_1' value='?'>Shared Unrecognized | |
| <br><span class='swatch' id='swatch_2'> </span><input checked type='checkbox' id='check_2' value='unique'>Unique | |
| <br><span class='swatch' id='swatch_3'> </span><input checked type='checkbox' id='check_3' value='uniDart'>Unique Dart | |
| </tr> | |
| <tr><td colspan=3 style='text-align: center; white-space: nowrap; padding-top: 1em;'> | |
| Select <input type='button' onclick='filterSetAll(true)' value='All'>, | |
| <input type='button' onclick='filterSetAll(false)' value='None'>, | |
| or type a string: <input id='blob_types_filter' size=30 value='' onkeyup='blobFilterTextChanged()' onblur='updateFilterText()'> | |
| <span style='-webkit-user-select: none; cursor: help;' title='Enter codes from the list above for the blobs you want to see. The checkboxes will update automatically to match the string that you enter.'>[?]</span> | |
| </td></tr> | |
| </table> | |
| </td></tr><tr><td style='vertical-align: top; padding-top: 10px; border-top: 1px solid grey;'> | |
| <table cellspacing=0 cellborder=0 style='width: 100%'> | |
| <tr><th colspan=2 style='padding-bottom: .25em; text-decoration: underline;'>Advanced Options</th></tr> | |
| <tr> | |
| <td style='white-space: nowrap; vertical-align: top;'> | |
| <input type='checkbox' id='check_regex'> | |
| Only include blobs matching this regex: | |
| </td> | |
| <td style='text-align: right; vertical-align: top;'> | |
| <input disabled id='blob_filter_regex' size=30 value='' style='text-align: right;'> | |
| <span style='-webkit-user-select: none; cursor: help;' title='Enter a javascript regex. Only blobs that match this regex will be shown. This filter applies before any exclusion regex specified below. The format of each blob is [path]:[blob_name]'>[?]</span> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td style='white-space: nowrap; vertical-align: top;'> | |
| <input type='checkbox' id='check_exclude_regex'> | |
| Exclude all blobs matching this regex: | |
| </td> | |
| <td style='text-align: right; vertical-align: top;'> | |
| <input disabled id='blob_filter_exclude_regex' size=30 value='' style='text-align: right;'> | |
| <span style='-webkit-user-select: none; cursor: help;' title='Enter a javascript regex. blobs that match this tegex will not be shown. This filter applies after any inclusion filter specified above. The format of each blob is [path]:[blob_name]'>[?]</span> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td style='white-space: nowrap; vertical-align: top;'> | |
| <input type='checkbox' id='check_gte'> | |
| Only include blobs that are at least <span style='font-style: italic;'>n</span> bytes: | |
| </td> | |
| <td style='text-align: right; vertical-align: top;'> | |
| <input disabled id='blob_filter_gte' size=8 value='' style='text-align: right;'> | |
| <span style='-webkit-user-select: none; cursor: help;' title='blobs whose size is less than this value will be hidden.'>[?]</span> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td style='white-space: nowrap vertical-align: top;;'> | |
| Show at most <span style='font-style: italic;'>n</span> levels of detail at a time: | |
| </td> | |
| <td style='text-align: right; vertical-align: top;'> | |
| <input id='max_levels' size=4 value='2' style='text-align: right;'><span style='-webkit-user-select: none; cursor: help;' title='Increasing this value shows more detail without the need to zoom, but uses more computing power.'>[?]</span> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td style='white-space: nowrap vertical-align: top;;'> | |
| Set the size of the treemap to <span style='font-style: italic;'>W x H</span> pixels: | |
| </td> | |
| <td style='text-align: right; vertical-align: top;'> | |
| <input id='width' size=4 value='' style='text-align: right;'> | |
| x <input id='height' size=4 value='' style='text-align: right;'> | |
| </td> | |
| </tr> | |
| </table> | |
| </td></tr> | |
| <tr><td style='padding-top: 10px; text-align: right; border-top: 1px solid grey'> | |
| <input type='button' value='Apply' onclick='applySettings()'> | |
| <input type='button' value='Cancel' onclick='cancelSettings()'> | |
| </td></tr></table> | |
| </body> | |
| </html> |