1# 2# Copyright (C) 2016 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16 17import sys 18 19SVG_CANVAS_WIDTH = 1124 20SVG_NODE_HEIGHT = 17 21FONT_SIZE = 12 22 23 24def hash_to_float(string): 25 return hash(string) / float(sys.maxint) 26 27def getLegacyColor(method) : 28 r = 175 + int(50 * hash_to_float(reversed(method))) 29 g = 60 + int(180 * hash_to_float(method)) 30 b = 60 +int(55 * hash_to_float(reversed(method))) 31 return (r,g,b) 32 33 34def getDSOColor(method) : 35 r = 170 + int(80 * hash_to_float(reversed(method))) 36 g = 180 +int(70 * hash_to_float((method))) 37 b = 170 + int(80 * hash_to_float(reversed(method))) 38 return (r,g,b) 39 40 41def getHeatColor(callsite, num_samples) : 42 r = 245 + 10* (1- float(callsite.num_samples)/ num_samples) 43 g = 110 + 105* (1-float(callsite.num_samples)/ num_samples) 44 b = 100 45 return (r,g,b) 46 47 48def createSVGNode(callsite, depth, f, num_samples, height, color_scheme, nav): 49 x = float(callsite.offset)/float(num_samples)*SVG_CANVAS_WIDTH 50 y = height - (depth * SVG_NODE_HEIGHT) - SVG_NODE_HEIGHT 51 width = float(callsite.num_samples) /float(num_samples) * SVG_CANVAS_WIDTH 52 53 method = callsite.method.replace(">", ">").replace("<", "<") 54 if (width <= 0) : 55 return 56 57 if color_scheme == "dso": 58 r, g, b = getDSOColor(callsite.dso) 59 elif color_scheme == "legacy": 60 r, g, b = getLegacyColor(method) 61 else: 62 r, g, b = getHeatColor(callsite, num_samples) 63 64 65 66 r_border = (r - 50) 67 if r_border < 0: 68 r_border = 0 69 70 g_border = (g - 50) 71 if g_border < 0: 72 g_border = 0 73 74 b_border = (b - 50) 75 if (b_border < 0): 76 b_border = 0 77 78 f.write( 79 '<g id=%d class="n" onclick="zoom(this);" onmouseenter="select(this);" nav="%s"> \n\ 80 <title>%s | %s (%d samples: %3.2f%%)</title>\n \ 81 <rect x="%f" y="%f" ox="%f" oy="%f" width="%f" owidth="%f" height="15.0" ofill="rgb(%d,%d,%d)" \ 82 fill="rgb(%d,%d,%d)" style="stroke:rgb(%d,%d,%d)"/>\n \ 83 <text x="%f" y="%f" font-size="%d" font-family="Monospace"></text>\n \ 84 </g>\n' % (callsite.id, ','.join(str(x) for x in nav), 85 method, callsite.dso, callsite.num_samples, callsite.num_samples/float(num_samples) * 100, 86 x, y, x, y, width , width, r, g, b, r, g, b, r_border, g_border, b_border, 87 x+2, y+12, FONT_SIZE)) 88 89 90def renderSVGNodes(flamegraph, depth, f, num_samples, height, color_scheme): 91 for i, callsite in enumerate(flamegraph.callsites): 92 # Prebuild navigation target for wasd 93 94 if i == 0: 95 left_index = 0 96 else: 97 left_index = flamegraph.callsites[i-1].id 98 99 if i == len(flamegraph.callsites)-1: 100 right_index = 0 101 else: 102 right_index = flamegraph.callsites[i+1].id 103 104 105 up_index = 0 106 max_up = 0 107 for upcallsite in callsite.callsites: 108 if upcallsite.num_samples > max_up: 109 max_up = upcallsite.num_samples 110 up_index = upcallsite.id 111 112 # up, left, down, right 113 nav = [up_index, left_index,flamegraph.id,right_index] 114 115 createSVGNode(callsite, depth, f, num_samples, height, color_scheme, nav) 116 # Recurse down 117 renderSVGNodes(callsite, depth+1, f, num_samples, height, color_scheme) 118 119def renderSearchNode(f): 120 f.write( 121 '<rect id="search_rect" style="stroke:rgb(0,0,0);" onclick="search(this);" class="t" rx="10" ry="10" \ 122 x="%d" y="10" width="80" height="30" fill="rgb(255,255,255)""/> \ 123 <text id="search_text" class="t" x="%d" y="30" onclick="search(this);">Search</text>\n' 124 % (SVG_CANVAS_WIDTH - 95, SVG_CANVAS_WIDTH - 80) 125 ) 126 127 128def renderUnzoomNode(f): 129 f.write( 130 '<rect id="zoom_rect" style="display:none;stroke:rgb(0,0,0);" class="t" onclick="unzoom(this);" \ 131 rx="10" ry="10" x="10" y="10" width="80" height="30" fill="rgb(255,255,255)"/> \ 132 <text id="zoom_text" style="display:none;" class="t" x="19" y="30" \ 133 onclick="unzoom(this);">Zoom out</text>\n' 134 ) 135 136def renderInfoNode(f): 137 f.write( 138 '<clipPath id="info_clip_path"> <rect id="info_rect" style="stroke:rgb(0,0,0);" \ 139 rx="10" ry="10" x="120" y="10" width="%d" height="30" fill="rgb(255,255,255)"/></clipPath> \ 140 <rect id="info_rect" style="stroke:rgb(0,0,0);" \ 141 rx="10" ry="10" x="120" y="10" width="%d" height="30" fill="rgb(255,255,255)"/> \ 142 <text clip-path="url(#info_clip_path)" id="info_text" x="128" y="30"></text>\n' % (SVG_CANVAS_WIDTH - 335, SVG_CANVAS_WIDTH - 325) 143 ) 144 145def renderPercentNode(f): 146 f.write( 147 '<rect id="percent_rect" style="stroke:rgb(0,0,0);" \ 148 rx="10" ry="10" x="%d" y="10" width="82" height="30" fill="rgb(255,255,255)"/> \ 149 <text id="percent_text" text-anchor="end" x="%d" y="30">100.00%%</text>\n' % (SVG_CANVAS_WIDTH - (95 * 2),SVG_CANVAS_WIDTH - (125)) 150 ) 151 152 153def renderSVG(flamegraph, f, color_scheme, width): 154 global SVG_CANVAS_WIDTH 155 SVG_CANVAS_WIDTH = width 156 height = (flamegraph.get_max_depth() + 2 )* SVG_NODE_HEIGHT 157 f.write('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" \ 158 width="%d" height="%d" style="border: 1px solid black;" \ 159 onload="adjust_text_size(this);" rootid="%d">\n' % (SVG_CANVAS_WIDTH, height, flamegraph.callsites[0].id)) 160 f.write('<defs > <linearGradient id="background_gradiant" y1="0" y2="1" x1="0" x2="0" > \ 161 <stop stop-color="#eeeeee" offset="5%" /> <stop stop-color="#efefb1" offset="90%" /> </linearGradient> </defs>') 162 f.write('<rect x="0.0" y="0" width="%d" height="%d" fill="url(#background_gradiant)" />' % \ 163 (SVG_CANVAS_WIDTH, height)) 164 renderSVGNodes(flamegraph, 0, f, flamegraph.num_samples, height, color_scheme) 165 renderSearchNode(f) 166 renderUnzoomNode(f) 167 renderInfoNode(f) 168 renderPercentNode(f) 169 f.write("</svg><br/>\n\n")