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 17 class CallSite: 18 def __init__(self, ip, method, dso): 19 self.ip = ip 20 self.method = method 21 self.dso = dso 22 23 24 25 class Thread: 26 def __init__(self, tid): 27 self.tid = tid 28 self.samples = [] 29 self.flamegraph = {} 30 self.num_samples = 0 31 32 33 def add_callchain(self, callchain, symbol, sample): 34 chain = [] 35 self.num_samples += 1 36 for j in range(callchain.nr): 37 entry = callchain.entries[callchain.nr - j - 1] 38 if entry.ip == 0: 39 continue 40 chain.append(CallSite(entry.ip, entry.symbol.symbol_name, entry.symbol.dso_name)) 41 42 chain.append(CallSite(sample.ip, symbol.symbol_name, symbol.dso_name)) 43 self.samples.append(chain) 44 45 46 def collapse_flamegraph(self): 47 flamegraph = FlameGraphCallSite("root", "") 48 flamegraph.id = 0 # This is used for wasd navigation, 0 = not a valid target. 49 self.flamegraph = flamegraph 50 for sample in self.samples: 51 flamegraph = self.flamegraph 52 for callsite in sample: 53 flamegraph = flamegraph.get_callsite(callsite.method, callsite.dso) 54 55 # Populate root note. 56 for node in self.flamegraph.callsites: 57 self.flamegraph.num_samples += node.num_samples 58 59 60 class Process: 61 def __init__(self, name, pid): 62 self.name = name 63 self.pid = pid 64 self.threads = {} 65 self.cmd = "" 66 self.props = {} 67 self.args = None 68 self.num_samples = 0 69 70 def get_thread(self, tid): 71 if (tid not in self.threads.keys()): 72 self.threads[tid] = Thread(tid) 73 return self.threads[tid] 74 75 CALLSITE_COUNTER = 0 76 def get_callsite_id(): 77 global CALLSITE_COUNTER 78 CALLSITE_COUNTER += 1 79 toReturn = CALLSITE_COUNTER 80 return toReturn 81 82 83 class FlameGraphCallSite: 84 85 def __init__(self, method, dso): 86 self.callsites = [] 87 self.method = method 88 self.dso = dso 89 self.num_samples = 0 90 self.offset = 0 # Offset allows position nodes in different branches. 91 self.id = get_callsite_id() 92 93 94 def get_callsite(self, name, dso): 95 for c in self.callsites: 96 if c.equivalent(name, dso): 97 c.num_samples += 1 98 return c 99 callsite = FlameGraphCallSite(name, dso) 100 callsite.num_samples = 1 101 self.callsites.append(callsite) 102 return callsite 103 104 def equivalent(self, method, dso): 105 return self.method == method and self.dso == dso 106 107 108 def get_max_depth(self): 109 max = 0 110 for c in self.callsites: 111 depth = c.get_max_depth() 112 if depth > max: 113 max = depth 114 return max +1