1 <!-- 2 Copyright 2014 The Chromium Authors. All rights reserved. 3 Use of this source code is governed by a BSD-style license that can be 4 found in the LICENSE file. 5 --> 6 <html> 7 <head> 8 <title>Binary Size Analysis</title> 9 <script src="d3/d3.js" charset="utf-8"></script> 10 <script src="D3SymbolTreeMap.js" charset="utf-8"></script> 11 <script src="data.js" charset="utf-8"></script> 12 <style> 13 body { 14 margin: 0px; 15 padding: 5px; 16 } 17 .swatch { 18 border: 1px solid rgb(100,100,100); 19 -webkit-user-select: none; 20 cursor: default; 21 } 22 </style> 23 <script> 24 var treemap; 25 var filterChanging = false; 26 var savedSettings = {}; 27 28 function init() { 29 if (window.metadata !== undefined && window.metadata.subtitle) { 30 document.getElementById('subtitle').innerHTML = ': ' + escape(metadata.subtitle); 31 } 32 initFilterOptions(); 33 treemap = new D3SymbolTreeMap( 34 savedSettings.width, 35 savedSettings.height, 36 savedSettings.maxLevels); 37 treemap.init(); 38 } 39 40 function getIdealSizes() { 41 var width = window.innerWidth - 20; 42 var height = window.innerHeight - 70; 43 return {'width': width, 'height': height}; 44 } 45 46 function showReport(title, data, headers, dataFunction, styleFunction) { 47 var div = d3.select('body').append('div') 48 .style('margin', '0') 49 .style('padding', '5px') 50 .style('position', 'absolute') 51 .style('top', '10%') 52 .style('left', '10%') 53 .style('background-color', 'rgba(255,255,255,0.9)') 54 .style('width', '80%') 55 .style('height', '80%') 56 .style('z-index', '2147483647') 57 .style('border', '3px ridge grey') 58 .style('box-shadow', '10px 10px 5px rgba(80,80,80,0.7)') 59 .style('text-align', 'center') 60 .style('border-radius', '10px'); 61 var titlebar = div.append('div') 62 .style('margin', '0') 63 .style('padding', '5px') 64 .style('position', 'absolute') 65 .style('top', '0%') 66 .style('left', '0%') 67 .style('width', '100%') 68 .style('height', '10%') 69 .style('font-size', 'x-large'); 70 titlebar.text(title); 71 var controls = div.append('div') 72 .style('margin', '0') 73 .style('padding', '5px') 74 .style('position', 'absolute') 75 .style('top', '90%') 76 .style('left', '0%') 77 .style('width', '100%') 78 .style('height', '10%'); 79 controls.append('input').attr('type', 'button') 80 .attr('value', 'Dismiss') 81 .on('click', function(){div.remove();}); 82 83 var tableDiv = div.append('div') 84 .style('overflow', 'auto') 85 .style('position', 'absolute') 86 .style('top', '10%') 87 .style('left', '0%') 88 .style('width', '100%') 89 .style('height', '80%') 90 .style('border-top', '1px solid rgb(230,230,230)') 91 .style('border-bottom', '1px solid rgb(230,230,230)'); 92 var table = tableDiv.append('table') 93 .attr('border', '1') 94 .attr('cellspacing', '0') 95 .attr('cellpadding', '2') 96 .style('margin-left', 'auto') 97 .style('margin-right', 'auto'); 98 var header = table.append('tr'); 99 for (var i = 0; i < headers.length; i++) { 100 header.append('th').text(headers[i]); 101 } 102 103 for (var i = 0; i < data.length; i++) { 104 var row = table.append('tr'); 105 for (j = 0; j < headers.length; j++) { 106 var td = row.append('td'); 107 if (styleFunction) { 108 styleFunction.call(this, td, j); 109 } 110 dataFunction.call(this, data[i], j, td); 111 } 112 } 113 } 114 115 function bigSymbolsReport() { 116 var list = treemap.biggestSymbols(100); 117 var headers = ['Rank', 'Size (Bytes)', 'Type', 'Location']; 118 var styleFunction = function(selection, index) { 119 if (index === 3) { 120 selection.style('font-family', 'monospace'); 121 } 122 }; 123 var recordIndex = 1; 124 var dataFunction = function(record, index, cell) { 125 if (index === 0) { 126 cell.text(recordIndex++); 127 } else if (index === 1) { 128 cell.text(D3SymbolTreeMap._pretty(record.value)); 129 } else if (index === 2) { 130 cell.text(record.t); 131 } else { 132 if (treemap.pathFor(record).indexOf('/out') == 0) { 133 cell.append('span').text(treemap.pathFor(record)); 134 cell.append('br'); 135 cell.append('span').text('Symbol: '); 136 cell.append('span').text(record.n); 137 } else { 138 var href = 'https://code.google.com/p/chromium/codesearch#chromium/src' 139 + treemap.pathFor(record) 140 + '&q=' 141 + record.n; 142 cell.append('a') 143 .attr('href', href) 144 .attr('target', '_blank') 145 .text(treemap.pathFor(record)); 146 cell.append('br'); 147 cell.append('span').text('Symbol: '); 148 cell.append('span').text(record.n); 149 } 150 } 151 }; 152 showReport('100 Largest Symbols', list, headers, dataFunction, styleFunction); 153 } 154 155 function bigPathsReport() { 156 var list = treemap.biggestPaths(100); 157 var headers = ['Rank', 'Size (Bytes)', 'Location']; 158 var styleFunction = function(selection, index) { 159 if (index === 2) { 160 selection.style('font-family', 'monospace'); 161 } 162 }; 163 var recordIndex = 1; 164 var dataFunction = function(record, index, cell) { 165 if (index === 0) { 166 cell.text(recordIndex++); 167 } else if (index === 1) { 168 cell.text(D3SymbolTreeMap._pretty(record.value)); 169 } else if (index === 2) { 170 if (treemap.pathFor(record).indexOf('/out') == 0) { 171 cell.text(treemap.pathFor(record)); 172 } else { 173 var href = 'https://code.google.com/p/chromium/codesearch#chromium/src' + treemap.pathFor(record); 174 cell.append('a') 175 .attr('href', href) 176 .attr('target', '_blank') 177 .text(treemap.pathFor(record)); 178 } 179 180 } 181 }; 182 showReport('100 Largest Paths', list, headers, dataFunction, styleFunction); 183 } 184 185 function symbolFilterTextChanged() { 186 if (filterChanging) return true; 187 filterChanging = true; 188 var enabled = document.getElementById('symbol_types_filter').value; 189 for (var x=0; x<=25; x++) { 190 var checkBox = document.getElementById('check_' + x); 191 checkBox.checked = (enabled.indexOf(checkBox.value) != -1); 192 } 193 filterChanging = false; 194 } 195 196 function updateFilterText() { 197 if (filterChanging) return true; 198 filterChanging = true; 199 var text = ''; 200 for (var x=0; x<=25; x++) { 201 var checkBox = document.getElementById('check_' + x); 202 if (checkBox.checked) { 203 text += checkBox.value; 204 } 205 } 206 document.getElementById('symbol_types_filter').value=text; 207 filterChanging = false; 208 } 209 210 function initFilterOptions() { 211 updateFilterText(); 212 for (var x=0; x<=25; x++) { 213 var checkBox = document.getElementById('check_' + x); 214 checkBox.onchange=updateFilterText; 215 var swatch = document.getElementById('swatch_' + x); 216 swatch.style.backgroundColor = D3SymbolTreeMap.getColorForType(checkBox.value).toString(); 217 } 218 var gteCheckbox = document.getElementById('check_gte'); 219 gteCheckbox.onchange = function() { 220 document.getElementById('symbol_filter_gte').disabled = !gteCheckbox.checked; 221 } 222 var regexCheckbox = document.getElementById('check_regex'); 223 regexCheckbox.onchange = function() { 224 document.getElementById('symbol_filter_regex').disabled = !regexCheckbox.checked; 225 } 226 var excludeRegexCheckbox = document.getElementById('check_exclude_regex'); 227 excludeRegexCheckbox.onchange = function() { 228 document.getElementById('symbol_filter_exclude_regex').disabled = !excludeRegexCheckbox.checked; 229 } 230 var idealSizes = getIdealSizes(); 231 document.getElementById('width').value = idealSizes.width; 232 document.getElementById('height').value = idealSizes.height; 233 saveFilterSettings(); 234 } 235 236 function filterSetAll(enabled) { 237 for (var x=0; x<=25; x++) { 238 var checkBox = document.getElementById('check_' + x); 239 checkBox.checked = enabled; 240 } 241 updateFilterText(); 242 } 243 244 function showOptions() { 245 loadFilterSettings(); 246 var container = document.getElementById('options_container'); 247 var w = container.offsetWidth; 248 var h = container.offsetHeight; 249 container.style.margin = '-' + (h/2) + 'px 0 0 -' + (w/2) + 'px'; 250 container.style.visibility = 'visible'; 251 } 252 253 function hideOptions() { 254 var container = document.getElementById('options_container'); 255 container.style.visibility = 'hidden'; 256 } 257 258 function applySettings() { 259 hideOptions(); 260 var oldWidth = savedSettings.width; 261 var oldHeight = savedSettings.height; 262 var oldSymbols = savedSettings.symbolTypes; 263 var oldRegex = savedSettings.regex; 264 var oldExcludeRegex = savedSettings.excludeRegex; 265 var oldGte = savedSettings.gte; 266 var oldMaxLevels = savedSettings.maxLevels; 267 saveFilterSettings(); 268 var resizeNeeded = oldWidth !== savedSettings.width || oldHeight !== savedSettings.height; 269 var regexChanged = oldRegex !== savedSettings.regex; 270 var excludeRegexChanged = oldExcludeRegex !== savedSettings.excludeRegex; 271 var symbolsChanged = oldSymbols !== savedSettings.symbolTypes; 272 var gteChanged = oldGte !== savedSettings.gte; 273 var filterChanged = regexChanged || excludeRegexChanged || symbolsChanged || gteChanged; 274 var maxLevelsChanged = oldMaxLevels !== savedSettings.maxLevels; 275 276 if (filterChanged) { 277 // Type filters 278 typeFilter = function(datum) { 279 if (datum.depth === 0) return true; // root node 280 if (datum.t === undefined) return true; 281 return savedSettings.symbolTypes !== undefined && 282 savedSettings.symbolTypes.indexOf(datum.t) !== -1; 283 } 284 285 // Regex filter 286 var regexFilter = undefined; 287 if (savedSettings.regex !== undefined && savedSettings.regex.length > 0) { 288 console.log('filter: regex is "' + savedSettings.regex + '"'); 289 var regex = new RegExp(savedSettings.regex); 290 regexFilter = function(datum) { 291 if (datum.depth === 0) return true; // root node 292 var fullName = this.pathFor(datum); 293 if (datum.children === undefined) { // it is a leaf node (symbol) 294 fullName += ':' + datum.n; 295 } 296 return regex.test(fullName); 297 } 298 } 299 300 // Exclude regex filter 301 var excludeRegexFilter = undefined; 302 if (savedSettings.excludeRegex !== undefined && savedSettings.excludeRegex.length > 0) { 303 console.log('filter: exclude-regex is "' + savedSettings.excludeRegex + '"'); 304 var excludeRegex = new RegExp(savedSettings.excludeRegex); 305 excludeRegexFilter = function(datum) { 306 if (datum.depth === 0) return true; // root node 307 var fullName = this.pathFor(datum); 308 if (datum.children === undefined) { // it is a leaf node (symbol) 309 fullName += ':' + datum.n; 310 } 311 return !excludeRegex.test(fullName); 312 } 313 } 314 315 // Size filter 316 var sizeFilter = undefined; 317 if (savedSettings.gte !== undefined) { 318 console.log('filter: minimum size is ' + savedSettings.gte + ' bytes'); 319 sizeFilter = function(datum) { 320 if (datum.children !== undefined) return true; // non-leaf 321 if (datum.value === undefined) console.log('whoops'); 322 return datum.value >= savedSettings.gte; 323 } 324 } 325 326 // Make a filter to apply to the tree 327 var filter = function(datum) { 328 if (typeFilter && !typeFilter.call(this, datum)) return false; 329 if (regexFilter && !regexFilter.call(this, datum)) return false; 330 if (excludeRegexFilter && !excludeRegexFilter.call(this, datum)) return false; 331 if (sizeFilter && !sizeFilter.call(this, datum)) return false; 332 return true; 333 }; 334 treemap.filter(filter); 335 } 336 337 // Adjust levels if needed. 338 if (maxLevelsChanged) { 339 treemap.setMaxLevels(savedSettings.maxLevels); 340 } 341 342 // Resize map if necessary. 343 if (resizeNeeded) { 344 console.log('desired treemap dimensions have changed, requesting resize'); 345 treemap.resize(savedSettings.width, savedSettings.height); 346 } 347 } 348 349 function cancelSettings() { 350 hideOptions(); 351 loadFilterSettings(); 352 } 353 354 function saveFilterSettings() { 355 savedSettings.symbolTypes = document.getElementById('symbol_types_filter').value; 356 if (document.getElementById('check_regex').checked) { 357 savedSettings.regex = document.getElementById('symbol_filter_regex').value; 358 } else { 359 savedSettings.regex = undefined; 360 } 361 if (document.getElementById('check_exclude_regex').checked) { 362 savedSettings.excludeRegex = document.getElementById('symbol_filter_exclude_regex').value; 363 } else { 364 savedSettings.excludeRegex = undefined; 365 } 366 if (document.getElementById('check_gte').checked) { 367 savedSettings.gte = parseInt(document.getElementById('symbol_filter_gte').value); 368 } else { 369 savedSettings.gte = undefined; 370 } 371 savedSettings.width = parseInt(document.getElementById('width').value); 372 savedSettings.height = parseInt(document.getElementById('height').value); 373 savedSettings.maxLevels = parseInt(document.getElementById('max_levels').value); 374 } 375 376 function loadFilterSettings() { 377 document.getElementById('symbol_types_filter').value = savedSettings.symbolTypes; 378 symbolFilterTextChanged(); 379 if (savedSettings.regex !== undefined) { 380 document.getElementById('check_regex').checked = true; 381 document.getElementById('symbol_filter_regex').value = savedSettings.regex; 382 } else { 383 document.getElementById('check_regex').checked = false; 384 } 385 if (savedSettings.excludeRegex !== undefined) { 386 document.getElementById('check_exclude_regex').checked = true; 387 document.getElementById('symbol_filter_exclude_regex').value = savedSettings.excludeRegex; 388 } else { 389 document.getElementById('check_exclude_regex').checked = false; 390 } 391 if (savedSettings.gte !== undefined) { 392 document.getElementById('check_gte').checked = true; 393 document.getElementById('symbol_filter_gte').value = savedSettings.gte; 394 } else { 395 document.getElementById('check_gte').checked = false; 396 } 397 document.getElementById('width').value = savedSettings.width; 398 document.getElementById('height').value = savedSettings.height; 399 document.getElementById('max_levels').value = savedSettings.maxLevels; 400 } 401 402 function escape(str) { 403 return str.replace(/&/g, '&') 404 .replace(/"/g, '"') 405 .replace(/</g, '<') 406 .replace(/>/g, '>'); 407 } 408 </script> 409 </head> 410 <body onload='init()'> 411 <div style='position: absolute; top: 5px; left: 5px;'> 412 <input type='button' onclick='showOptions()' value='Options & Legend...'> 413 <span style='-webkit-user-select: none; cursor: help;' title='Click to view the symbol legend or to configure filters and options for the treemap'>[?]</span> 414 </div> 415 <div style='position: absolute; right: 5px; top: 5px; white-space: nowrap;'> 416 Reports: 417 <input type='button' onclick='bigSymbolsReport()' value='Large Symbols' title='Click to view a report of the largest 100 symbols that are with the bounds of the treemap that is currently displayed.'> 418 <input type='button' onclick='bigPathsReport()' value='Large Files' title='Click to view a report of the largest 100 source files that are with the bounds of the treemap that is currently displayed.'> 419 </div> 420 <div style='text-align: center; margin-bottom: 5px;'> 421 <span style='font-size: x-large; font-weight: bold; font-variant: small-caps'>Binary Size Analysis<span id='subtitle'></span></span> 422 <br><span style='font-size: small; font-style: italic;'>Double-click a box to zoom in, double-click outermost title to zoom out.</span> 423 </div> 424 <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'> 425 <table cellspacing=0 cellborder=0 style='width:100%'> 426 <tr><th colspan=3 style='padding-bottom: .25em; text-decoration: underline;'>Symbol Types To Show</th></tr> 427 <tr> 428 <td style='width: 33%; white-space: nowrap; vertical-align: top;'> 429 <span class='swatch' id='swatch_0'> </span><input checked type='checkbox' id='check_0' value='A'>Global absolute (A) 430 <br><span class='swatch' id='swatch_1'> </span><input checked type='checkbox' id='check_1' value='B'>Global uninitialized data (B) 431 <br><span class='swatch' id='swatch_2'> </span><input checked type='checkbox' id='check_2' value='b'>Local uninitialized data (b) 432 <br><span class='swatch' id='swatch_3'> </span><input checked type='checkbox' id='check_3' value='C'>Global uninitialized common (C) 433 <br><span class='swatch' id='swatch_4'> </span><input checked type='checkbox' id='check_4' value='D'>Global initialized data (D) 434 <br><span class='swatch' id='swatch_5'> </span><input checked type='checkbox' id='check_5' value='d'>Local initialized data (d) 435 <br><span class='swatch' id='swatch_6'> </span><input checked type='checkbox' id='check_6' value='G'>Global small initialized data (G) 436 <br><span class='swatch' id='swatch_7'> </span><input checked type='checkbox' id='check_7' value='g'>Local small initialized data (g) 437 <br><span class='swatch' id='swatch_8'> </span><input checked type='checkbox' id='check_8' value='i'>Indirect function (i) 438 </td> 439 <td style='width: 33%; white-space: nowrap; vertical-align: top;'> 440 <span class='swatch' id='swatch_9'> </span><input checked type='checkbox' id='check_9' value='N'>Debugging (N) 441 <br><span class='swatch' id='swatch_10'> </span><input checked type='checkbox' id='check_10' value='p'>Stack unwind (p) 442 <br><span class='swatch' id='swatch_11'> </span><input checked type='checkbox' id='check_11' value='R'>Global read-only data (R) 443 <br><span class='swatch' id='swatch_12'> </span><input checked type='checkbox' id='check_12' value='r'>Local read-only data (r) 444 <br><span class='swatch' id='swatch_13'> </span><input checked type='checkbox' id='check_13' value='S'>Global small uninitialized data (S) 445 <br><span class='swatch' id='swatch_14'> </span><input checked type='checkbox' id='check_14' value='s'>Local small uninitialized data (s) 446 <br><span class='swatch' id='swatch_15'> </span><input checked type='checkbox' id='check_15' value='T'>Global code (T) 447 <br><span class='swatch' id='swatch_16'> </span><input checked type='checkbox' id='check_16' value='t'>Local code (t) 448 <br><span class='swatch' id='swatch_17'> </span><input checked type='checkbox' id='check_17' value='U'>Undefined (U) 449 </td> 450 <td style='width: 33%; white-space: nowrap; vertical-align: top;'> 451 <span class='swatch' id='swatch_18'> </span><input checked type='checkbox' id='check_18' value='u'>Unique (u) 452 <br><span class='swatch' id='swatch_19'> </span><input checked type='checkbox' id='check_19' value='V'>Global weak object (V) 453 <br><span class='swatch' id='swatch_20'> </span><input checked type='checkbox' id='check_20' value='v'>Local weak object (v) 454 <br><span class='swatch' id='swatch_21'> </span><input checked type='checkbox' id='check_21' value='W'>Global weak symbol (W) 455 <br><span class='swatch' id='swatch_22'> </span><input checked type='checkbox' id='check_22' value='w'>Local weak symbol (w) 456 <br><span class='swatch' id='swatch_23'> </span><input checked type='checkbox' id='check_23' value='@'>Vtable entry (@) 457 <br><span class='swatch' id='swatch_24'> </span><input checked type='checkbox' id='check_24' value='-'>STABS debugging (-) 458 <br><span class='swatch' id='swatch_25'> </span><input checked type='checkbox' id='check_25' value='?'>Unrecognized (?) 459 </td> 460 </tr> 461 <tr><td colspan=3 style='text-align: center; white-space: nowrap; padding-top: 1em;'> 462 Select <input type='button' onclick='filterSetAll(true)' value='All'>, 463 <input type='button' onclick='filterSetAll(false)' value='None'>, 464 or type a string: <input id='symbol_types_filter' size=30 value='' onkeyup='symbolFilterTextChanged()' onblur='updateFilterText()'> 465 <span style='-webkit-user-select: none; cursor: help;' title='Enter codes from the list above for the symbols you want to see. The checkboxes will update automatically to match the string that you enter.'>[?]</span> 466 </td></tr> 467 </table> 468 </td></tr><tr><td style='vertical-align: top; padding-top: 10px; border-top: 1px solid grey;'> 469 <table cellspacing=0 cellborder=0 style='width: 100%'> 470 <tr><th colspan=2 style='padding-bottom: .25em; text-decoration: underline;'>Advanced Options</th></tr> 471 <tr> 472 <td style='white-space: nowrap; vertical-align: top;'> 473 <input type='checkbox' id='check_regex'> 474 Only include symbols matching this regex: 475 </td> 476 <td style='text-align: right; vertical-align: top;'> 477 <input disabled id='symbol_filter_regex' size=30 value='' style='text-align: right;'> 478 <span style='-webkit-user-select: none; cursor: help;' title='Enter a javascript regex. Only symbols that match this regex will be shown. This filter applies before any exclusion regex specified below. The format of each symbol is [path]:[symbol_name]'>[?]</span> 479 </td> 480 </tr> 481 <tr> 482 <td style='white-space: nowrap; vertical-align: top;'> 483 <input type='checkbox' id='check_exclude_regex'> 484 Exclude all symbols matching this regex: 485 </td> 486 <td style='text-align: right; vertical-align: top;'> 487 <input disabled id='symbol_filter_exclude_regex' size=30 value='' style='text-align: right;'> 488 <span style='-webkit-user-select: none; cursor: help;' title='Enter a javascript regex. Symbols that match this tegex will not be shown. This filter applies after any inclusion filter specified above. The format of each symbol is [path]:[symbol_name]'>[?]</span> 489 </td> 490 </tr> 491 <tr> 492 <td style='white-space: nowrap; vertical-align: top;'> 493 <input type='checkbox' id='check_gte'> 494 Only include symbols that are at least <span style='font-style: italic;'>n</span> bytes: 495 </td> 496 <td style='text-align: right; vertical-align: top;'> 497 <input disabled id='symbol_filter_gte' size=8 value='' style='text-align: right;'> 498 <span style='-webkit-user-select: none; cursor: help;' title='Symbols whose size is less than this value will be hidden.'>[?]</span> 499 </td> 500 </tr> 501 <tr> 502 <td style='white-space: nowrap vertical-align: top;;'> 503 Show at most <span style='font-style: italic;'>n</span> levels of detail at a time: 504 </td> 505 <td style='text-align: right; vertical-align: top;'> 506 <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> 507 </td> 508 </tr> 509 <tr> 510 <td style='white-space: nowrap vertical-align: top;;'> 511 Set the size of the treemap to <span style='font-style: italic;'>W x H</span> pixels: 512 </td> 513 <td style='text-align: right; vertical-align: top;'> 514 <input id='width' size=4 value='' style='text-align: right;'> 515 x <input id='height' size=4 value='' style='text-align: right;'> 516 </td> 517 </tr> 518 </table> 519 </td></tr> 520 <tr><td style='padding-top: 10px; text-align: right; border-top: 1px solid grey'> 521 <input type='button' value='Apply' onclick='applySettings()'> 522 <input type='button' value='Cancel' onclick='cancelSettings()'> 523 </td></tr></table> 524 </body> 525 </html> 526