PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/util/stats/profile.py

https://bitbucket.org/musleh123/ece565
Python | 504 lines | 458 code | 18 blank | 28 comment | 25 complexity | 198a468b6ae4cff6b3e848d5f6298416 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, WTFPL
  1. # Copyright (c) 2005 The Regents of The University of Michigan
  2. # All rights reserved.
  3. #
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions are
  6. # met: redistributions of source code must retain the above copyright
  7. # notice, this list of conditions and the following disclaimer;
  8. # redistributions in binary form must reproduce the above copyright
  9. # notice, this list of conditions and the following disclaimer in the
  10. # documentation and/or other materials provided with the distribution;
  11. # neither the name of the copyright holders nor the names of its
  12. # contributors may be used to endorse or promote products derived from
  13. # this software without specific prior written permission.
  14. #
  15. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  16. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  17. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  18. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  19. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  20. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  21. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  22. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  23. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  25. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. #
  27. # Authors: Nathan Binkert
  28. from orderdict import orderdict
  29. import output
  30. class FileData(dict):
  31. def __init__(self, filename):
  32. self.filename = filename
  33. fd = file(filename)
  34. current = []
  35. for line in fd:
  36. line = line.strip()
  37. if line.startswith('>>>'):
  38. current = []
  39. self[line[3:]] = current
  40. else:
  41. current.append(line)
  42. fd.close()
  43. class RunData(dict):
  44. def __init__(self, filename):
  45. self.filename = filename
  46. def __getattribute__(self, attr):
  47. if attr == 'total':
  48. total = 0.0
  49. for value in self.itervalues():
  50. total += value
  51. return total
  52. if attr == 'filedata':
  53. return FileData(self.filename)
  54. if attr == 'maxsymlen':
  55. return max([ len(sym) for sym in self.iterkeys() ])
  56. return super(RunData, self).__getattribute__(attr)
  57. def display(self, output=None, limit=None, maxsymlen=None):
  58. if not output:
  59. import sys
  60. output = sys.stdout
  61. elif isinstance(output, str):
  62. output = file(output, 'w')
  63. total = float(self.total)
  64. # swap (string,count) order so we can sort on count
  65. symbols = [ (count,name) for name,count in self.iteritems() ]
  66. symbols.sort(reverse=True)
  67. if limit is not None:
  68. symbols = symbols[:limit]
  69. if not maxsymlen:
  70. maxsymlen = self.maxsymlen
  71. symbolf = "%-" + str(maxsymlen + 1) + "s %.2f%%"
  72. for number,name in symbols:
  73. print >>output, symbolf % (name, 100.0 * (float(number) / total))
  74. class PCData(RunData):
  75. def __init__(self, filename=None, categorize=None, showidle=True):
  76. super(PCData, self).__init__(self, filename)
  77. filedata = self.filedata['PC data']
  78. for line in filedata:
  79. (symbol, count) = line.split()
  80. if symbol == "0x0":
  81. continue
  82. count = int(count)
  83. if categorize is not None:
  84. category = categorize(symbol)
  85. if category is None:
  86. category = 'other'
  87. elif category == 'idle' and not showidle:
  88. continue
  89. self[category] = count
  90. class FuncNode(object):
  91. def __new__(cls, filedata=None):
  92. if filedata is None:
  93. return super(FuncNode, cls).__new__(cls)
  94. nodes = {}
  95. for line in filedata['function data']:
  96. data = line.split(' ')
  97. node_id = long(data[0], 16)
  98. node = FuncNode()
  99. node.symbol = data[1]
  100. if node.symbol == '':
  101. node.symbol = 'unknown'
  102. node.count = long(data[2])
  103. node.children = [ long(child, 16) for child in data[3:] ]
  104. nodes[node_id] = node
  105. for node in nodes.itervalues():
  106. children = []
  107. for cid in node.children:
  108. child = nodes[cid]
  109. children.append(child)
  110. child.parent = node
  111. node.children = tuple(children)
  112. if not nodes:
  113. print filedata.filename
  114. print nodes
  115. return nodes[0]
  116. def total(self):
  117. total = self.count
  118. for child in self.children:
  119. total += child.total()
  120. return total
  121. def aggregate(self, dict, categorize, incategory):
  122. category = None
  123. if categorize:
  124. category = categorize(self.symbol)
  125. total = self.count
  126. for child in self.children:
  127. total += child.aggregate(dict, categorize, category or incategory)
  128. if category:
  129. dict[category] = dict.get(category, 0) + total
  130. return 0
  131. elif not incategory:
  132. dict[self.symbol] = dict.get(self.symbol, 0) + total
  133. return total
  134. def dump(self):
  135. kids = [ child.symbol for child in self.children]
  136. print '%s %d <%s>' % (self.symbol, self.count, ', '.join(kids))
  137. for child in self.children:
  138. child.dump()
  139. def _dot(self, dot, threshold, categorize, total):
  140. from pydot import Dot, Edge, Node
  141. self.dot_node = None
  142. value = self.total() * 100.0 / total
  143. if value < threshold:
  144. return
  145. if categorize:
  146. category = categorize(self.symbol)
  147. if category and category != 'other':
  148. return
  149. label = '%s %.2f%%' % (self.symbol, value)
  150. self.dot_node = Node(self, label=label)
  151. dot.add_node(self.dot_node)
  152. for child in self.children:
  153. child._dot(dot, threshold, categorize, total)
  154. if child.dot_node is not None:
  155. dot.add_edge(Edge(self, child))
  156. def _cleandot(self):
  157. for child in self.children:
  158. child._cleandot()
  159. self.dot_node = None
  160. del self.__dict__['dot_node']
  161. def dot(self, dot, threshold=0.1, categorize=None):
  162. self._dot(dot, threshold, categorize, self.total())
  163. self._cleandot()
  164. class FuncData(RunData):
  165. def __init__(self, filename, categorize=None):
  166. super(FuncData, self).__init__(filename)
  167. tree = self.tree
  168. tree.aggregate(self, categorize, incategory=False)
  169. self.total = tree.total()
  170. def __getattribute__(self, attr):
  171. if attr == 'tree':
  172. return FuncNode(self.filedata)
  173. return super(FuncData, self).__getattribute__(attr)
  174. def displayx(self, output=None, maxcount=None):
  175. if output is None:
  176. import sys
  177. output = sys.stdout
  178. items = [ (val,key) for key,val in self.iteritems() ]
  179. items.sort(reverse=True)
  180. for val,key in items:
  181. if maxcount is not None:
  182. if maxcount == 0:
  183. return
  184. maxcount -= 1
  185. percent = val * 100.0 / self.total
  186. print >>output, '%-30s %8s' % (key, '%3.2f%%' % percent)
  187. class Profile(object):
  188. # This list controls the order of values in stacked bar data output
  189. default_categories = [ 'interrupt',
  190. 'driver',
  191. 'stack',
  192. 'buffer',
  193. 'copy',
  194. 'syscall',
  195. 'user',
  196. 'other',
  197. 'idle']
  198. def __init__(self, datatype, categorize=None):
  199. categories = Profile.default_categories
  200. self.datatype = datatype
  201. self.categorize = categorize
  202. self.data = {}
  203. self.categories = categories[:]
  204. self.rcategories = categories[:]
  205. self.rcategories.reverse()
  206. self.cpu = 0
  207. # Read in files
  208. def inputdir(self, directory):
  209. import os, os.path, re
  210. from os.path import expanduser, join as joinpath
  211. directory = expanduser(directory)
  212. label_ex = re.compile(r'profile\.(.*).dat')
  213. for root,dirs,files in os.walk(directory):
  214. for name in files:
  215. match = label_ex.match(name)
  216. if not match:
  217. continue
  218. filename = joinpath(root, name)
  219. prefix = os.path.commonprefix([root, directory])
  220. dirname = root[len(prefix)+1:]
  221. data = self.datatype(filename, self.categorize)
  222. self.setdata(dirname, match.group(1), data)
  223. def setdata(self, run, cpu, data):
  224. if run not in self.data:
  225. self.data[run] = {}
  226. if cpu in self.data[run]:
  227. raise AttributeError, \
  228. 'data already stored for run %s and cpu %s' % (run, cpu)
  229. self.data[run][cpu] = data
  230. def getdata(self, run, cpu):
  231. try:
  232. return self.data[run][cpu]
  233. except KeyError:
  234. print run, cpu
  235. return None
  236. def alldata(self):
  237. for run,cpus in self.data.iteritems():
  238. for cpu,data in cpus.iteritems():
  239. yield run,cpu,data
  240. def get(self, job, stat, system=None):
  241. if system is None and hasattr('system', job):
  242. system = job.system
  243. if system is None:
  244. raise AttributeError, 'The job must have a system set'
  245. cpu = '%s.run%d' % (system, self.cpu)
  246. data = self.getdata(str(job), cpu)
  247. if not data:
  248. return None
  249. values = []
  250. for category in self.categories:
  251. val = float(data.get(category, 0.0))
  252. if val < 0.0:
  253. raise ValueError, 'value is %f' % val
  254. values.append(val)
  255. total = sum(values)
  256. return [ v / total * 100.0 for v in values ]
  257. def dump(self):
  258. for run,cpu,data in self.alldata():
  259. print 'run %s, cpu %s' % (run, cpu)
  260. data.dump()
  261. print
  262. def write_dot(self, threshold, jobfile=None, jobs=None):
  263. import pydot
  264. if jobs is None:
  265. jobs = [ job for job in jobfile.jobs() ]
  266. for job in jobs:
  267. cpu = '%s.run%d' % (job.system, self.cpu)
  268. symbols = self.getdata(job.name, cpu)
  269. if not symbols:
  270. continue
  271. dot = pydot.Dot()
  272. symbols.tree.dot(dot, threshold=threshold)
  273. dot.write(symbols.filename[:-3] + 'dot')
  274. def write_txt(self, jobfile=None, jobs=None, limit=None):
  275. if jobs is None:
  276. jobs = [ job for job in jobfile.jobs() ]
  277. for job in jobs:
  278. cpu = '%s.run%d' % (job.system, self.cpu)
  279. symbols = self.getdata(job.name, cpu)
  280. if not symbols:
  281. continue
  282. output = file(symbols.filename[:-3] + 'txt', 'w')
  283. symbols.display(output, limit)
  284. def display(self, jobfile=None, jobs=None, limit=None):
  285. if jobs is None:
  286. jobs = [ job for job in jobfile.jobs() ]
  287. maxsymlen = 0
  288. thejobs = []
  289. for job in jobs:
  290. cpu = '%s.run%d' % (job.system, self.cpu)
  291. symbols = self.getdata(job.name, cpu)
  292. if symbols:
  293. thejobs.append(job)
  294. maxsymlen = max(maxsymlen, symbols.maxsymlen)
  295. for job in thejobs:
  296. cpu = '%s.run%d' % (job.system, self.cpu)
  297. symbols = self.getdata(job.name, cpu)
  298. print job.name
  299. symbols.display(limit=limit, maxsymlen=maxsymlen)
  300. print
  301. from categories import func_categorize, pc_categorize
  302. class PCProfile(Profile):
  303. def __init__(self, categorize=pc_categorize):
  304. super(PCProfile, self).__init__(PCData, categorize)
  305. class FuncProfile(Profile):
  306. def __init__(self, categorize=func_categorize):
  307. super(FuncProfile, self).__init__(FuncData, categorize)
  308. def usage(exitcode = None):
  309. print '''\
  310. Usage: %s [-bc] [-g <dir>] [-j <jobfile>] [-n <num>]
  311. -c groups symbols into categories
  312. -b dumps data for bar charts
  313. -d generate dot output
  314. -g <d> draw graphs and send output to <d>
  315. -j <jobfile> specify a different jobfile (default is Test.py)
  316. -n <n> selects number of top symbols to print (default 5)
  317. ''' % sys.argv[0]
  318. if exitcode is not None:
  319. sys.exit(exitcode)
  320. if __name__ == '__main__':
  321. import getopt, re, sys
  322. from os.path import expanduser
  323. from output import StatOutput
  324. # default option values
  325. numsyms = 10
  326. graph = None
  327. cpus = [ 0 ]
  328. categorize = False
  329. showidle = True
  330. funcdata = True
  331. jobfilename = 'Test.py'
  332. dodot = False
  333. dotfile = None
  334. textout = False
  335. threshold = 0.01
  336. inputfile = None
  337. try:
  338. opts, args = getopt.getopt(sys.argv[1:], 'C:cdD:f:g:ij:n:pT:t')
  339. except getopt.GetoptError:
  340. usage(2)
  341. for o,a in opts:
  342. if o == '-C':
  343. cpus = [ int(x) for x in a.split(',') ]
  344. elif o == '-c':
  345. categorize = True
  346. elif o == '-D':
  347. dotfile = a
  348. elif o == '-d':
  349. dodot = True
  350. elif o == '-f':
  351. inputfile = expanduser(a)
  352. elif o == '-g':
  353. graph = a
  354. elif o == '-i':
  355. showidle = False
  356. elif o == '-j':
  357. jobfilename = a
  358. elif o == '-n':
  359. numsyms = int(a)
  360. elif o == '-p':
  361. funcdata = False
  362. elif o == '-T':
  363. threshold = float(a)
  364. elif o == '-t':
  365. textout = True
  366. if args:
  367. print "'%s'" % args, len(args)
  368. usage(1)
  369. if inputfile:
  370. catfunc = None
  371. if categorize:
  372. catfunc = func_categorize
  373. data = FuncData(inputfile, categorize=catfunc)
  374. if dodot:
  375. import pydot
  376. dot = pydot.Dot()
  377. data.tree.dot(dot, threshold=threshold)
  378. #dot.orientation = 'landscape'
  379. #dot.ranksep='equally'
  380. #dot.rank='samerank'
  381. dot.write(dotfile, format='png')
  382. else:
  383. data.display(limit=numsyms)
  384. else:
  385. from jobfile import JobFile
  386. jobfile = JobFile(jobfilename)
  387. if funcdata:
  388. profile = FuncProfile()
  389. else:
  390. profile = PCProfile()
  391. if not categorize:
  392. profile.categorize = None
  393. profile.inputdir(jobfile.rootdir)
  394. if graph:
  395. for cpu in cpus:
  396. profile.cpu = cpu
  397. if funcdata:
  398. name = 'funcstacks%d' % cpu
  399. else:
  400. name = 'stacks%d' % cpu
  401. output = StatOutput(jobfile, info=profile)
  402. output.xlabel = 'System Configuration'
  403. output.ylabel = '% CPU utilization'
  404. output.stat = name
  405. output.graph(name, graph)
  406. if dodot:
  407. for cpu in cpus:
  408. profile.cpu = cpu
  409. profile.write_dot(jobfile=jobfile, threshold=threshold)
  410. if textout:
  411. for cpu in cpus:
  412. profile.cpu = cpu
  413. profile.write_txt(jobfile=jobfile)
  414. if not graph and not textout and not dodot:
  415. for cpu in cpus:
  416. if not categorize:
  417. profile.categorize = None
  418. profile.cpu = cpu
  419. profile.display(jobfile=jobfile, limit=numsyms)