# # Copyright (C) 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import sys SVG_NODE_HEIGHT = 17 FONT_SIZE = 12 UNZOOM_NODE_ORIGIN_X = 10 UNZOOM_NODE_WIDTH = 80 INFO_NODE_ORIGIN_X = 120 INFO_NODE_WIDTH = 800 PERCENT_NODE_ORIGIN_X = 930 PERCENT_NODE_WIDTH = 250 SEARCH_NODE_ORIGIN_X = 1190 SEARCH_NODE_WIDTH = 80 RECT_TEXT_PADDING = 10 def hash_to_float(string): return hash(string) / float(sys.maxsize) def getLegacyColor(method): r = 175 + int(50 * hash_to_float(reversed(method))) g = 60 + int(180 * hash_to_float(method)) b = 60 + int(55 * hash_to_float(reversed(method))) return (r, g, b) def getDSOColor(method): r = 170 + int(80 * hash_to_float(reversed(method))) g = 180 + int(70 * hash_to_float((method))) b = 170 + int(80 * hash_to_float(reversed(method))) return (r, g, b) def getHeatColor(callsite, total_weight): r = 245 + 10 * (1 - callsite.weight() / total_weight) g = 110 + 105 * (1 - callsite.weight() / total_weight) b = 100 return (r, g, b) def get_proper_scaled_time_string(value): if value >= 1e9: return '%.3f s' % (value / 1e9) if value >= 1e6: return '%.3f ms' % (value / 1e6) if value >= 1e3: return '%.3f us' % (value / 1e3) return '%.0f ns' % value def createSVGNode(process, callsite, depth, f, total_weight, height, color_scheme, nav): x = float(callsite.offset) / total_weight * 100 y = height - (depth + 1) * SVG_NODE_HEIGHT width = callsite.weight() / total_weight * 100 method = callsite.method.replace(">", ">").replace("<", "<") if width <= 0: return if color_scheme == "dso": r, g, b = getDSOColor(callsite.dso) elif color_scheme == "legacy": r, g, b = getLegacyColor(method) else: r, g, b = getHeatColor(callsite, total_weight) r_border, g_border, b_border = [max(0, color - 50) for color in [r, g, b]] if process.props['trace_offcpu']: weight_str = get_proper_scaled_time_string(callsite.weight()) else: weight_str = "{:,}".format(int(callsite.weight())) + ' events' f.write( """ %s | %s (%s: %3.2f%%) """ % (callsite.id, ','.join(str(x) for x in nav), method, callsite.dso, weight_str, callsite.weight() / total_weight * 100, x, y, x, y, width, width, r, g, b, r, g, b, r_border, g_border, b_border, x, y + 12, FONT_SIZE)) def renderSVGNodes(process, flamegraph, depth, f, total_weight, height, color_scheme): for i, child in enumerate(flamegraph.children): # Prebuild navigation target for wasd if i == 0: left_index = 0 else: left_index = flamegraph.children[i - 1].id if i == len(flamegraph.children) - 1: right_index = 0 else: right_index = flamegraph.children[i + 1].id up_index = max(child.children, key=lambda x: x.weight()).id if child.children else 0 # up, left, down, right nav = [up_index, left_index, flamegraph.id, right_index] createSVGNode(process, child, depth, f, total_weight, height, color_scheme, nav) # Recurse down renderSVGNodes(process, child, depth + 1, f, total_weight, height, color_scheme) def renderSearchNode(f): f.write( """ Search """ % (SEARCH_NODE_ORIGIN_X, SEARCH_NODE_WIDTH, SEARCH_NODE_ORIGIN_X + RECT_TEXT_PADDING)) def renderUnzoomNode(f): f.write( """