サイトマップ

JavaScript Profiler/Tracer --- JavaScript

resources/template/analyze.css

analyze.css
body {
	margin: 0;
	padding: 0;
}
pre.line {
	margin:0 0 0 0px;
	min-height:1em;
	font-size: 11pt;
}
pre.line.hilit {
	background: yellow;
}
#content-profile {
	height: 100%;
}
#content-trace,
#content-source {
	display: none;
	height: 400px;
}
#content {
    font-family: monospace;
}
.trace {
    margin-left:20px !important;
    font-family: monospace;
    font-size: 11pt;
}
.trace.open > a,
.trace.close > a {
    text-decoration: none;
    cursor: pointer;
}
.trace.open > a.icon:before {
    content: "+ ";
}
.trace.close > a.icon:before {
    content: "- ";
}
.trace.open > div.trace {
    display: none;
}
.trace.close > div.trace {
    display: default;
}

resources/templateanalyze.html

analyze.html
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>jstools analyzer</title>
    <link rel="stylesheet" type="text/css" href="analyze.css">
    <link rel="stylesheet" type="text/css" href="lib/w2ui.css">
    <script src="lib/jquery.js"></script>
    <script src="lib/w2ui.js"></script>
    <script type="text/javascript" src="function_map.js"></script>
    <script type="text/javascript" src="analyze.js"></script>
  </head>
  <body>
    <div id="layout" style="height:400px; overflow:hidden;"></div>
  </body>
</html>

resources/template/analyze.js

analyze.js
$(function() {
    var currFunc;
    var summary = [];
 
    function initProfile() {
        var summary = {};
        var records = [];
 
        function calcSummary(data) {
            var sum = summary[data.id];
            if (data.id in summary) {
                sum.count++;
                sum.elapsed += data.elapsed;
                sum.effective += data.effective;
            } else {
                sum = summary[data.id] = {
                        id: data.id,
                        fileName: functionMap.files[functionMap.functions[data.id].fileId],
                        name: functionMap.functions[data.id].name,
                        count: 1,
                        elapsed: data.elapsed,
                        effective: data.effective
                    };
            }
            for (var i = 0; i < data.children.length; i++) {
                calcSummary(data.children[i]);
            }
        }
        function calcEffective(data) {
            data.effective = data.elapsed;
            for (var i = 0; i < data.children.length; i++) {
                var child = data.children[i];
                data.effective -= child.elapsed;
                calcEffective(child);
            }
        }
 
        calcEffective(root);
        calcSummary(root);
 
        for (var key in summary) {
            var rec = summary[key];
            records.push({
               recid: rec.id,
               name: esc(rec.name),
               count: rec.count,
               elapsed: rec.elapsed,
               effective: rec.effective
            });
        }
        records.sort(function(a, b) {
            return a.effective < b.effective ? 1 :
                (a.effective > b.effective ? -1 : 0);
        });
 
        $('#content-profile').w2grid({
            name: 'profile',
            show: {
                toolbar: false,
                footer: true
            },
            columns: [
                { field: 'name', caption: 'name', size: '100%', sortable: true },
                { field: 'count', caption: 'count', size: '80px', sortable: true, render:'int' },
                { field: 'elapsed', caption: 'elapsed', size: '150px', sortable: true, render:'float:3' },
                { field: 'effective', caption: 'effective', size: '150px', sortable: true, render:'float:3' }
            ],
            onClick: function(evt) {
                if (evt.column === 0 && evt.recid != 0) {
                    currFunc = evt.recid;
                    w2ui.layout.panels[0].tabs.enable('tab-source');
                    w2ui.layout.panels[0].tabs.click('tab-source');
                }
            },
            sortData: [
                { "field": "effective", "direction": "DESC" }
            ],
            records: records
        });
    }
 
    $('#layout').w2layout({
        name    : 'layout',
        panels  : [
            {
                type: 'main', 
                content:
                    '<div id="content-profile"></div>' +
                    '<div id="content-trace"></div>' +
                    '<div id="content-source"></div>',
                tabs: {
                    active: 'tab-profile',
                    tabs: [
                        { id: 'tab-profile', caption: 'profile' },
                        { id: 'tab-trace', caption: 'trace' },
                        { id: 'tab-source', caption: 'source', disabled: true }
                    ],
                    onClick: function (id, data) {
                        switch(id) {
                        case 'tab-profile':
                            $('#content-profile').css('display', 'block');
                            $('#content-trace').css('display', 'none');
                            $('#content-source').css('display', 'none');
                            break;
                        case 'tab-trace':
                            var o = $(w2ui.layout.el('main'));
                            $('#content-profile').css('display', 'none');
                            $('#content-trace').css('display', 'block');
                            $('#content-source').css('display', 'none');
                            break;
                        case 'tab-source':
                            $('#content-profile').css('display', 'none');
                            $('#content-trace').css('display', 'none');
                            $('#content-source').css('display', 'block');
                            showFunction(currFunc);
                            break;
                        }
                    }
                }
            }
        ]
    });
    function initTrace() {
        function createDom(data) {
            var f = functionMap.functions[data.id];
            var node = document.createElement('div');
            node.id = 'trace-' + data.id;
            node.className = 'trace close';
            var icon = node.appendChild(document.createElement('a'));
            icon.className = 'icon';
            icon.onclick = toggle;
            var file = functionMap.files[f.fileId];
            var name = node.appendChild(document.createElement('a'));
            name.appendChild(document.createTextNode(functionMap.functions[data.id].name + '(' + data.args.join(', ') + ')'));
            if (file) {
                name.title = file + ':' + f.row + ':' + f.col;
                name.onclick = showSource;
            }
            for (var i = 0; i < data.children.length; i++) {
                node.appendChild(createDom(data.children[i]));
            }
            return node;
        }
        function toggle(evt) {
            var target = $(evt.target.parentNode);
            if (target.hasClass('open')) {
                target.removeClass('open');
                target.addClass('close');
            } else {
                target.removeClass('close');
                target.addClass('open');
            }
        }
 
        $('#content-trace').append(createDom(root));
    }
 
    var root = JSON.parse(localStorage.getItem("jstools.profile"));
    initProfile();
    initTrace();
 
    function onResize() {
        var height = $(window).height();
        $('#layout').css('height', height + 'px');
    }
 
    function esc(text) {
        if (!text) return text;
        return text
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;');
    }
    function showFunction(id) {
        function hilit(id) {
            $('pre.line.hilit').removeClass('hilit');
            var obj = $('#line-' + id).addClass('hilit');
            var offsetParent = obj[0].offsetParent;
            var offsetTop = obj[0].offsetTop;
            offsetParent.scrollTop = offsetTop - offsetParent.offsetHeight / 2;
        }
        var f = functionMap.functions[id];
        var fileName = 'sources/' + functionMap.files[f.fileId] + '.html';
 
        if ($('#content-source').attr("title") != fileName) {
            $.ajax({
                type: "GET",
                url: fileName,
                dataType: "html",
                success: function(html){
                    $("#content-source").html('');
                    $("#content-source").append(html);
                    $("#content-source").attr('title', fileName);
                    hilit(f.row);
                }
            });
        } else {
            hilit(f.row);
        }
        currFunc = id;
    }
    function showSource(evt) {
        currFunc = evt.target.parentNode.id.replace(/^trace\-/, '');
        w2ui.layout.panels[0].tabs.enable('tab-source');
        w2ui.layout.panels[0].tabs.click('tab-source');
    }
 
    onResize();
    $(window).resize(onResize);
});

resources/template/jstools.js

jstools.js
if (!("__jstools__" in window)) {
    window.__jstools__ = (function() {
        var running = true;
        var controller;
        var root = new node(0, null);
        var curr = root;
        var handlers = {
                'enter': [],
                'exit': []
        }
        var now = "performance" in window ?
                function() { return performance.now(); } :
                function() { return (new Date()).getTime(); };
 
        function node(id, parent) {
            this.id = id;
            this.parent = parent;
            this.args = [];
            this.children = [];
            this.elapsed = -1;
        }
        node.prototype.setArgs = function(args) {
            this.args = [];
            if (!args) {
                return;
            }
            for (var i = 0; i < args.length; i++) {
                var arg = args[i];
                if (typeof arg == 'string') arg = "'" + arg + "'";
                else if (typeof arg == 'object') arg = 'object';
                this.args.push(arg);
            }
        };
        node.prototype.addChild = function(child) {
            this.children.push(child);
        };
        node.prototype.startTimer = function() {
            this.stime = now();
        };
        node.prototype.stopTimer = function() {
            this.elapsed = now() - this.stime;
            delete this.stime;
        };
        node.prototype.clear = function() {
            for (var i = 0; i < this.children.length; i++) {
                this.children[i].clear();
            }
            this.children = [];
        };
 
        function start() {
            running = true;
            controller.style.background = "red";
        }
        function stop() {
            running = false;
            controller.style.background = "lightgreen";
            report();
            clear();
        }
        function clear() {
            root.clear();
            root = new node(0, null);
            curr = root;
            //localStorage.removeItem('jstools.profile');
        }
        function enter(num, name) {
            if (!running) return;
            var args = arguments.callee.caller.arguments;
            var child = new node(num, curr);
            child.setArgs(args);
            child.startTimer();
            curr.addChild(child);
            curr = child;
            for (var i = 0; i < handlers['enter'].length; i++) {
                handlers['enter'][i](num, name);
            }
        }
        function exit(retval) {
            if (!running) return retval;
            curr.stopTimer();
            curr = curr.parent;
            for (var i = 0; i < handlers['exit'].length; i++) {
                handlers['exit'][i](retval);
            }
            return retval;
        }
        function report() {
            localStorage.setItem('jstools.profile', JSON.stringify(tree()));
            clear();
            window.open(".jstools/analyze.html", "jstools");
        }
        function tree() {
            function dig(node) {
                var result = { id:node.id, elapsed:node.elapsed, args:node.args, children:[]};
                for (var i = 0; i < node.children.length; i++) {
                    result.children.push(dig(node.children[i]));
                }
                return result;
            }
            return dig(root);
        }
        function createControl() {
            var icon = controller = document.createElement("div");
            icon.style.position = "fixed";
            icon.style.width = "10px";
            icon.style.height = "10px";
            icon.style.top = "10px";
            icon.style.right = "10px";
            icon.style.background = "red";
            icon.style.zIndex = 999999;
            icon.onclick = function(evt) {
                running ? stop() : start();
            };
            document.body.appendChild(icon);
        }
        function addHandler(name, fn) {
            handlers[name].push(fn);
        }
 
        window.addEventListener('load', createControl, false);
        return {
            'enter':  enter,
            'exit':   exit,
            'addHandler': addHandler
        }
    })();
}

resources/template/profile.css

profile.css
div#nav {
    text-align: right;
}

resources/template/profile.html

profile.html
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>profile</title>
    <link rel="stylesheet" type="text/css" href="profile.css">
    <script type="text/javascript" src="function_map.js"></script>
    <script type="text/javascript" src="profile.js"></script>
  </head>
  <body>
    <div id="nav"><a href="trace.html">trace</a></div>
    <div id="content"></div>
  </body>
</html>

resources/template/profile.js

profile.js
(function() {
    var summary = {};
    function esc(text) {
        if (!text) return text;
        return text
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;');
    }
    function calc(data) {
        var sum = summary[data.id];
        if (data.id in summary) {
            sum.count++;
            sum.elapsed += data.elapsed;
        } else {
            sum = summary[data.id] = {
                    id: data.id,
                    fileName: functionMap.files[functionMap.functions[data.id].fileId],
                    name: functionMap.functions[data.id].name,
                    count: 1,
                    elapsed: data.elapsed,
                    effective: data.elapsed
                };
        }
        for (var i = 0; i < data.children.length; i++) {
            calc(data.children[i]);
        }
        for (var i = 0; i < data.children.length; i++) {
            sum.effective -= data.children[i].elapsed;
        }
    }
    function show() {
        var array = [];
        for (var key in summary) {
            array.push(summary[key]);
        }
        array.sort(function(a, b) {
            return a.elapsed > b.elapsed ? -1 : (a.elapsed < b.elapsed ? 1 : 0);
        });
        var html = '<table>';
        for (var i = 0; i < array.length; i++) {
            var v = array[i];
            var h = '<tr><td><a target="source" href="sources/' +
                v.fileName + '.html#line-' + functionMap.functions[v.id].row +
                '">' + esc(v.name) + '</a></td><td>' + v.count + '</td><td>' +
                v.elapsed + '</td><td>' + v.effective + '</td></tr>';
            html += h;
        }
        html += '</table>';
        document.getElementById('content').innerHTML = html;
    }
    function showSource(evt) {
        var p = evt.target.title.split(/:/);
        window.open("sources/" + p[0] + ".html#line-" + p[1], "source");
    }
    function hasClass(node, name) {
        if (!node.className) {
            return false;
        } else {
            return (' ' + node.className + ' ').includes(' ' + name + ' ');
        }
    }
    function removeClass(node, name) {
        if (hasClass(node, name)) {
            var parts = node.className.trim().split(/ +/);
            parts.splice(parts.indexOf(name), 1);
            node.className = parts.join(' ');
        }
    }
    function addClass(node, name) {
        if (!hasClass(node, name)) {
            node.className = node.className ?
                    node.className + ' ' + name :
                    name;
        }
    }
    window.addEventListener('load', function() {
        var root = JSON.parse(localStorage.getItem("jstools.profile"));
        calc(root);
        show();
    });
})();

resources/template/trace.css

trace.css
div#nav {
    text-align: right;
}
#content {
	font-family: monospace;
}
.trace {
	margin-left:20px;
}
.trace.open > a,
.trace.close > a {
	text-decoration: none;
	cursor: pointer;
}
.trace.open > a.icon:before {
	content: "+ ";
}
.trace.close > a.icon:before {
	content: "- ";
}
.trace.open > div.trace {
	display: none;
}
.trace.close > div.trace {
	display: default;
}

resources/template/trace.html

trace.html
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>trace</title>
    <link rel="stylesheet" type="text/css" href="trace.css">
    <script type="text/javascript" src="function_map.js"></script>
    <script type="text/javascript" src="trace.js"></script>
  </head>
  <body>
    <div id="nav"><a href="profile.html">profile</a></div>
    <div id="content"></div>
  </body>
</html>

resources/template/trace.js

trace.js
(function() {
    function esc(text) {
        if (!text) return text;
        return text
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;');
    }
    function createDom(data) {
        var f = functionMap.functions[data.id];
        var node = document.createElement('div');
        node.id = 'trace-' + data.id;
        node.className = 'trace close';
        var icon = node.appendChild(document.createElement('a'));
        icon.className = 'icon';
        icon.onclick = toggle;
        var file = functionMap.files[f.fileId];
        var name = node.appendChild(document.createElement('a'));
        name.appendChild(document.createTextNode(functionMap.functions[data.id].name + '(' + data.args.join(', ') + ')'));
        if (file) {
            name.title = file + ':' + f.row + ':' + f.col;
            name.onclick = showSource;
        }
        for (var i = 0; i < data.children.length; i++) {
            node.appendChild(createDom(data.children[i]));
        }
        return node;
    }
    function toggle(evt) {
        var target = evt.target.parentNode;
        if (hasClass(target, 'open')) {
            removeClass(target, 'open');
            addClass(target, 'close');
        } else {
            removeClass(target, 'close');
            addClass(target, 'open');
        }
    }
    function showSource(evt) {
        var p = evt.target.title.split(/:/);
        window.open("sources/" + p[0] + ".html#line-" + p[1], "source");
    }
    function hasClass(node, name) {
        if (!node.className) {
            return false;
        } else {
            return (' ' + node.className + ' ').includes(' ' + name + ' ');
        }
    }
    function removeClass(node, name) {
        if (hasClass(node, name)) {
            var parts = node.className.trim().split(/ +/);
            parts.splice(parts.indexOf(name), 1);
            node.className = parts.join(' ');
        }
    }
    function addClass(node, name) {
        if (!hasClass(node, name)) {
            node.className = node.className ?
                    node.className + ' ' + name :
                    name;
        }
    }
    window.addEventListener('load', function() {
        var root = JSON.parse(localStorage.getItem("jstools.profile"));
        document.getElementById('content').appendChild(createDom(root));
    });
})();
 
research/20260419163656.txt · 最終更新: 2026/04/19 16:45 by Kazuyuki Matsuda
特に明示されていない限り、本サイトの内容は次のライセンスに従います:Copyright(C) 2011 Shorindo, Inc. All Rights Reserved
Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki