Commit a055ad28 authored by Scott Graham's avatar Scott Graham

Improve sizeviewer for Windows

Now breaks down to the line level, and shows an annotated display
of the source file with the size cost of each line, as well as all
symbols that contribute to that size (can be many in the case of
templates).

Example output here:

https://dl.dropboxusercontent.com/u/116514/chrome.dll.html
https://dl.dropboxusercontent.com/u/116514/chrome_child.dll.html

Not really intended for loading remotely as they're very large,
but might eventually finish loading.

R=jam@chromium.org, mkosiba@chromium.org
BUG=441867

Review URL: https://codereview.chromium.org/801123002

Cr-Commit-Position: refs/heads/master@{#308394}
parent 1bae4cbc
...@@ -163,6 +163,10 @@ tools/symsrc/pefile.py ...@@ -163,6 +163,10 @@ tools/symsrc/pefile.py
tools/traceline/traceline/assembler.h tools/traceline/traceline/assembler.h
# Copyright Google Inc; BSD license. Tool only. # Copyright Google Inc; BSD license. Tool only.
tools/traceline/traceline/sidestep/mini_disassembler.cc tools/traceline/traceline/sidestep/mini_disassembler.cc
# Copyright Marijn Haverbeke. MIT license. Tool only, not used on Android.
tools/win/sizeviewer/clike.js
# Copyright Marijn Haverbeke. MIT license. Tool only, not used on Android.
tools/win/sizeviewer/codemirror.js
# Copyright The Chromium Authors, Apple Inc; BSD license. Not used on Android. # Copyright The Chromium Authors, Apple Inc; BSD license. Not used on Android.
ui/base/clipboard/clipboard_util_win.cc ui/base/clipboard/clipboard_util_win.cc
# Copyright The Chromium Authors, Apple Inc; BSD license. Not used on Android. # Copyright The Chromium Authors, Apple Inc; BSD license. Not used on Android.
......
code_tally.exe binary from code_tally.exe binary from
http://syzygy-archive.commondatastorage.googleapis.com/index.html?path=builds/official/1626/ http://syzygy-archive.commondatastorage.googleapis.com/index.html?path=builds/official/1626/
favicon.png is chart_pie.png unmodified from
http://www.famfamfam.com/lab/icons/silk/
codemirror.js and clike.js are 4.8.0 unmodified from http://codemirror.net/.
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineMode("clike", function(config, parserConfig) {
var indentUnit = config.indentUnit,
statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
dontAlignCalls = parserConfig.dontAlignCalls,
keywords = parserConfig.keywords || {},
builtin = parserConfig.builtin || {},
blockKeywords = parserConfig.blockKeywords || {},
atoms = parserConfig.atoms || {},
hooks = parserConfig.hooks || {},
multiLineStrings = parserConfig.multiLineStrings,
indentStatements = parserConfig.indentStatements !== false;
var isOperatorChar = /[+\-*&%=<>!?|\/]/;
var curPunc;
function tokenBase(stream, state) {
var ch = stream.next();
if (hooks[ch]) {
var result = hooks[ch](stream, state);
if (result !== false) return result;
}
if (ch == '"' || ch == "'") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
}
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
curPunc = ch;
return null;
}
if (/\d/.test(ch)) {
stream.eatWhile(/[\w\.]/);
return "number";
}
if (ch == "/") {
if (stream.eat("*")) {
state.tokenize = tokenComment;
return tokenComment(stream, state);
}
if (stream.eat("/")) {
stream.skipToEnd();
return "comment";
}
}
if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar);
return "operator";
}
stream.eatWhile(/[\w\$_\xa1-\uffff]/);
var cur = stream.current();
if (keywords.propertyIsEnumerable(cur)) {
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
return "keyword";
}
if (builtin.propertyIsEnumerable(cur)) {
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
return "builtin";
}
if (atoms.propertyIsEnumerable(cur)) return "atom";
return "variable";
}
function tokenString(quote) {
return function(stream, state) {
var escaped = false, next, end = false;
while ((next = stream.next()) != null) {
if (next == quote && !escaped) {end = true; break;}
escaped = !escaped && next == "\\";
}
if (end || !(escaped || multiLineStrings))
state.tokenize = null;
return "string";
};
}
function tokenComment(stream, state) {
var maybeEnd = false, ch;
while (ch = stream.next()) {
if (ch == "/" && maybeEnd) {
state.tokenize = null;
break;
}
maybeEnd = (ch == "*");
}
return "comment";
}
function Context(indented, column, type, align, prev) {
this.indented = indented;
this.column = column;
this.type = type;
this.align = align;
this.prev = prev;
}
function pushContext(state, col, type) {
var indent = state.indented;
if (state.context && state.context.type == "statement")
indent = state.context.indented;
return state.context = new Context(indent, col, type, null, state.context);
}
function popContext(state) {
var t = state.context.type;
if (t == ")" || t == "]" || t == "}")
state.indented = state.context.indented;
return state.context = state.context.prev;
}
// Interface
return {
startState: function(basecolumn) {
return {
tokenize: null,
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
indented: 0,
startOfLine: true
};
},
token: function(stream, state) {
var ctx = state.context;
if (stream.sol()) {
if (ctx.align == null) ctx.align = false;
state.indented = stream.indentation();
state.startOfLine = true;
}
if (stream.eatSpace()) return null;
curPunc = null;
var style = (state.tokenize || tokenBase)(stream, state);
if (style == "comment" || style == "meta") return style;
if (ctx.align == null) ctx.align = true;
if ((curPunc == ";" || curPunc == ":" || curPunc == ",") && ctx.type == "statement") popContext(state);
else if (curPunc == "{") pushContext(state, stream.column(), "}");
else if (curPunc == "[") pushContext(state, stream.column(), "]");
else if (curPunc == "(") pushContext(state, stream.column(), ")");
else if (curPunc == "}") {
while (ctx.type == "statement") ctx = popContext(state);
if (ctx.type == "}") ctx = popContext(state);
while (ctx.type == "statement") ctx = popContext(state);
}
else if (curPunc == ctx.type) popContext(state);
else if (indentStatements &&
(((ctx.type == "}" || ctx.type == "top") && curPunc != ';') ||
(ctx.type == "statement" && curPunc == "newstatement")))
pushContext(state, stream.column(), "statement");
state.startOfLine = false;
return style;
},
indent: function(state, textAfter) {
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
var closing = firstChar == ctx.type;
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
else if (ctx.align && (!dontAlignCalls || ctx.type != ")")) return ctx.column + (closing ? 0 : 1);
else if (ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
else return ctx.indented + (closing ? 0 : indentUnit);
},
electricChars: "{}",
blockCommentStart: "/*",
blockCommentEnd: "*/",
lineComment: "//",
fold: "brace"
};
});
function words(str) {
var obj = {}, words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj;
}
var cKeywords = "auto if break int case long char register continue return default short do sizeof " +
"double static else struct entry switch extern typedef float union for unsigned " +
"goto while enum void const signed volatile";
function cppHook(stream, state) {
if (!state.startOfLine) return false;
for (;;) {
if (stream.skipTo("\\")) {
stream.next();
if (stream.eol()) {
state.tokenize = cppHook;
break;
}
} else {
stream.skipToEnd();
state.tokenize = null;
break;
}
}
return "meta";
}
function cpp11StringHook(stream, state) {
stream.backUp(1);
// Raw strings.
if (stream.match(/(R|u8R|uR|UR|LR)/)) {
var match = stream.match(/"([^\s\\()]{0,16})\(/);
if (!match) {
return false;
}
state.cpp11RawStringDelim = match[1];
state.tokenize = tokenRawString;
return tokenRawString(stream, state);
}
// Unicode strings/chars.
if (stream.match(/(u8|u|U|L)/)) {
if (stream.match(/["']/, /* eat */ false)) {
return "string";
}
return false;
}
// Ignore this hook.
stream.next();
return false;
}
// C#-style strings where "" escapes a quote.
function tokenAtString(stream, state) {
var next;
while ((next = stream.next()) != null) {
if (next == '"' && !stream.eat('"')) {
state.tokenize = null;
break;
}
}
return "string";
}
// C++11 raw string literal is <prefix>"<delim>( anything )<delim>", where
// <delim> can be a string up to 16 characters long.
function tokenRawString(stream, state) {
// Escape characters that have special regex meanings.
var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&');
var match = stream.match(new RegExp(".*?\\)" + delim + '"'));
if (match)
state.tokenize = null;
else
stream.skipToEnd();
return "string";
}
function def(mimes, mode) {
if (typeof mimes == "string") mimes = [mimes];
var words = [];
function add(obj) {
if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))
words.push(prop);
}
add(mode.keywords);
add(mode.builtin);
add(mode.atoms);
if (words.length) {
mode.helperType = mimes[0];
CodeMirror.registerHelper("hintWords", mimes[0], words);
}
for (var i = 0; i < mimes.length; ++i)
CodeMirror.defineMIME(mimes[i], mode);
}
def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
name: "clike",
keywords: words(cKeywords),
blockKeywords: words("case do else for if switch while struct"),
atoms: words("null"),
hooks: {"#": cppHook},
modeProps: {fold: ["brace", "include"]}
});
def(["text/x-c++src", "text/x-c++hdr"], {
name: "clike",
keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
"static_cast typeid catch operator template typename class friend private " +
"this using const_cast inline public throw virtual delete mutable protected " +
"wchar_t alignas alignof constexpr decltype nullptr noexcept thread_local final " +
"static_assert override"),
blockKeywords: words("catch class do else finally for if struct switch try while"),
atoms: words("true false null"),
hooks: {
"#": cppHook,
"u": cpp11StringHook,
"U": cpp11StringHook,
"L": cpp11StringHook,
"R": cpp11StringHook
},
modeProps: {fold: ["brace", "include"]}
});
def("text/x-java", {
name: "clike",
keywords: words("abstract assert boolean break byte case catch char class const continue default " +
"do double else enum extends final finally float for goto if implements import " +
"instanceof int interface long native new package private protected public " +
"return short static strictfp super switch synchronized this throw throws transient " +
"try void volatile while"),
blockKeywords: words("catch class do else finally for if switch try while"),
atoms: words("true false null"),
hooks: {
"@": function(stream) {
stream.eatWhile(/[\w\$_]/);
return "meta";
}
},
modeProps: {fold: ["brace", "import"]}
});
def("text/x-csharp", {
name: "clike",
keywords: words("abstract as base break case catch checked class const continue" +
" default delegate do else enum event explicit extern finally fixed for" +
" foreach goto if implicit in interface internal is lock namespace new" +
" operator out override params private protected public readonly ref return sealed" +
" sizeof stackalloc static struct switch this throw try typeof unchecked" +
" unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
" global group into join let orderby partial remove select set value var yield"),
blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
builtin: words("Boolean Byte Char DateTime DateTimeOffset Decimal Double" +
" Guid Int16 Int32 Int64 Object SByte Single String TimeSpan UInt16 UInt32" +
" UInt64 bool byte char decimal double short int long object" +
" sbyte float string ushort uint ulong"),
atoms: words("true false null"),
hooks: {
"@": function(stream, state) {
if (stream.eat('"')) {
state.tokenize = tokenAtString;
return tokenAtString(stream, state);
}
stream.eatWhile(/[\w\$_]/);
return "meta";
}
}
});
function tokenTripleString(stream, state) {
var escaped = false;
while (!stream.eol()) {
if (!escaped && stream.match('"""')) {
state.tokenize = null;
break;
}
escaped = stream.next() != "\\" && !escaped;
}
return "string";
}
def("text/x-scala", {
name: "clike",
keywords: words(
/* scala */
"abstract case catch class def do else extends false final finally for forSome if " +
"implicit import lazy match new null object override package private protected return " +
"sealed super this throw trait try trye type val var while with yield _ : = => <- <: " +
"<% >: # @ " +
/* package scala */
"assert assume require print println printf readLine readBoolean readByte readShort " +
"readChar readInt readLong readFloat readDouble " +
"AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
"Enumeration Equiv Error Exception Fractional Function IndexedSeq Integral Iterable " +
"Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
"Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
"StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector :: #:: " +
/* package java.lang */
"Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
"Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
"Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
"StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
),
multiLineStrings: true,
blockKeywords: words("catch class do else finally for forSome if match switch try while"),
atoms: words("true false null"),
indentStatements: false,
hooks: {
"@": function(stream) {
stream.eatWhile(/[\w\$_]/);
return "meta";
},
'"': function(stream, state) {
if (!stream.match('""')) return false;
state.tokenize = tokenTripleString;
return state.tokenize(stream, state);
}
}
});
def(["x-shader/x-vertex", "x-shader/x-fragment"], {
name: "clike",
keywords: words("float int bool void " +
"vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
"mat2 mat3 mat4 " +
"sampler1D sampler2D sampler3D samplerCube " +
"sampler1DShadow sampler2DShadow" +
"const attribute uniform varying " +
"break continue discard return " +
"for while do if else struct " +
"in out inout"),
blockKeywords: words("for while do if else struct"),
builtin: words("radians degrees sin cos tan asin acos atan " +
"pow exp log exp2 sqrt inversesqrt " +
"abs sign floor ceil fract mod min max clamp mix step smootstep " +
"length distance dot cross normalize ftransform faceforward " +
"reflect refract matrixCompMult " +
"lessThan lessThanEqual greaterThan greaterThanEqual " +
"equal notEqual any all not " +
"texture1D texture1DProj texture1DLod texture1DProjLod " +
"texture2D texture2DProj texture2DLod texture2DProjLod " +
"texture3D texture3DProj texture3DLod texture3DProjLod " +
"textureCube textureCubeLod " +
"shadow1D shadow2D shadow1DProj shadow2DProj " +
"shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " +
"dFdx dFdy fwidth " +
"noise1 noise2 noise3 noise4"),
atoms: words("true false " +
"gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
"gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
"gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
"gl_FogCoord " +
"gl_Position gl_PointSize gl_ClipVertex " +
"gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
"gl_TexCoord gl_FogFragCoord " +
"gl_FragCoord gl_FrontFacing " +
"gl_FragColor gl_FragData gl_FragDepth " +
"gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
"gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
"gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
"gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " +
"gl_ProjectionMatrixInverseTranspose " +
"gl_ModelViewProjectionMatrixInverseTranspose " +
"gl_TextureMatrixInverseTranspose " +
"gl_NormalScale gl_DepthRange gl_ClipPlane " +
"gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " +
"gl_FrontLightModelProduct gl_BackLightModelProduct " +
"gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " +
"gl_FogParameters " +
"gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " +
"gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " +
"gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
"gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
"gl_MaxDrawBuffers"),
hooks: {"#": cppHook},
modeProps: {fold: ["brace", "include"]}
});
def("text/x-nesc", {
name: "clike",
keywords: words(cKeywords + "as atomic async call command component components configuration event generic " +
"implementation includes interface module new norace nx_struct nx_union post provides " +
"signal task uses abstract extends"),
blockKeywords: words("case do else for if switch while struct"),
atoms: words("null"),
hooks: {"#": cppHook},
modeProps: {fold: ["brace", "include"]}
});
def("text/x-objectivec", {
name: "clike",
keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginery BOOL Class bycopy byref id IMP in " +
"inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),
atoms: words("YES NO NULL NILL ON OFF"),
hooks: {
"@": function(stream) {
stream.eatWhile(/[\w\$]/);
return "keyword";
},
"#": cppHook
},
modeProps: {fold: "brace"}
});
});
This source diff could not be displayed because it is too large. You can view the blob instead.
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
google.load("visualization", "1", {packages:["treemap"]});
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable(g_raw_data);
tree = new google.visualization.TreeMap(
document.getElementById('chart_div'));
tree.draw(data, {
minColor: '#faa',
midColor: '#f77',
maxColor: '#f44',
headerHeight: 20,
fontColor: 'black',
showScale: true,
minColorValue: 0,
maxColorValue: g_maxval,
generateTooltip: tooltip
});
// Update from 'Loading'.
document.getElementById('title').innerText = g_dllname;
// Set favicon.
var doc_head = document.getElementsByTagName('head')[0];
var new_link = document.createElement('link');
new_link.rel = 'shortcut icon';
new_link.href = 'data:image/png;base64,'+g_favicon;
doc_head.appendChild(new_link);
var cur_line_sizes = null;
function nodeSelect() {
symlist.setValue('');
var selected = tree.getSelection();
if (selected.length > 0) {
var filename = data.getValue(selected[0].row, 0);
var size = data.getValue(selected[0].row, 2);
if (size >= 0) {
// Is a leaf.
cur_line_sizes = g_line_data[filename];
var body = g_file_contents[filename];
editor.setValue(body);
var maximum_size = 0;
for (var line in cur_line_sizes) {
maximum_size = Math.max(maximum_size, cur_line_sizes[line][0]);
}
for (var line in cur_line_sizes) {
var symbol_indices = cur_line_sizes[line][1];
var symbols = [];
for (var i = 0; i < symbol_indices.length; ++i) {
symbols.push(g_symbol_list[symbol_indices[i]]);
}
var size = cur_line_sizes[line][0];
// Zero based lines.
var line_num = parseInt(line, 10) - 1;
if (size >= maximum_size * 0.9)
editor.addLineClass(line_num, 'gutter', 'linebg-top10');
else if (size >= maximum_size * 0.75)
editor.addLineClass(line_num, 'gutter', 'linebg-top25');
else if (size >= maximum_size * 0.5)
editor.addLineClass(line_num, 'gutter', 'linebg-top50');
function addTag() {
var line_num = parseInt(line, 10);
var symbols_tooltip = symbols.join('\n');
var num_syms = symbols.length;
// markText wants 0-based lines.
var mark = editor.markText({line: line_num - 1, ch: 0},
{line: line_num, ch: 0},
{ className: 'symbol-tag' });
CodeMirror.on(mark, 'beforeCursorEnter', function(e) {
symlist.setValue(num_syms +
' symbol(s) contributing to line ' +
line_num + ':\n' +
symbols_tooltip);
});
}
addTag();
}
}
}
}
google.visualization.events.addListener(tree, 'select', nodeSelect);
editor = CodeMirror.fromTextArea(
document.getElementById('source_view'), {
readOnly: "nocursor",
mode: { name: 'text/x-c++src' },
lineNumbers: true,
lineNumberFormatter: weightGetter
});
editor.setSize(850, 600);
symlist = CodeMirror.fromTextArea(
document.getElementById('symlist_view'), {
readOnly: "nocursor",
mode: { name: 'none' },
lineNumbers: false
});
symlist.setSize(850, 150);
function tooltip(row, size, value) {
return '<div style="background:#fd9;' +
' padding:10px; border-style:solid"><b>' +
data.getValue(row, 0) + '</b><br>' +
data.getColumnLabel(2) +
' (total value of this cell and its children): ' + size +
'<br>';
}
function weightGetter(line) {
if (cur_line_sizes && cur_line_sizes.hasOwnProperty('' + line)) {
return cur_line_sizes['' + line][0] + ' bytes ' + line;
}
return line;
}
}
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
import base64
import codecs
import json import json
import os import os
import string import string
...@@ -25,12 +27,12 @@ def FindNode(node, component): ...@@ -25,12 +27,12 @@ def FindNode(node, component):
def InsertIntoTree(tree, source_name, size): def InsertIntoTree(tree, source_name, size):
components = source_name.replace(':', '').split('\\') components = source_name[3:].split('\\')
node = tree node = tree
for index, component in enumerate(components): for index, component in enumerate(components):
data = FindNode(node, component) data = FindNode(node, component)
if not data: if not data:
data = { 'name': component } data = { 'name': source_name, 'name': component }
if index == len(components) - 1: if index == len(components) - 1:
data['size'] = size data['size'] = size
else: else:
...@@ -39,6 +41,37 @@ def InsertIntoTree(tree, source_name, size): ...@@ -39,6 +41,37 @@ def InsertIntoTree(tree, source_name, size):
node = data node = data
def FlattenTree(tree):
result = [['Path', 'Parent', 'Size', 'Value']]
def Flatten(node, parent):
name = node['name']
if parent and parent != '/':
name = parent + '/' + name
if 'children' in node:
result.append([name, parent, -1, -1])
for c in node['children']:
Flatten(c, name)
else:
result.append([name, parent, node['size'], node['size']])
Flatten(tree, '')
return result
def GetAsset(filename):
with open(os.path.join(BASE_DIR, filename), 'rb') as f:
return f.read()
def AppendAsScriptBlock(f, value, var=None):
f.write('<script type="text/javascript">\n')
if var:
f.write('var ' + var + ' = ')
f.write(value)
if var:
f.write(';\n')
f.write('</script>\n')
def main(): def main():
out_dir = os.path.join(BASE_DIR, '..', '..', '..', 'out', 'Release') out_dir = os.path.join(BASE_DIR, '..', '..', '..', 'out', 'Release')
jsons = [] jsons = []
...@@ -56,37 +89,99 @@ def main(): ...@@ -56,37 +89,99 @@ def main():
print 'Couldn\'t find binaries, looking in', out_dir print 'Couldn\'t find binaries, looking in', out_dir
return 1 return 1
# Munge the code_tally json format into an easier-to-view format.
for json_name in jsons: for json_name in jsons:
with open(json_name, 'r') as jsonf: with open(json_name, 'r') as jsonf:
all_data = json.load(jsonf) all_data = json.load(jsonf)
html_path = os.path.splitext(json_name)[0] + '.html' html_path = os.path.splitext(json_name)[0] + '.html'
print 'Generating %s...' % html_path print 'Generating %s... (standlone)' % html_path
by_source = {} by_source = {}
symbols_index = {}
symbols = []
for obj_name, obj_data in all_data['objects'].iteritems(): for obj_name, obj_data in all_data['objects'].iteritems():
for symbol, symbol_data in obj_data.iteritems(): for symbol, symbol_data in obj_data.iteritems():
size = int(symbol_data['size']) size = int(symbol_data['size'])
# Sometimes there's symbols with no source file, we just ignore those. # Sometimes there's symbols with no source file, we just ignore those.
if 'contribs' in symbol_data: if 'contribs' in symbol_data:
# There may be more than one file in the list, we just assign to the i = 0
# first source file that contains the symbol, rather than try to while i < len(symbol_data['contribs']):
# split or duplicate info. src_index = symbol_data['contribs'][i]
src_index = symbol_data['contribs'][0] i += 1
source = all_data['sources'][int(src_index)] per_line = symbol_data['contribs'][i]
if source not in by_source: i += 1
by_source[source] = [] source = all_data['sources'][int(src_index)]
by_source[source].append(size) if source not in by_source:
by_source[source] = {'lines': {}, 'total_size': 0}
size = 0
# per_line is [line, size, line, size, line, size, ...]
for j in range(0, len(per_line), 2):
line_number = per_line[j]
size += per_line[j + 1]
# Save some time/space in JS by using an array here. 0 == size,
# 1 == symbol list.
by_source[source]['lines'].setdefault(line_number, [0, []])
by_source[source]['lines'][line_number][0] += per_line[j + 1]
if symbol in symbols_index:
symindex = symbols_index[symbol]
else:
symbols.append(symbol)
symbols_index[symbol] = symindex = len(symbols) - 1
by_source[source]['lines'][line_number][1].append(
symindex)
by_source[source]['total_size'] += size
binary_name = all_data['executable']['name'] binary_name = all_data['executable']['name']
data = {} data = {}
data['name'] = binary_name data['name'] = '/'
data['children'] = [] data['children'] = []
for source, sizes in by_source.iteritems(): file_contents = {}
InsertIntoTree(data, source, sum(sizes)) line_data = {}
for source, file_data in by_source.iteritems():
InsertIntoTree(data, source, file_data['total_size'])
store_as = source[3:].replace('\\', '/')
try:
with codecs.open(source, 'rb', encoding='latin1') as f:
file_contents[store_as] = f.read()
except IOError:
file_contents[store_as] = '// Unable to load source.'
line_data[store_as] = file_data['lines']
# code_tally attempts to assign fractional bytes when code is shared
# across multiple symbols. Round off here for display after summing above.
for per_line in line_data[store_as].values():
per_line[0] = round(per_line[0])
flattened = FlattenTree(data)
maxval = 0
for i in flattened[1:]:
maxval = max(i[2], maxval)
flattened_str = json.dumps(flattened)
to_write = GetAsset('template.html')
# Save all data and what would normally be external resources into the
# one html so that it's a standalone report.
with open(html_path, 'w') as f: with open(html_path, 'w') as f:
with open(os.path.join(BASE_DIR, 'template.html'), 'r') as templatef: f.write(to_write)
template = templatef.read() # These aren't subbed in as a silly workaround for 32-bit python.
f.write(string.Template(template).substitute( # The end result is only ~100M, but while substituting these into a
{'data': json.dumps(data, indent=2), # template, it otherwise raises a MemoryError, I guess due to
'dllname': binary_name + ' ' + all_data['executable']['version']})) # fragmentation. So instead, we just append them as variables to the file
# and then refer to the variables in the main script.
filedata_str = json.dumps(file_contents).replace(
'</script>', '</scr"+"ipt>')
AppendAsScriptBlock(f, filedata_str, var='g_file_contents')
AppendAsScriptBlock(f, json.dumps(line_data), var='g_line_data')
AppendAsScriptBlock(f, json.dumps(symbols), var='g_symbol_list')
favicon_str = json.dumps(base64.b64encode(GetAsset('favicon.png')))
AppendAsScriptBlock(f, favicon_str, var='g_favicon')
AppendAsScriptBlock(f, flattened_str, var='g_raw_data')
AppendAsScriptBlock(f, str(maxval), var='g_maxval')
dllname_str = binary_name + ' ' + all_data['executable']['version']
AppendAsScriptBlock(f, json.dumps(dllname_str), var='g_dllname')
AppendAsScriptBlock(f, GetAsset('codemirror.js'))
AppendAsScriptBlock(f, GetAsset('clike.js'))
AppendAsScriptBlock(f, GetAsset('main.js'))
f.write('</html>')
return 0 return 0
......
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> <script type="text/javascript" src="https://www.google.com/jsapi"></script>
<link type="text/css" rel="stylesheet" href="style.css"/>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<style type="text/css"> <style type="text/css">
/* ----------------------------------------- Slightly modified codemirror.css */
body { /* BASICS */
overflow: hidden;
margin: 0; .CodeMirror {
font-size: 14px; /* Set height, width, borders, and global font properties here */
font-family: "Helvetica Neue", Helvetica; font-family: 'Consolas', 'Monaco', 'Courier';
font-size: 12px;
height: 300px;
}
.CodeMirror-scroll {
/* Set scrolling behaviour here */
overflow: auto;
} }
#chart, #header, #footer { /* PADDING */
position: absolute;
top: 0; .CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
} }
#header, #footer { .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
z-index: 1; background-color: white; /* The little square between H and V scrollbars */
display: block;
font-size: 36px;
font-weight: 300;
text-shadow: 0 1px 0 #fff;
} }
#header.inverted, #footer.inverted { /* GUTTER */
color: #fff;
text-shadow: 0 1px 4px #000; .CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
-moz-box-sizing: content-box;
box-sizing: content-box;
} }
#header { .CodeMirror-guttermarker { color: black; }
top: 80px; .CodeMirror-guttermarker-subtle { color: #999; }
left: 140px;
width: 1000px; /* CURSOR */
.CodeMirror div.CodeMirror-cursor {
border-left: 1px solid black;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
width: auto;
border: 0;
background: #7e7;
}
.CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
} }
#footer { .cm-animate-fat-cursor {
top: 1080px; width: auto;
right: 140px; border: 0;
text-align: right; -webkit-animation: blink 1.06s steps(1) infinite;
-moz-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
} }
@-moz-keyframes blink {
0% { background: #7e7; }
50% { background: none; }
100% { background: #7e7; }
}
@-webkit-keyframes blink {
0% { background: #7e7; }
50% { background: none; }
100% { background: #7e7; }
}
@keyframes blink {
0% { background: #7e7; }
50% { background: none; }
100% { background: #7e7; }
}
/* Can style cursor different in overwrite (non-insert) mode */
div.CodeMirror-overwrite div.CodeMirror-cursor {}
rect { .cm-tab { display: inline-block; text-decoration: inherit; }
fill: none;
pointer-events: all; .CodeMirror-ruler {
border-left: 1px solid #ccc;
position: absolute;
} }
pre { /* DEFAULT THEME */
font-size: 18px;
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable,
.cm-s-default .cm-punctuation,
.cm-s-default .cm-property,
.cm-s-default .cm-operator {}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}
.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}
/* Default styles for common addons */
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
.CodeMirror-activeline-background {background: #e8f2ff;}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
line-height: 1;
position: relative;
overflow: hidden;
background: white;
color: black;
} }
line { .CodeMirror-scroll {
stroke: #000; /* 30px is the magic margin used to hide the element's real scrollbars */
stroke-width: 1.5px; /* See overflow: hidden in .CodeMirror */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
-moz-box-sizing: content-box;
box-sizing: content-box;
} }
.string, .regexp { /* The fake, visible scrollbars. Used to force redraw during scrolling
color: #f39; before actuall scrolling happens, thus preventing shaking and
flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
}
.CodeMirror-gutter-filler {
left: 0; bottom: 0;
} }
.keyword { .CodeMirror-gutters {
color: #00c; position: absolute; left: 0; top: 0;
padding-bottom: 30px;
z-index: 3;
}
.CodeMirror-gutter {
white-space: normal;
height: 100%;
-moz-box-sizing: content-box;
box-sizing: content-box;
padding-bottom: 30px;
margin-bottom: -32px;
display: inline-block;
/* Hack to make IE7 behave */
*zoom:1;
*display:inline;
}
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
height: 100%;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
} }
.comment { .CodeMirror-lines {
color: #777; cursor: text;
font-style: oblique; min-height: 1px; /* prevents collapsing before first draw */
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
} }
.number { .CodeMirror-linebackground {
color: #369; position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 0;
} }
.class, .special { .CodeMirror-linewidget {
color: #1181B8; position: relative;
z-index: 2;
overflow: auto;
} }
a:link, a:visited { .CodeMirror-widget {}
color: #000;
text-decoration: none; .CodeMirror-wrap .CodeMirror-scroll {
overflow-x: hidden;
} }
a:hover { .CodeMirror-measure {
color: #666; position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
} }
.CodeMirror-measure pre { position: static; }
.hint { .CodeMirror div.CodeMirror-cursor {
position: absolute; position: absolute;
right: 0; border-right: none;
width: 1280px; width: 0;
font-size: 12px; }
color: #999;
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
} }
.chart { .CodeMirror-focused div.CodeMirror-cursors {
display: block; visibility: visible;
margin: auto;
margin-top: 60px;
font-size: 11px;
} }
rect { .CodeMirror-selected { background: #d9d9d9; }
stroke: #eee; .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
fill: #aaa; .CodeMirror-crosshair { cursor: crosshair; }
fill-opacity: .8;
.cm-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
} }
rect.parent { /* IE7 hack to prevent it from returning funny offsetTops on the spans */
cursor: pointer; .CodeMirror span { *vertical-align: text-bottom; }
fill: steelblue;
/* Used to force a border model for a node */
.cm-force-border { padding-right: .1px; }
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
} }
text { /* See issue #2901 */
pointer-events: none; .cm-tab-wrap-hack:after { content: ''; }
/* Help users use markselection to safely style text background */
span.CodeMirror-selectedtext { background: none; }
/* ---------------------------------- End of slightly modified codemirror.css */
body {
font-family: "Helvetica Neue", Helvetica, Arial;
} }
#outer {
width: 1500px;
height: 800px;
display: flex;
}
.CodeMirror {
border: 1px solid black;
margin-top: 3px;
}
.CodeMirror-linenumbers {
width: 150px;
}
.linebg-top10 { background: #f88; }
.linebg-top25 { background: #fbb; }
.linebg-top50 { background: #fee; }
.symbol-tag { background: #eee; }
.charts-tooltip { z-index: 1000; }
</style> </style>
</head> </head>
<body> <body>
<div id="body"> <h1 id='title'>Loading...</h1>
<div id="footer"> <div id="outer">
$dllname <div id="tree>">
<button onclick="tree.goUpAndDraw()">Go up</button>
<div id="chart_div" style="width: 600px; height: 750px;">
</div>
</div> </div>
</div> <div id=source">
<script type="text/javascript"> <textarea id="source_view" style="width: 850px; height: 600px;">
/*
var w = 1120,
h = 1000,
x = d3.scale.linear().range([0, w]),
y = d3.scale.linear().range([0, h]);
var vis = d3.select("#body").append("div")
.attr("class", "chart")
.style("width", w + "px")
.style("height", h + "px")
.append("svg:svg")
.attr("width", w)
.attr("height", h);
var partition = d3.layout.partition()
.value(function(d) { return d.size; });
(function(root) {
var g = vis.selectAll("g")
.data(partition.nodes(root))
.enter().append("svg:g")
.attr("transform", function(d) { return "translate(" + x(d.y) + "," + y(d.x) + ")"; })
.on("click", click);
var kx = w / root.dx,
ky = h / 1;
g.append("svg:rect")
.attr("width", root.dy * kx)
.attr("height", function(d) { return d.dx * ky; })
.attr("class", function(d) { return d.children ? "parent" : "child"; });
g.append("svg:text")
.attr("transform", transform)
.attr("dy", ".35em")
.style("opacity", function(d) { return d.dx * ky > 12 ? 1 : 0; })
.text(function(d) { return d.name; })
d3.select(window)
.on("click", function() { click(root); })
function click(d) {
if (!d.children) return;
kx = (d.y ? w - 40 : w) / (1 - d.y);
ky = h / d.dx;
x.domain([d.y, 1]).range([d.y ? 40 : 0, w]);
y.domain([d.x, d.x + d.dx]);
var t = g.transition()
.duration(d3.event.altKey ? 7500 : 750)
.attr("transform", function(d) { return "translate(" + x(d.y) + "," + y(d.x) + ")"; });
t.select("rect")
.attr("width", d.dy * kx)
.attr("height", function(d) { return d.dx * ky; });
t.select("text")
.attr("transform", transform)
.style("opacity", function(d) { return d.dx * ky > 12 ? 1 : 0; });
d3.event.stopPropagation();
}
function transform(d) { Drill down on the left to an interesting file to see a line-by-line breakdown.
return "translate(8," + d.dx * ky / 2 + ")";
} Largest lines are annotated here in darkest red.
})($data);
</script> Click on a line with bytes assigned to see contributing symbols below
*/
</textarea>
<textarea id="symlist_view" style="width: 850px; height: 100px;">
</textarea>
</div>
</body> </body>
</html> <!--
Various things appended: codemirror.js, clike.js, various g_xxx variables ending
with main.js, followed by the closing html tag.
-->
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment