[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