8 from xml.dom.minidom import parse as parseXML
9 from django.template.loader import render_to_string
15 """ Utility function: return the number in a 'count' element contained in
17 countnode = node.getElementsByTagName('count')
18 return int(countnode.item(0).firstChild.data)
20 def _threshold_list(list, fn, threshold):
21 """ Utility function: given a list and an ordering function, return the
22 elements of the list that are above the threshold.
24 If threshold is a percentage (ie, ends with '%'), then the items
25 that fn returns above that percentage of the total. If it's an absolute
26 number, then that number of items is returned"""
28 if threshold.endswith('%'):
29 percentage = float(threshold[0:-1]) / 100
30 total = reduce(int.__add__, map(fn, list))
31 list = [ x for x in list if fn(x) > (total * percentage) ]
32 list.sort(key = fn, reverse = True)
35 count = int(threshold)
36 list.sort(key = fn, reverse = True)
41 class Connection(object):
42 def __init__(self, host):
45 def execute(self, command, input = None):
48 command = ['ssh', self.host] + command
51 stdin = subprocess.PIPE
55 null_fd = open('/dev/null', 'w')
56 proc = subprocess.Popen(command, stdin = stdin,
57 stdout = subprocess.PIPE, stderr = null_fd)
60 proc.stdin.write(input)
64 class SymbolInstruction(object):
65 def __init__(self, addr, asm, source = None):
74 def set_samples(self, samples, total):
75 self.samples = samples
76 self.percentage = 100 * float(samples) / total
79 if not self.percentage:
82 if self.percentage * 40 > 0xc0:
85 return '#ff%02x00' % (0xc0 - self.percentage * 40)
88 # 13283 0.7260 :1031b6e0: stwu r1,-48(r1)
89 sampled_asm_re = re.compile( \
90 '^\s*(?P<samples>\d+)\s+\S+\s+:\s*(?P<addr>[0-9a-f]+):\s*(?P<asm>.*)\s*$')
92 # :1031b72c: addi r0,r9,4
93 unsampled_asm_re = re.compile( \
94 '^\s*:\s*(?P<addr>[0-9a-f]+):\s*(?P<asm>.*)\s*$')
96 # :AllocSetAlloc(MemoryContext context, Size size)
97 source_re = re.compile( \
98 '^\s*:(?P<source>.*)$')
100 class SymbolReference(object):
102 def __init__(self, id, name, module):
107 self.annotations = None
109 # annotation parsing buf
113 def module_name(self):
114 return self.module.split('/')[-1]
117 return 'symbol-%s.html' % self.id
119 def annotate(self, line):
122 sampled_match = sampled_asm_re.match(line)
124 match = sampled_match
126 unsampled_match = unsampled_asm_re.match(line)
128 match = unsampled_match
131 insn = SymbolInstruction(match.group('addr'), match.group('asm'),
134 insn.set_samples(int(match.group('samples')), self.count)
135 self.insns.append(insn)
139 match = source_re.match(line)
141 self.source_buf += match.group('source') + '\n'
144 def parse(report, node, module):
145 id = int(node.getAttribute('idref'))
146 ref = SymbolReference(id, report.symtab[id], module)
148 ref.count = _get_count(node)
151 class Binary(object):
153 def __init__(self, name, count):
158 self.id = b_id = b_id + 1
161 s = '%s: %d' % (self.name, self.count)
162 for ref in self.references:
167 return self.name.split('/')[-1]
170 return 'binary-%d.html' % self.id
172 def threshold(self, thresholds):
173 self.references = _threshold_list(self.references,
174 lambda r: r.count, thresholds['symbol'])
175 self.reference_dict = dict([ (r.name, r) for r in self.references ])
177 def annotate(self, report, conn):
178 fn_re = re.compile('^[0-9a-f]+\s+<[^>]+>: /\* (\S+) total:')
180 symbols = [ s for s in self.references if s.name != '(no symbols)' ]
185 command = ['opannotate', '--source', '--assembly',
186 '--include-file=' + self.name,
187 '-i', ','.join([ s.name for s in symbols ])]
189 fd = conn.execute(command)
193 for line in fd.readlines():
194 match = fn_re.match(line)
197 symbol.annotate(line)
198 symname = match.group(1)
199 if symname in self.reference_dict:
200 symbol = self.reference_dict[symname]
204 symbol.annotate(line)
206 def parse_symbol(self, report, node, module = None):
210 ref = SymbolReference.parse(report, node, module)
211 ref.percentage = 100 * float(ref.count) / self.count
212 self.references.append(ref)
216 def parse(report, node):
217 name = node.getAttribute('name')
219 binary = Binary(name, _get_count(node))
221 for child_node in node.childNodes:
222 if child_node.nodeType != node.ELEMENT_NODE:
225 if child_node.nodeName == 'symbol':
226 binary.parse_symbol(report, child_node, None)
228 elif child_node.nodeName == 'module':
229 module_name = child_node.getAttribute('name')
230 for child_sym_node in child_node.getElementsByTagName('symbol'):
231 binary.parse_symbol(report, child_sym_node, module_name)
235 class Report(object):
236 def __init__(self, host, arch, cpu):
242 self.total_samples = 0
244 def add_binary(self, binary):
245 self.binaries.append(binary)
246 self.total_samples += binary.count
248 def threshold(self, thresholds):
249 self.binaries = _threshold_list(self.binaries,
250 lambda b: b.count, thresholds['binary'])
252 for binary in self.binaries:
253 binary.threshold(thresholds)
255 def annotate(self, conn):
256 for binary in self.binaries:
257 binary.annotate(self, conn)
260 def parse(doc, hostname):
261 node = doc.documentElement
263 cpu = '%s (%s MHz)' % (\
264 node.getAttribute('processor'),
265 node.getAttribute('mhz'))
267 report = Report(hostname, node.getAttribute('cputype'), cpu)
270 symtab_node = doc.getElementsByTagName('symboltable').item(0)
272 for node in symtab_node.childNodes:
273 if node.nodeType != node.ELEMENT_NODE:
275 report.symtab.insert(int(node.getAttribute('id')),
276 node.getAttribute('name'))
279 # parse each binary node
280 for node in doc.getElementsByTagName('binary'):
281 binary = Binary.parse(report, node)
282 report.add_binary(binary)
284 # calculate percentages
285 for binary in report.binaries:
286 binary.percentage = 100 * float(binary.count) / report.total_samples
291 def extract(connection):
292 fd = connection.execute(['opreport', '--xml'])
296 hostname = connection.host
298 hostname = socket.gethostname()
300 return Report.parse(doc, hostname)
303 return self.machine + '\n' + '\n'.join(map(str, self.binaries))
305 def write_report(report, resourcedir, outdir):
309 # copy required files over
310 files = ['style.css', 'hiprofile.js', 'bar.png', 'jquery-1.3.1.min.js']
312 shutil.copy(os.path.join(resourcedir, file), outdir)
314 f = open(os.path.join(outdir, 'index.html'), 'w')
315 f.write(render_to_string('report.html', { 'report': report }))
318 for binary in report.binaries:
319 f = open(os.path.join(outdir, binary.filename()), 'w')
320 f.write(render_to_string('binary.html',
321 { 'report': report, 'binary': binary }))
324 for ref in binary.references:
325 f = open(os.path.join(outdir, ref.filename()), 'w')
326 f.write(render_to_string('symbol.html',
327 { 'report': report, 'binary': binary, 'symbol': ref }))