[LNT] r267536 - [profile] Add CFG view to profile page.

Sean Silva via llvm-commits llvm-commits at lists.llvm.org
Tue May 10 00:26:31 PDT 2016


On Tue, May 3, 2016 at 11:46 PM, Kristof Beyls via llvm-commits <
llvm-commits at lists.llvm.org> wrote:

> I fully agree. I was hoping to find some time in the next couple of weeks
> to look up what the popular unit testing frameworks are for javascript and
> see which ones could be used for testing LNT's javascript.
> I have no experience whatsoever in javascript unit testing frameworks, so
> I'm all ears for recommendations or pitfalls to look out for.
>

I've used tape and mocha in the node ecosystem. I think that `tape` (
https://github.com/substack/tape) is specifically meant for browsers, but
I'm not sure how hard it is to integrate it without browserify.

I'm by no means an expert in this stuff.

-- Sean Silva


>
> Just thinking out loud:
> My current guess is that the hardest part is that most javascript unit
> testing frameworks probably run in a browser.
> Is it even possible to test HTML DOM-related functionality when not
> running in a browser?
> I'm hoping that it's still possible somehow to trigger unit
> test/regression test runs from the command line, and get results on the
> command line, even if in the background a browser is invoked to run the
> tests.
>
> Thanks,
>
> Kristof
>
> > On 3 May 2016, at 19:26, Chris Matthews <chris.matthews at apple.com>
> wrote:
> >
> > I think we need to settle on how we are going to test LNT’s javascript!
> >
> > I don’t even have a suggestion for this, does anyone else?
> >
> >> On Apr 26, 2016, at 2:55 AM, Kristof Beyls via llvm-commits <
> llvm-commits at lists.llvm.org> wrote:
> >>
> >> Author: kbeyls
> >> Date: Tue Apr 26 04:55:06 2016
> >> New Revision: 267536
> >>
> >> URL: http://llvm.org/viewvc/llvm-project?rev=267536&view=rev
> >> Log:
> >> [profile] Add CFG view to profile page.
> >>
> >> This implements the mockup presented on slide 32 of
> >>
> http://llvm.org/devmtg/2015-10/slides/Beyls-AutomatedPerformanceTrackingOfLlvmGeneratedCode.pdf
> >>
> >> The control flow graph is constructed by scanning instruction
> >> disassembly, looking for instructions that change control flow. This is
> >> done by matching instruction text with regular expressions. This isn't
> >> perfect, but can reconstruct CFGs well in most cases.  Furthermore, for
> >> this visualization tool, it isn't required that the CFG will always be
> >> reconstructed 100% correctly.
> >>
> >> This patch only implements regular expressions to reconstruct the
> >> Control Flow Graph for the AArch64 instruction set. It doesn't implement
> >> all necessary regular expressions for AArch64. Nonetheless, the CFG is
> >> already reconstructed well in surprisingly many cases.
> >>
> >>
> >> Modified:
> >>   lnt/trunk/lnt/server/ui/static/lnt_profile.css
> >>   lnt/trunk/lnt/server/ui/static/lnt_profile.js
> >>   lnt/trunk/lnt/server/ui/templates/v4_profile.html
> >>
> >> Modified: lnt/trunk/lnt/server/ui/static/lnt_profile.css
> >> URL:
> http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/static/lnt_profile.css?rev=267536&r1=267535&r2=267536&view=diff
> >>
> ==============================================================================
> >> --- lnt/trunk/lnt/server/ui/static/lnt_profile.css (original)
> >> +++ lnt/trunk/lnt/server/ui/static/lnt_profile.css Tue Apr 26 04:55:06
> 2016
> >> @@ -124,3 +124,43 @@
> >> .address a:hover {
> >>    text-decoration: none;
> >> }
> >> +
> >>
> +/***********************************************************************/
> >> +/* CFG view of the profiles themselves */
> >> +.instruction {
> >> +    fill: black;
> >> +    font-family: sans-serif;
> >> +    font-size: 10px;
> >> +}
> >> +.basicblockweight {
> >> +    fill: black;
> >> +    font-family: sans-serif;
> >> +    font-size: 14px;
> >> +}
> >> +.instructiontext {
> >> +}
> >> +.instructionaddress {
> >> +    font-family: monospace;
> >> +    font-size: 10px;
> >> +}
> >> +.instructionweight {
> >> +}
> >> +.basicblock {
> >> +    fill: #F2F3F4;
> >> +    stroke: black;
> >> +}
> >> +.basicblocksidebar {
> >> +    fill: #F2F3F4;
> >> +    stroke: none;
> >> +}
> >> +.edge {
> >> +    stroke-width: 1px;
> >> +    stroke: black;
> >> +    fill: none;
> >> +}
> >> +.backedge {
> >> +    stroke-width: 1px;
> >> +    stroke: #D35400;
> >> +    fill: none;
> >> +}
> >> +
> >>
> >> Modified: lnt/trunk/lnt/server/ui/static/lnt_profile.js
> >> URL:
> http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/static/lnt_profile.js?rev=267536&r1=267535&r2=267536&view=diff
> >>
> ==============================================================================
> >> --- lnt/trunk/lnt/server/ui/static/lnt_profile.js (original)
> >> +++ lnt/trunk/lnt/server/ui/static/lnt_profile.js Tue Apr 26 04:55:06
> 2016
> >> @@ -3,6 +3,410 @@
> >> // (v4_profile.html).
> >>
> >>
> >> +function CFGInstruction (weight, address, text) {
> >> +    this.address = address;
> >> +    this.text = text;
> >> +    this.weight = weight;
> >> +};
> >> +
> >> +function CFGBasicBlock (instructions, cfg) {
> >> +    this.cfg = cfg;
> >> +    this.instructionParser = this.cfg.instructionParser;
> >> +    this.instructions = instructions;
> >> +    this.address = instructions[0].address;
> >> +    var gjt = this.instructionParser
> >> +        .getJumpTargets(instructions[instructions.length-1], this.cfg);
> >> +    this.targets = gjt[1];
> >> +    this.noFallThru = gjt[0];
> >> +    this.weight = this.instructions
> >> +        .reduce(function (a,b) {
> >> +            var weight_a = (a.weight === undefined)?a:a.weight;
> >> +            var weight_b = (b.weight === undefined)?b:b.weight;
> >> +            return weight_a+weight_b;
> >> +        }, 0);
> >> +};
> >> +
> >> +function CFGEdge (bb_from, bb_to) {
> >> +    this.from = bb_from;
> >> +    this.to = bb_to;
> >> +};
> >> +
> >> +function CFG (disassembly, instructionParser) {
> >> +    this.disassembly = disassembly;
> >> +    this.instructionParser = instructionParser;
> >> +    var instructions = this.parseDisassembly(this.disassembly);
> >> +    this.bbs = this.createBasicBlocks(instructions);
> >> +    // The special "UNKNOWN" basic block is used to represent jump
> targets for
> >> +    // which no corresponding basic block can be found. So, edges
> jumping to a
> >> +    // target that has no associated basic block will go to this
> "UNKNOWN"
> >> +    // basic block.
> >> +    this.UNKNOWN_BB = this.createUnknownBB();
> >> +    this.bbs.push(this.UNKNOWN_BB);
> >> +    this.address2bb = {};
> >> +    this.bb_incoming_edges = {};
> >> +    this.bb_outgoing_edges = {};
> >> +    this.edges = [];
> >> +    for (var i=0; i<this.bbs.length; ++i) {
> >> +        var bb = this.bbs[i];
> >> +        this.address2bb[bb.address] = bb;
> >> +    }
> >> +    var address2bb = this.address2bb;
> >> +    for (var i=0; i<this.bbs.length; ++i) {
> >> +        var bb = this.bbs[i];
> >> +        var target_bbs = bb.targets.map(function (a) { return
> address2bb[a]; });
> >> +        if (target_bbs.length == 0 &&
> >> +            !bb.noFallThru) {
> >> +            // this is a fall-thru-only BB.
> >> +            if (i+1<this.bbs.length)
> >> +                target_bbs = [this.bbs[i+1]];
> >> +        }
> >> +        for (var j=0; j<target_bbs.length; ++j) {
> >> +            var target_bb = target_bbs[j];
> >> +            // Jump to special "unknown bb" for jumps to an unknown
> basic block.
> >> +            if (!target_bb)
> >> +                target_bb = this.UNKNOWN_BB;
> >> +            var edge = new CFGEdge(this.bbs[i], target_bb);
> >> +            this.edges.push(edge);
> >> +        }
> >> +    }
> >> +};
> >> +
> >> +function AArch64InstructionSetParser () {
> >> +};
> >> +
> >> +AArch64InstructionSetParser.prototype = {
> >> +    getNextInstructionAddress: function(instruction) {
> >> +        // AArch64: all instructions are 4 bytes:
> >> +        return instruction.address + 4;
> >> +    },
> >> +
> >> +    AArch64JumpTargetRegexps: [
> >> +        // (regexp, noFallThru?)
> >> +        // branch conditional:
> >> +        [new RegExp("^\\s*b\\.[a-z]+\\s+([^\\s]+)"), false],
> >> +        // branch unconditional:
> >> +        [new RegExp("^\\s*b\\s+([^\\s]+)"), true],
> >> +        // cb(n)z
> >> +        [new RegExp("^\\s*cbn?z\\s+[^,]+,\\s*([^\\s]+)"), false],
> >> +        // ret
> >> +        [new RegExp("^\\s*ret"), true]
> >> +        // FIXME: also add tbz, ...
> >> +    ],
> >> +
> >> +    getJumpTargets: function(instruction, cfg) {
> >> +        for(var i=0; i<this.AArch64JumpTargetRegexps.length; ++i) {
> >> +            var regexp = this.AArch64JumpTargetRegexps[i][0];
> >> +            var noFallThru = this.AArch64JumpTargetRegexps[i][1];
> >> +            var match = instruction.text.match(regexp);
> >> +            if (match) {
> >> +                var targets = [];
> >> +                if (!noFallThru)
> >> +
> targets.push(this.getNextInstructionAddress(instruction));
> >> +                if (match.length > 1)
> >> +                    targets.push(cfg.convertToAddress(match[1]));
> >> +                return [noFallThru, targets];
> >> +            }
> >> +        }
> >> +        return [false, []];
> >> +    },
> >> +};
> >> +
> >> +CFG.prototype = {
> >> +    // The following method will have different implementations
> depending
> >> +    // on the profiler, or kind of profiling input.
> >> +    convertToAddress: function (addressString) {
> >> +      return parseInt(addressString, 16);
> >> +    },
> >> +
> >> +    parseDisassembly: function() {
> >> +        var instructions = [];
> >> +        for (var i=0; i<this.disassembly.length; ++i) {
> >> +            var profiled_instruction = this.disassembly[i];
> >> +            var counter2weight = profiled_instruction[0];
> >> +            var address = profiled_instruction[1];
> >> +            var text = profiled_instruction[2];
> >> +            // FIXME: strip leading white space from text?
> >> +            var cfgInstruction =
> >> +                new CFGInstruction(counter2weight['cycles'],
> >> +                                   address,
> >> +                                   text);
> >> +            instructions.push(cfgInstruction);
> >> +        }
> >> +        return instructions;
> >> +    },
> >> +
> >> +    createBasicBlocks: function(instructions) {
> >> +        var bbstarts = {};
> >> +        for (var i=0; i<instructions.length; ++i) {
> >> +            var instruction = instructions[i];
> >> +            var gjt = this.instructionParser
> >> +                .getJumpTargets(instruction, this);
> >> +            var jumpTargets=gjt[1], noFallThru=gjt[0];
> >> +            if (jumpTargets.length > 0) {
> >> +                for(var j=0; j<jumpTargets.length; ++j) {
> >> +                    var jumpTarget = jumpTargets[j];
> >> +                    bbstarts[jumpTarget] = true;
> >> +                }
> >> +                bbstarts[this.instructionParser
> >> +                         .getNextInstructionAddress(instruction)] =
> true;
> >> +            }
> >> +        }
> >> +        // start creating basic blocks now:
> >> +        var bbs = [];
> >> +        var instructionsInCurrentBB = [];
> >> +        for (var i=0; i<instructions.length; ++i) {
> >> +            var instruction = instructions[i];
> >> +            if (bbstarts[instruction.address] && i > 0) {
> >> +                bbs.push(new CFGBasicBlock(instructionsInCurrentBB,
> this));
> >> +                instructionsInCurrentBB = [];
> >> +            }
> >> +            instructionsInCurrentBB.push(instruction);
> >> +        }
> >> +        bbs.push(new CFGBasicBlock(instructionsInCurrentBB,
> >> +                                   this));
> >> +        return bbs;
> >> +    },
> >> +
> >> +    createUnknownBB: function() {
> >> +        var i = new CFGInstruction(0.0 /* weight */,
> >> +                                   "" /* address */,
> >> +                                   "UNKNOWN");
> >> +        return new CFGBasicBlock([i], this);
> >> +    }
> >> +};
> >> +
> >> +
> >> +function D3CFG (cfg) {
> >> +    this.cfg = cfg;
> >> +    this.d3bbs = [];
> >> +    this.bb2d3bb = new Map();
> >> +    this.d3edges = [];
> >> +    this.d3bbTopSlots = [];
> >> +    this.d3bbBotSlots = [];
> >> +    this.d3vlanes = [];
> >> +    this.vlane_occupancies = [];
> >> +    for (var bbi=0; bbi<this.cfg.bbs.length; ++bbi) {
> >> +        var bb = this.cfg.bbs[bbi];
> >> +        var d3bb = new D3BB(bb, bbi);
> >> +        this.d3bbs.push(d3bb);
> >> +        this.bb2d3bb.set(bb, d3bb);
> >> +    }
> >> +    for (var ei=0; ei<this.cfg.edges.length; ++ei) {
> >> +        var e = this.cfg.edges[ei];
> >> +        this.d3edges.push(new D3Edge(e, this));
> >> +    }
> >> +};
> >> +
> >> +D3CFG.prototype = {
> >> +    compute_layout: function() {
> >> +        var offset = 0;
> >> +        for(var i=0; i<this.d3bbs.length; ++i) {
> >> +            var d3bb = this.d3bbs[i];
> >> +            d3bb.compute_layout(this);
> >> +            d3bb.y = d3bb.y + offset;
> >> +            offset += d3bb.height + this.bbgap;
> >> +        }
> >> +        for(var i=0; i<this.d3edges.length; ++i) {
> >> +            var d3e = this.d3edges[i];
> >> +            d3e.compute_layout_part1(this);
> >> +        }
> >> +        // heuristic: layout shorter edges first, so they remain
> closer to
> >> +        // the basic blocks.
> >> +        this.d3edges.sort(function (e1,e2) {
> >> +            var e1_length = Math.abs(e1.start_bb_index -
> e1.end_bb_index);
> >> +            var e2_length = Math.abs(e2.start_bb_index -
> e2.end_bb_index);
> >> +            return e1_length - e2_length;
> >> +        });
> >> +        for(var i=0; i<this.d3edges.length; ++i) {
> >> +            var d3e = this.d3edges[i];
> >> +            d3e.compute_layout_part2(this);
> >> +        }
> >> +        this.height = offset;
> >> +        this.vlane_width = this.vlane_gap *
> this.vlane_occupancies.length;
> >> +        this.vlane_offset = Math.max.apply(Math,
> >> +            this.d3bbs.map(function(i){return i.width;})) +
> this.vlane_gap;
> >> +        this.width = this.vlane_offset + this.vlane_width;
> >> +    },
> >> +
> >> +    // layout parameters:
> >> +    vlane_gap: 10,
> >> +    bbgap: 15,
> >> +    instructionTextSize: 10,
> >> +    avgCharacterWidth: 6,
> >> +    bb_weight_width: 20,
> >> +    x_offset_instruction_text: 100+20/*bb_weight_width*/,
> >> +    slot_gap: 0
> >> +};
> >> +
> >> +function D3BB (bb, index) {
> >> +    this.bb = bb;
> >> +    this.index = index;
> >> +    this.d3instructions = [];
> >> +    this.free_top_slot = 0;
> >> +    this.free_bottom_slot = 0;
> >> +    for (var i=0; i<this.bb.instructions.length; ++i) {
> >> +        var instr = this.bb.instructions[i];
> >> +        this.d3instructions.push(new D3Instruction(instr));
> >> +    }
> >> +};
> >> +
> >> +D3BB.prototype = {
> >> +    compute_layout: function(d3cfg) {
> >> +        var offset = 0;
> >> +        this.d3instructions.forEach(function(d3i) {
> >> +            d3i.compute_layout(d3cfg);
> >> +            d3i.y = d3i.y + offset;
> >> +            offset += d3i.height + 1;
> >> +        });
> >> +        this.x = 0;
> >> +        this.y = 0;
> >> +        this.height = offset;
> >> +        this.width = Math.max.apply(Math,
> >> +            this.d3instructions.map(function(i){return i.width;}))
> >> +                                  + d3cfg.x_offset_instruction_text;
> >> +    },
> >> +
> >> +    reserve_bottom_slot: function(d3cfg) {
> >> +        var offset = this.free_bottom_slot++;
> >> +        y_coord = this.y + this.height - (d3cfg.slot_gap*offset);
> >> +        return y_coord;
> >> +    },
> >> +    reserve_top_slot: function(d3cfg) {
> >> +        var offset = this.free_top_slot++;
> >> +        y_coord = this.y + (d3cfg.slot_gap*offset);
> >> +        return y_coord;
> >> +    },
> >> +};
> >> +
> >> +function D3Edge (edge, d3cfg) {
> >> +    this.edge = edge;
> >> +    this.d3cfg = d3cfg;
> >> +};
> >> +
> >> +D3Edge.prototype = {
> >> +    compute_layout_part1: function (d3cfg) {
> >> +        var bb_from = this.edge.from;
> >> +        var bb_to = this.edge.to;
> >> +        d3bb_from = this.d3cfg.bb2d3bb.get(bb_from);
> >> +        d3bb_to = this.d3cfg.bb2d3bb.get(bb_to);
> >> +        this.downward = d3bb_from.y < d3bb_to.y;
> >> +        if (this.downward) {
> >> +            this.start_bb_index = d3bb_from.index;
> >> +            this.end_bb_index = d3bb_to.index;
> >> +        } else {
> >> +            this.start_bb_index = d3bb_to.index;
> >> +            this.end_bb_index = d3bb_from.index;
> >> +        }
> >> +        this.fallthru = this.start_bb_index+1 == this.end_bb_index;
> >> +        if (this.fallthru) {
> >> +            this.start_y = d3bb_from.y + d3bb_from.height;
> >> +            this.end_y = d3bb_to.y;
> >> +            var x = Math.min(d3bb_from.width/2, d3bb_to.width/2);
> >> +            this.start_x = this.end_x = x;
> >> +            return;
> >> +        }
> >> +        this.start_y = d3bb_from.reserve_bottom_slot(d3cfg);
> >> +        this.end_y = d3bb_to.reserve_top_slot(d3cfg);
> >> +        this.start_x = d3bb_from.width;
> >> +        this.end_x = d3bb_to.width;
> >> +    },
> >> +
> >> +    reserve_lane: function (lanenr) {
> >> +        var vlane_occupancy = this.d3cfg.vlane_occupancies[lanenr];
> >> +        if (this.downward) {
> >> +            vlane_occupancy[this.start_bb_index].bottom = this;
> >> +        } else {
> >> +            vlane_occupancy[this.start_bb_index].top = this;
> >> +        }
> >> +        for(var i=this.start_bb_index+1; i<this.end_bb_index; ++i) {
> >> +            vlane_occupancy[i].top = this;
> >> +            vlane_occupancy[i].bottom = this;
> >> +        }
> >> +        if (this.downward) {
> >> +            vlane_occupancy[this.end_bb_index].top = this;
> >> +        } else {
> >> +            vlane_occupancy[this.end_bb_index].bottom = this;
> >> +        }
> >> +        this.vlane = lanenr;
> >> +    },
> >> +
> >> +    compute_layout_part2: function() {
> >> +        // special case for fall-thru: just draw a direct line.
> >> +        // reserve an edge connection slot at the top bb and the end bb
> >> +        // look for first vertical lane that's free across all basic
> blocks
> >> +        // this edge will cross, and reserve that.
> >> +        if (this.fall_thru)
> >> +            return;
> >> +        var available=false;
> >> +        iterate_vlanes_loop:
> >> +        for(var j=0; j<this.d3cfg.vlane_occupancies.length; ++j) {
> >> +            var vlane_occupancy = this.d3cfg.vlane_occupancies[j];
> >> +            available=true;
> >> +            if (this.downward) {
> >> +                if (vlane_occupancy[this.start_bb_index].bottom) {
> >> +                    available=false;
> >> +                    continue iterate_vlanes_loop;
> >> +                }
> >> +            } else {
> >> +                if (vlane_occupancy[this.start_bb_index].top) {
> >> +                    available=false;
> >> +                    continue iterate_vlanes_loop;
> >> +                }
> >> +            }
> >> +            for(var i=this.start_bb_index+1; i<this.end_bb_index; ++i)
> {
> >> +                if (vlane_occupancy[i].top ||
> vlane_occupancy[i].bottom) {
> >> +                    // this vlane slot is already taken - continue
> looking
> >> +                    // in next vlane.
> >> +                    available = false;
> >> +                    continue iterate_vlanes_loop;
> >> +                }
> >> +            }
> >> +            if (this.downward) {
> >> +                if (vlane_occupancy[this.end_bb_index].top) {
> >> +                    available=false;
> >> +                    continue iterate_vlanes_loop;
> >> +                }
> >> +            } else {
> >> +                if (vlane_occupancy[this.end_bb_index].bottom) {
> >> +                    available=false;
> >> +                    continue iterate_vlanes_loop;
> >> +                }
> >> +            }
> >> +            // lane j is available for this edge:
> >> +            if (available) {
> >> +                this.reserve_lane(j);
> >> +                this.vlane=j;
> >> +                break iterate_vlanes_loop;
> >> +            }
> >> +        }
> >> +        if (!available) {
> >> +            // no vlane found, create a new one.
> >> +            this.d3cfg.vlane_occupancies.push(
> >> +                Array.apply(null,
> >> +                    Array(this.d3cfg.d3bbs.length)).map(function () {
> >> +                        o = new Object;
> >> +                        o.top = null;
> >> +                        o.bottom = null;
> >> +                        return o;
> >> +                    }));
> >> +            this.reserve_lane(this.d3cfg.vlane_occupancies.length-1);
> >> +        }
> >> +    }
> >> +};
> >> +
> >> +function D3Instruction (cfgInstruction) {
> >> +    this.instruction = cfgInstruction;
> >> +};
> >> +
> >> +D3Instruction.prototype = {
> >> +    compute_layout: function(d3cfg) {
> >> +        this.x = 0;
> >> +        this.y = 0;
> >> +        this.height = d3cfg.instructionTextSize;
> >> +        this.width =
> d3cfg.avgCharacterWidth*this.instruction.text.length;
> >> +    }
> >> +}
> >> +
> >> function Profile(element, runid, testid, unique_id) {
> >>    this.element = $(element);
> >>    this.runid = runid;
> >> @@ -19,17 +423,19 @@ Profile.prototype = {
> >>        $(this.element).html('<center><i>Select a run and function
> above<br> ' +
> >>                             'to view a performance
> profile</i></center>');
> >>    },
> >> -    go: function(function_name, counter_name, displayType,
> total_ctr_for_fn) {
> >> +    go: function(function_name, counter_name, displayType,
> counterDisplayType,
> >> +                 total_ctr_for_fn) {
> >>        this.counter_name = counter_name
> >>        this.displayType = displayType;
> >> +        this.counterDisplayType = counterDisplayType;
> >>        this.total_ctr = total_ctr_for_fn;
> >>        if (this.function_name != function_name)
> >> -            this._fetch_and_go(function_name);
> >> +            this._fetch_and_display(function_name);
> >>        else
> >> -            this._go();
> >> +            this._display();
> >>    },
> >>
> >> -    _fetch_and_go: function(fname, then) {
> >> +    _fetch_and_display: function(fname, then) {
> >>        this.function_name = fname;
> >>        var this_ = this;
> >>        $.ajax(g_urls.getCodeForFunction, {
> >> @@ -38,7 +444,7 @@ Profile.prototype = {
> >>                   'f': fname},
> >>            success: function(data) {
> >>                this_.data = data;
> >> -                this_._go();
> >> +                this_._display();
> >>            },
> >>            error: function(xhr, textStatus, errorThrown) {
> >>                pf_flash_error('accessing URL ' +
> g_urls.getCodeForFunction +
> >> @@ -47,10 +453,147 @@ Profile.prototype = {
> >>        });
> >>    },
> >>
> >> -    _go: function() {
> >> +    _display: function() {
> >> +        try {
> >> +            if (this.displayType != 'cfg')
> >> +                this._display_straightline();
> >> +            else
> >> +                this._display_cfg();
> >> +        }
> >> +        catch (err) {
> >> +            $(this.element).html(
> >> +                '<center><i>The javascript on this page to<br> ' +
> >> +                'analyze and visualize the profile has crashed:<br>'+
> >> +                +err.message+'</i></center>'+
> >> +                err.stack);
> >> +        }
> >> +    },
> >> +
> >> +    _display_cfg: function() {
> >> +        this.element.empty();
> >> +        var profiledDisassembly = this.data;
> >> +        var instructionParser = new AArch64InstructionSetParser();
> >> +        var cfg = new CFG(profiledDisassembly, instructionParser);
> >> +        var d3cfg = new D3CFG(cfg);
> >> +        d3cfg.compute_layout();
> >> +        var d3data = [d3cfg];
> >> +        var d3cfg_dom = d3.select(this.element.get(0))
> >> +            .selectAll("svg")
> >> +            .data(d3data)
> >> +            .enter().append("svg");
> >> +        var profile = this;
> >> +
> >> +        // add circle and arrow marker definitions
> >> +        var svg_defs = d3cfg_dom.append("defs");
> >> +        svg_defs.append("marker")
> >> +            .attr("id", "markerCircle")
> >> +            .attr("markerWidth", "7").attr("markerHeight", "7")
> >> +            .attr("refX", "3").attr("refY", "3")
> >> +            .append("circle")
> >> +            .attr("cx","3").attr("cy", "3").attr("r","3")
> >> +            .attr("style","stroke: none; fill: #000000;");
> >> +        svg_defs.append("marker")
> >> +            .attr("id", "markerArrow")
> >> +            .attr("markerWidth", "11").attr("markerHeight", "7")
> >> +            .attr("refX", "10").attr("refY", "3")
> >> +            .attr("orient", "auto")
> >> +            .append("path")
> >> +            .attr("d","M0,0 L0,6 L10,3 L0,0")
> >> +            .attr("style","fill: #000000;");
> >> +        d3cfg_dom
> >> +            .attr('width', d3data[0].width)
> >> +            .attr('height', d3data[0].height);
> >> +        var d3bbs_dom = d3cfg_dom.selectAll("g .bbgroup")
> >> +            .data(function (d,i) { return d.d3bbs; })
> >> +            .enter().append("g").attr('class', 'bbgroup');
> >> +        d3bbs_dom
> >> +            .attr("transform", function (d) {
> >> +              return "translate(" + d.x + "," + d.y + ")"});
> >> +        d3bbs_dom.append("rect")
> >> +            .attr('class', 'basicblock')
> >> +            .attr("x", function(d) { return d3cfg.bb_weight_width; })
> >> +            .attr("y", function (d) { return 0; })
> >> +            .attr("height", function (d) { return d.height; })
> >> +            .attr("width", function (d) {
> >> +                return d.width-d3cfg.bb_weight_width; });
> >> +        // draw weight of basic block in a rectangle on the left.
> >> +        d3bbs_dom.append("rect")
> >> +            .attr('class', 'basicblocksidebar')
> >> +            .attr("x", function(d) { return 0; })
> >> +            .attr("y", function (d) { return 0; })
> >> +            .attr("height", function (d) { return d.height; })
> >> +            .attr("width", function (d) { return
> d3cfg.bb_weight_width; })
> >> +            .attr("style", function (d) {
> >> +                var lData = profile._label(d.bb.weight, true
> /*littleSpace*/);
> >> +                return "fill: "+lData.background_color;
> >> +            });
> >> +        d3bbs_dom.append("g")
> >> +            .attr('transform', function (d) {
> >> +                return "translate("
> >> +                    +(d.x+d3cfg.bb_weight_width-3)
> >> +                    +","
> >> +                    +(d.height/2)
> >> +                    +")"; })
> >> +            .append("text")
> >> +            .attr('class', 'basicblockweight')
> >> +            .attr("transform", "rotate(-90)")
> >> +            .attr("text-anchor", "middle")
> >> +            .text(function (d,i) {
> >> +                var lData = profile._label(d.bb.weight, true
> /*littleSpace*/);
> >> +                return lData.text; //d.bb.weight.toFixed(0)+"%";
> >> +            });
> >> +        var d3inst_dom = d3bbs_dom.selectAll("text:root")
> >> +            .data(function (d,i) { return d.d3instructions; })
> >> +            .enter();
> >> +        // draw disassembly text
> >> +        d3inst_dom.append("text")
> >> +            .attr('class', 'instruction instructiontext')
> >> +            .attr("x", function (d,i) { return
> d.x+d3cfg.x_offset_instruction_text; })
> >> +            .attr("y", function (d,i) { return d.y+10; })
> >> +            .text(function (d,i) { return d.instruction.text; });
> >> +        // draw address of instruction
> >> +        d3inst_dom.append("text")
> >> +            .attr('class', 'instruction instructionaddress')
> >> +            .attr("x", function (d,i) { return
> d.x+d3cfg.bb_weight_width+50; })
> >> +            .attr("y", function (d,i) { return d.y+10; })
> >> +            .text(function (d,i) { return
> d.instruction.address.toString(16); });
> >> +        // draw profile weight of instruction
> >> +        d3inst_dom.append("text")
> >> +            .attr('class', 'instruction instructionweight')
> >> +            .attr("x", function (d,i) { return
> d.x+d3cfg.bb_weight_width+0; })
> >> +            .attr("y", function (d,i) { return d.y+10; })
> >> +            .text(function (d,i) {
> >> +                if (d.instruction.weight == 0)
> >> +                    return "";
> >> +                else
> >> +                    return d.instruction.weight.toFixed(2)+"%";
> >> +                });
> >> +        var d3edges_dom = d3cfg_dom.selectAll("g .edgegroup")
> >> +            .data(function (d,i) { return d.d3edges; })
> >> +            .enter().append("g").attr('class', 'edgegroup');
> >> +        d3edges_dom.append("polyline")
> >> +            .attr("points", function (d,i) {
> >> +                if (d.fallthru) {
> >> +                    return d.start_x+","+d.start_y+" "+
> >> +                           d.end_x+","+d.end_y;
> >> +                }
> >> +                var lane_x = d.d3cfg.vlane_offset +
> d.d3cfg.vlane_gap*d.vlane;
> >> +                return d.start_x+","+d.start_y+" "+
> >> +                       lane_x+","+d.start_y+" "+
> >> +                       lane_x+","+d.end_y+" "+
> >> +                       d.end_x+","+d.end_y; })
> >> +            .attr('class', function (d) {
> >> +                return d.downward?'edge':'backedge';
> >> +            })
> >> +            .attr('style',
> >> +                  'marker-start: url(#markerCircle); '+
> >> +                  'marker-end: url(#markerArrow);');
> >> +    },
> >> +
> >> +    _display_straightline: function() {
> >>        this.element.empty();
> >>        this.cumulative = 0;
> >> -        for (i in this.data) {
> >> +        for (var i=0; i<this.data.length; ++i) {
> >>            line = this.data[i];
> >>
> >>            row = $('<tr></tr>');
> >> @@ -70,7 +613,7 @@ Profile.prototype = {
> >>        }
> >>    },
> >>
> >> -    _labelTd: function(value) {
> >> +    _label: function(value, littleSpace) {
> >>        var this_ = this;
> >>        var labelPct = function(value) {
> >>            // Colour scheme: Black up until 1%, then yellow fading to
> red at 10%
> >> @@ -84,21 +627,33 @@ Profile.prototype = {
> >>                bg = 'hsl(0, 100%, 50%)';
> >>                hl = 'hsl(0, 100%, 30%)';
> >>            }
> >> -            return $('<td style="background-color:' + bg + ';
> border-right: 1px solid ' + hl + ';"></td>')
> >> -                .text(value.toFixed(2) + '%');
> >> +            return {
> >> +              background_color: bg,
> >> +              border_right_color: hl,
> >> +              text: (value.toFixed(littleSpace?0:2) + '%')
> >> +            };
> >>        };
> >>
> >>        var labelAbs = function(value) {
> >> -            var hue = lerp(50.0, 0.0, value / 100.0);
> >> -            var bg = 'hsl(' + hue.toFixed(0) + ', 100%, 50%)';
> >> -            var hl = 'hsl(' + hue.toFixed(0) + ', 100%, 30%)';
> >> +            // Colour scheme: Black up until 1%, then yellow fading to
> red at 10%
> >> +            var bg = '#fff';
> >> +            var hl = '#fff';
> >> +            if (value > 1.0 && value < 10.0) {
> >> +                hue = lerp(50.0, 0.0, (value - 1.0) / 9.0);
> >> +                bg = 'hsl(' + hue.toFixed(0) + ', 100%, 50%)';
> >> +                hl = 'hsl(' + hue.toFixed(0) + ', 100%, 30%)';
> >> +            } else if (value >= 10.0) {
> >> +                bg = 'hsl(0, 100%, 50%)';
> >> +                hl = 'hsl(0, 100%, 30%)';
> >> +            }
> >>
> >>            var absVal = (value / 100.0) * this_.total_ctr;
> >> -            return $('<td style="background-color:' + bg + ';
> border-right: 1px solid ' + hl + ';"></td>')
> >> -                .text(currencyify(absVal))
> >> -                .append($('<span></span>')
> >> -                        .text(value.toFixed(2) + '%')
> >> -                        .hide());
> >> +            return {
> >> +              background_color: bg,
> >> +              border_right_color: hl,
> >> +              text: (currencyify(absVal)),
> >> +              absVal: absVal
> >> +            };
> >>        };
> >>
> >>        var labelCumAbs = function(value) {
> >> @@ -109,9 +664,42 @@ Profile.prototype = {
> >>            var hl = 'hsl(' + hue.toFixed(0) + ', 100%, 40%)';
> >>
> >>            var absVal = (this_.cumulative / 100.0) * this_.total_ctr;
> >> +            return {
> >> +              background_color: bg,
> >> +              border_right_color: hl,
> >> +              text: (currencyify(absVal)),
> >> +              cumulative: this_.cumulative,
> >> +              absVal: absVal
> >> +            };
> >> +        }
> >> +
> >> +        if (this.counterDisplayType == 'cumulative')
> >> +            return labelCumAbs(value);
> >> +        else if (this.counterDisplayType == 'absolute')
> >> +            return labelAbs(value);
> >> +        else
> >> +            return labelPct(value);
> >> +    },
> >> +
> >> +    _labelTd: function(value) {
> >> +        var this_ = this;
> >> +        var labelPct = function(value, bg, hl, text) {
> >> +            return $('<td style="background-color:' + bg + ';
> border-right: 1px solid ' + hl + ';"></td>')
> >> +                .text(text);
> >> +        };
> >> +
> >> +        var labelAbs = function(value, bg, hl, text, absVal) {
> >> +            return $('<td style="background-color:' + bg + ';
> border-right: 1px solid ' + hl + ';"></td>')
> >> +                .text(currencyify(absVal))
> >> +                .append($('<span></span>')
> >> +                        .text(value.toFixed(2) + '%')
> >> +                        .hide());
> >> +        };
> >> +
> >> +        var labelCumAbs = function(value, bg, hl, text, cumulative) {
> >>            return $('<td style="border-right: 1px solid ' + hl +
> ';"></td>')
> >>                .css({color: 'gray', position: 'relative'})
> >> -                .text(currencyify(absVal))
> >> +                .text(text)
> >>                .append($('<span></span>')
> >>                        .text(value.toFixed(2) + '%')
> >>                        .hide())
> >> @@ -120,19 +708,31 @@ Profile.prototype = {
> >>                            position: 'absolute',
> >>                            bottom: '0px',
> >>                            left: '0px',
> >> -                            width: this_.cumulative + '%',
> >> +                            width: cumulative + '%',
> >>                            height: '2px',
> >>                            border: '1px solid ' + hl,
> >>                            backgroundColor: bg
> >>                        }));
> >>        }
> >>
> >> -        if (this.displayType == 'cumulative')
> >> -            return labelCumAbs(value);
> >> -        else if (this.displayType == 'absolute')
> >> -            return labelAbs(value);
> >> +        var lData = this._label(value, false /*littleSpace*/);
> >> +        if (this.counterDisplayType == 'cumulative')
> >> +            return labelCumAbs(value,
> >> +                               lData.background_color,
> >> +                               lData.border_right_color,
> >> +                               lData.text,
> >> +                               lData.cumulative);
> >> +        else if (this.counterDisplayType == 'absolute')
> >> +            return labelAbs(value,
> >> +                            lData.background_color,
> >> +                            lData.border_right_color,
> >> +                            lData.text,
> >> +                            lData.absVal);
> >>        else
> >> -            return labelPct(value);
> >> +            return labelPct(value,
> >> +                            lData.background_color,
> >> +                            lData.border_right_color,
> >> +                            lData.text);
> >>    },
> >> };
> >>
> >> @@ -311,8 +911,12 @@ function ToolBar(element) {
> >>    });
> >>
> >>    var this_ = this;
> >> -    element.find('.next-btn-l').click(function()
> {this_._findNextInstruction(this_, false);});
> >> -    element.find('.prev-btn-l').click(function()
> {this_._findNextInstruction(this_, true);});
> >> +    element.find('.next-btn-l').click(function() {
> >> +      this_._findNextInstruction(this_, false);
> >> +    });
> >> +    element.find('.prev-btn-l').click(function() {
> >> +      this_._findNextInstruction(this_, true);
> >> +    });
> >> }
> >>
> >> ToolBar.prototype = {
> >> @@ -531,9 +1135,9 @@ FunctionTypeahead.prototype = {
> >>
> >> $(document).ready(function () {
> >>    jQuery.fn.extend({
> >> -        profile: function(arg1, arg2, arg3, arg4, arg5) {
> >> +        profile: function(arg1, arg2, arg3, arg4, arg5, arg6) {
> >>            if (arg1 == 'go')
> >> -                this.data('profile').go(arg2, arg3, arg4, arg5);
> >> +                this.data('profile').go(arg2, arg3, arg4, arg5, arg6);
> >>            else if (arg1 && !arg2)
> >>                this.data('profile',
> >>                          new Profile(this,
> >> @@ -602,6 +1206,7 @@ function pf_init(run1, run2, testid, url
> >>                var ctr_value =
> $('#stats').statsBar().getCounterValue(pf_get_counter())[0];
> >>                $('#profile1').profile('go', fname,
> >>                                       pf_get_counter(),
> pf_get_display_type(),
> >> +                                       pf_get_counter_display_type(),
> >>                                       fn_percentage * ctr_value);
> >>            },
> >>            sourceRunUpdated: function(data) {
> >> @@ -636,6 +1241,7 @@ function pf_init(run1, run2, testid, url
> >>                var ctr_value =
> $('#stats').statsBar().getCounterValue(pf_get_counter())[1];
> >>                $('#profile2').profile('go', fname,
> >>                                       pf_get_counter(),
> pf_get_display_type(),
> >> +                                       pf_get_counter_display_type(),
> >>                                       fn_percentage * ctr_value);
> >>            },
> >>            sourceRunUpdated: function(data) {
> >> @@ -702,18 +1308,22 @@ function pf_init(run1, run2, testid, url
> >>
> >>    // Bind change events for the counter dropdown so that profiles are
> >>    // updated when it is modified.
> >> -    $('#counters, #absolute').change(function () {
> >> +    $('#view, #counters, #absolute').change(function () {
> >>        g_counter = $('#counters').val();
> >>        if ($('#fn1_box').val()) {
> >>            var fn_percentage =
> $('#fn1_box').functionTypeahead().getFunctionPercentage(fname) / 100.0;
> >>            var ctr_value =
> $('#stats').statsBar().getCounterValue(pf_get_counter())[0];
> >> -            $('#profile1').profile('go', $('#fn1_box').val(),
> g_counter, pf_get_display_type(),
> >> +            $('#profile1').profile('go', $('#fn1_box').val(),
> g_counter,
> >> +                                   pf_get_display_type(),
> >> +                                   pf_get_counter_display_type(),
> >>                                   fn_percentage * ctr_value);
> >>        }
> >>        if ($('#fn2_box').val()) {
> >>            var fn_percentage =
> $('#fn2_box').functionTypeahead().getFunctionPercentage(fname) / 100.0;
> >>            var ctr_value =
> $('#stats').statsBar().getCounterValue(pf_get_counter())[1];
> >> -            $('#profile2').profile('go', $('#fn2_box').val(),
> g_counter, pf_get_display_type(),
> >> +            $('#profile2').profile('go', $('#fn2_box').val(),
> g_counter,
> >> +                                   pf_get_display_type(),
> >> +                                   pf_get_counter_display_type(),
> >>                                   fn_percentage * ctr_value);
> >>        }
> >>    });
> >> @@ -822,11 +1432,17 @@ function pf_get_counter() {
> >>    return g_counter;
> >> }
> >>
> >> -// pf_get_display_type - Whether we should display absolute values or
> percentages.
> >> -function pf_get_display_type() {
> >> +// pf_get_counter_display_type - Whether we should display absolute
> values or percentages.
> >> +function pf_get_counter_display_type() {
> >>    return $('#absolute').val();
> >> }
> >>
> >> +// pf_get_display_type - Whether we should display straight-line
> profiles
> >> +// or control flow graphs.
> >> +function pf_get_display_type() {
> >> +    return $('#view').val();
> >> +}
> >> +
> >> // pf_update_history - Push a new history entry, as we've just navigated
> >> // to what could be a new bookmarkable page.
> >> function pf_update_history() {
> >>
> >> Modified: lnt/trunk/lnt/server/ui/templates/v4_profile.html
> >> URL:
> http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/templates/v4_profile.html?rev=267536&r1=267535&r2=267536&view=diff
> >>
> ==============================================================================
> >> --- lnt/trunk/lnt/server/ui/templates/v4_profile.html (original)
> >> +++ lnt/trunk/lnt/server/ui/templates/v4_profile.html Tue Apr 26
> 04:55:06 2016
> >> @@ -20,6 +20,9 @@
> >>  <script language="javascript" type="text/javascript"
> >>          src="{{ url_for('.static',
> >>
>  filename='jquery/jquery.scrolltofixed/jquery-scrolltofixed.js') }}">
> </script>
> >> +  <script language="javascript" type="text/javascript"
> >> +          src="{{ url_for('.static',
> >> +               filename='d3/d3.min.js') }}"> </script>
> >>  <link rel="stylesheet" href="{{ url_for('.static',
> >>                               filename='lnt_profile.css') }}">
> >>  <script>
> >> @@ -83,21 +86,27 @@
> >>
> >>  <div class="anchor"></div>
> >>  <div class="row toolbarrow" id="toolbar">
> >> -
> >> -        <div class="span4">
> >> -          <b>Hottest instructions:</b>
> >> -          <button class="btn btn-small prev-btn-l"><i
> class="icon-backward"></i> Prev</button>
> >> -          <button class="btn btn-small next-btn-l">Next <i
> class="icon-forward"></i></button>
> >> -        </div>
> >> -        <div class="span6">
> >> -          <b>Counter:</b>
> >> -          <select id="counters" class="input-medium"></select>
> >> -          <select class="input-medium" id="absolute">
> >> -            <option value="relative">Relative (%)</option>
> >> -            <option value="absolute">Absolute numbers</option>
> >> -            <option value="cumulative">Cumulative absolute
> numbers</option>
> >> -          </select>
> >> -        </div>
> >> +    <div class="span4">
> >> +      <b>View:</b>
> >> +      <select class="input-medium" id="view">
> >> +        <option value="cfg">Control-Flow Graph</option>
> >> +        <option value="straight">Straight</option>
> >> +      </select>
> >> +    </div>
> >> +    <div class="span4">
> >> +      <b>Hottest instructions:</b>
> >> +      <button class="btn btn-small prev-btn-l"><i
> class="icon-backward"></i> Prev</button>
> >> +      <button class="btn btn-small next-btn-l">Next <i
> class="icon-forward"></i></button>
> >> +    </div>
> >> +    <div class="span4">
> >> +      <b>Counter:</b>
> >> +      <select id="counters" class="input-medium"></select>
> >> +      <select class="input-medium" id="absolute">
> >> +        <option value="relative">Relative (%)</option>
> >> +        <option value="absolute">Absolute numbers</option>
> >> +        <option value="cumulative">Cumulative absolute numbers</option>
> >> +      </select>
> >> +    </div>
> >>  </div>
> >>
> >>  <div class="row">
> >>
> >>
> >> _______________________________________________
> >> llvm-commits mailing list
> >> llvm-commits at lists.llvm.org
> >> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
> >
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20160510/fb45519b/attachment.html>


More information about the llvm-commits mailing list