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

Chris Matthews via llvm-commits llvm-commits at lists.llvm.org
Tue May 3 10:26:18 PDT 2016


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



More information about the llvm-commits mailing list