[new attempt at filename display in html mode; clean up group names in json; more css on both views Drew Perttula **20070207080851] { hunk ./profileResults.html 4 - + hunk ./profileResults.html 19 + function addFilenameLink(itemID, database, td) { + var filename = database.getObject(itemID, "filename"); + td.innerHTML = "" + td.innerHTML + ""; + } + hunk ./profileResults.html 34 + self._columns[5].styler = addFilenameLink hunk ./profileResults.html 43 -body { - margin: 1em; - } - table { - border: 0px solid black; - border-collapse: collapse; - } - td {border: 0px solid black } - div.bar { - background:#72C8A1 none repeat scroll 0% 50%; - } -exhibit-tabularView-columnHeader { - font-size: 50%; -} + body { + margin: 1em; + background: #eee; + } + + table { + border: 0px solid black; + border-collapse: collapse; + width: 100%; + } + + td {border: 0px solid black } + + div.bar { + background:#72C8A1 none repeat scroll 0% 50%; + } + + exhibit-tabularView-columnHeader { + font-size: 50%; + } + + span.exhibit-value { + margin: 0; + top: 0; + } hunk ./profileResults.html 69 -span.exhibit-value { - margin: 0; - top: 0; + div.exhibit-facet-body { + height: 20em; + } hunk ./profileResults.html 73 +.exhibit-ui-protection table { + font-size: 100%; +} +.exhibit-ui-protection tr { + vertical-align: top; +} +a img { + border: none; +} + +div.exhibit-facet-body-frame { + background: #FDFBF0; hunk ./profileResults.html 87 -div.exhibit-facet-body { - height: 30em; - width: 15em; +div.exhibit-viewPanel { + background: white; + padding: 5px; + border: 1px solid black; hunk ./profileResults.html 92 + + hunk ./profileResults.html 108 - - ex:rowStyler="addBars" - + ex:sortColumn="1" + ex:sortAscending="false" hunk ./profileResults.html 114 -
+
hunk ./pstats2web 15 +from cPickle import dump, load hunk ./pstats2web 17 -#import pstats +import pstats hunk ./pstats2web 20 +def getStats(filename): + """here is where to detect the difference between pstats and + hotshot stats and call the appropriate loader. But I haven't cared + about non-hotshot yet""" + return hotshot.stats.load(filename) + #return pstats.Stats(filename) + hunk ./pstats2web 52 - self.groups = [self.noFilename] - else: - self.groups = [] - - for x in xrange(1, self.groupThreshold+1): - dirlist = os.path.dirname(self.filename).split(os.sep) - self.groups.append('/'.join(dirlist[-x:])) - - emptyGroups = reduce(lambda x, y: x and y, - map(lambda x: x == '', - self.groups)) + self.groups = set([self.noFilename]) + return + + self.groups = set() + for x in xrange(1, self.groupThreshold+1): + dirlist = os.path.dirname(self.filename).split(os.sep) + tailParts = dirlist[-x:] + while (len(tailParts) > 1 and + tailParts[0] in ['lib', 'python2.4', 'site-packages', + 'build']): + tailParts.pop(0) + self.groups.add(os.sep.join(tailParts)) hunk ./pstats2web 65 - if emptyGroups: - self.groups = [self.filename] + self.groups.difference_update(set([ + 'python', 'lib/python2.4', 'python2.4' + ])) + + if self.groups == set(['']): + self.groups = set([self.filename]) hunk ./pstats2web 80 - -def style(filename): - rgb = [int(x) / 10 for x in str(hash(filename))[-3:]] - return "background: #%01x%01x%01x" % tuple(14 - x * 6 for x in rgb) - hunk ./pstats2web 81 + """attempt to throw out large outliers in L. Currently, an outlier + is a vaule that's more than 5x the next smaller value. + + current version is not great on [1,1,1,1,1,2,7,8], where i think + the answer is 2 + """ hunk ./pstats2web 88 - return L[-4] - + while len(L) > 2 and L[-1] > 5 * L[-2]: + L.pop() + return L[-1] + hunk ./pstats2web 96 -# s = pstats.Stats(filename) - s = hotshot.stats.load(filename) + s = getStats(filename) hunk ./pstats2web 104 +def rtrimTo(s, maxLen): + if len(s) > maxLen: + return "..." + s[-maxLen+3:] + return s + hunk ./pstats2web 117 - u'tottime' : func.tottime, - u'cumtime' : func.cumtime, + u'tottime' : float("%.3f" % func.tottime), + u'cumtime' : float("%.3f" % func.cumtime), + u'tpercall' : float("%.4f" % func.tpercall), hunk ./pstats2web 124 - u'label' : u'%s (%s:%s)' % (func.function, func.filename[-20:], func.linenum), + u'label' : u'%s (%s:%s)' % (func.function, rtrimTo(func.filename, 40), func.linenum), hunk ./pstats2web 130 - if len(items) > 15: - break hunk ./pstats2web 134 - """return color,shortName for this filename: + cwd = os.path.abspath(os.getcwd()) + os.sep hunk ./pstats2web 136 - /home/drewp/projects/ffg-http/Nevow/build/lib/nevow/flat/flatsax.py - /usr/lib/python2.4/site-packages/twisted/python/reflect.py - /usr/lib/python2.4/urllib.py + if filename == '': + return "stringCode", filename hunk ./pstats2web 139 - """ - cwd = os.path.abspath(os.getcwd()) + '/' - filename = os.path.abspath(filename) - stdlib = '/usr/lib/python2.4/' - if filename.startswith(stdlib) and not filename.startswith(stdlib + 'site-packages/'): - label = "(stdlib )" + stdlib = os.path.dirname(os.__file__) + os.sep + if filename.startswith(stdlib) and not filename.startswith(stdlib + 'site-packages' + os.sep): + label = "(stdlib) " hunk ./pstats2web 144 - return "blue", label + filename[len(stdlib):] - sitePkg = '/usr/lib/python2.4/site-packages/' + return "stdlib", label + filename[len(stdlib):] + + sitePkg = os.path.join(stdlib, 'site-packages/') hunk ./pstats2web 148 - color = "green" - nextDir = os.path.split(filename[len(sitePkg):])[0] - if nextDir == 'twisted': - color = "gray" - return color, filename[len(sitePkg):] - if '/nevow/' in filename: # special build that's not installed - return "green", filename[filename.index('/nevow/')+1:] - if filename.startswith(cwd): - return "red", filename[len(cwd):] - return "gray", filename + return "site-packages", filename[len(sitePkg):] + + return "pkg-unknown", filename + +def style(filename): + rgb = [int(x) / 10 for x in str(hash(filename))[-3:]] + return "background: #%01x%01x%01x" % tuple(14 - x * 3 for x in rgb) + +def annotateCssNames(funcs): + """add more attributes to each func: + + cssClass - 'stdlib', 'site-packages', 'pkg-unknown', 'pkg-{dirname}' + shortName - for displaying + suffix - internal use + + returns additional CSS for coloring the pkg-{dirname} styles + """ + prefixesToRemove = {} # prefix : count + + for f in funcs: + cssClass, shortName = groupFilename(os.path.abspath(f.filename)) + if cssClass == 'pkg-unknown': + parts = f.filename.split(os.sep) + # don't consider a single-piece prefix like 'foo/bar.py' + for i in range(2, len(parts)): + prefix = os.sep.join(parts[:i]) + prefixesToRemove[prefix] = prefixesToRemove.get(prefix, 0) + 1 + + prefixes = sorted(prefixesToRemove, key=lambda p: -len(p)) + + classes = set() + for f in funcs: + suffix = f.filename + for prefix in prefixes: + if suffix.startswith(prefix): + prefix = prefix[:prefix[:-1].rfind(os.sep)] + suffix = suffix[len(prefix)+1:] + break + f.suffix = suffix + + cssClass, shortName = groupFilename(f.suffix) + + if cssClass == 'pkg-unknown' and os.sep in f.suffix: + cssClass = 'pkg-%s' % f.suffix[:f.suffix.find(os.sep)] + classes.add(cssClass) + + f.cssClass = cssClass + f.shortName = shortName + + css = "" + for cssClass in classes: + css += 'td.%s { %s }\n' % (cssClass, style(cssClass)) + return css hunk ./pstats2web 205 + + moreCss = annotateCssNames(funcs) + hunk ./pstats2web 210 - if f.function != 'profiler': - #s = style(f.groups[0]) - color, shortName = groupFilename(f.filename) + if f.function != 'profiler': + location = [T.span(class_="fileLinenum")[ + T.a(href=f.filename)[f.shortName], ":%d" % f.linenum], + " %s" % f.function] hunk ./pstats2web 215 - T.td[bar(f.tottime, maxTottime)["%.3f" % f.tottime]], + T.td[bar(f.tottime, maxTottime)[ + "%.3f" % f.tottime]], hunk ./pstats2web 218 - T.td[bar(f.cumtime, maxCumtime)["%.3f" % f.cumtime]], + T.td[bar(f.cumtime, maxCumtime)[ + "%.3f" % f.cumtime]], hunk ./pstats2web 221 - T.td(style="background: %s" % color)["%s:%d(%s)" % (shortName, int(f.linenum), f.function)]], + T.td(class_=f.cssClass)[location]], hunk ./pstats2web 234 + padding: 2px; + white-space: nowrap; hunk ./pstats2web 237 + hunk ./pstats2web 241 - '''] + td.stdlib { + background: #99CCFF; + } + td.site-packages { + background: #E8C98B; + } + + span.fileLinenum { + color: gray; + font-size: 90%; + } + span.fileLinenum a { + color: #444; + } + ''', moreCss] hunk ./pstats2web 269 - + +def cacheFuncs(funcsGet): + """while you're working on this program, it's nice to not reload + the slow stats every time""" + f = "/tmp/stat" + if not os.path.exists(f): + funcs = funcsGet() + dump(funcs, open(f, "w")) + return funcs + return load(open(f)) hunk ./pstats2web 283 - parser.add_option("-j", "--output-json", help="output exhibit data to this file") + parser.add_option("-j", "--output-json", + help="output exhibit data to this file") + parser.add_option("-m", "--max-rows", type="int", + help="max rows to show, when sorted by decreasing total time") hunk ./pstats2web 289 - funcs = parseProfile(sys.argv[1]) + if 0: # for quick startups while testing + funcs = cacheFuncs(lambda: parseProfile(args[0])) + else: + funcs = parseProfile(args[0]) hunk ./pstats2web 294 - funcs.sort(key=lambda f: f.tottime, - reverse=True) + funcs.sort(key=lambda f: f.tottime, reverse=True) + if opts.max_rows: + funcs = funcs[:opts.max_rows] }