commit 2858c560b2b5af8a9fa4bd9692a306b5edfe264f Author: Josef Weidendorfer Date: Tue Mar 4 00:31:51 2014 +0100 Fix hotshot2calltree: remove SVN keyword substitution Replaced by cmake configuration file substitution feature BUG: 331715 diff --git a/converters/CMakeLists.txt b/converters/CMakeLists.txt index 10fe81f..2589d7c 100644 --- a/converters/CMakeLists.txt +++ b/converters/CMakeLists.txt @@ -1,2 +1,11 @@ -install( PROGRAMS hotshot2calltree op2calltree pprof2calltree dprof2calltree memprof2calltree - DESTINATION ${BIN_INSTALL_DIR} ) +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/hotshot2calltree.cmake + ${CMAKE_CURRENT_BINARY_DIR}/hotshot2calltree + ) +macro_additional_clean_files( + ${CMAKE_CURRENT_BINARY_DIR}/hotshot2calltree + ) + +install( PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/hotshot2calltree + op2calltree pprof2calltree dprof2calltree memprof2calltree + DESTINATION ${BIN_INSTALL_DIR} ) diff --git a/converters/hotshot2calltree b/converters/hotshot2calltree deleted file mode 100644 index b7b9992..0000000 --- a/converters/hotshot2calltree +++ /dev/null @@ -1,394 +0,0 @@ -#!/usr/bin/env python -# _*_ coding: latin1 _*_ - -# -# Copyright (c) 2003 by WEB.DE, Karlsruhe -# Autor: Jörg Beyer -# -# hotshot2cachegrind is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -# Boston, MA 02110-1301, USA. -# -# -# This script transforms the pstat output of the hotshot -# python profiler into the input of kcachegrind. -# -# example usage: -# modify you python script to run this code: -# -# import hotshot -# filename = "pythongrind.prof" -# prof = hotshot.Profile(filename, lineevents=1) -# prof.runcall(run) # assuming that "run" should be called. -# prof.close() -# -# it will run the "run"-method under profiling and write -# the results in a file, called "pythongrind.prof". -# -# then call this script: -# hotshot2cachegrind -o -# or here: -# hotshot2cachegrind cachegrind.out.0 pythongrind.prof -# -# then call kcachegrind: -# kcachegrind cachegrind.out.0 -# -# TODO: -# * es gibt Probleme mit rekursiven (direkt und indirekt) Aufrufen - dann -# stimmen die Kosten nicht. -# -# * einige Funktionen werden mit "?" als Name angezeigt. Evtl sind -# das nur die C/C++ extensions. -# -# * es fehlt noch ein Funktionsnamen Mangling, dass die Filenamen berücksichtigt, -# zZ sind alle __init__'s und alle run's schwer unterscheidbar :-( -# -version = "$Revision$" -progname = "hotshot2cachegrind" - -import os, sys -from hotshot import stats,log -import os.path - -file_limit=0 - -what2text = { - log.WHAT_ADD_INFO : "ADD_INFO", - log.WHAT_DEFINE_FUNC : "DEFINE_FUNC", - log.WHAT_DEFINE_FILE : "DEFINE_FILE", - log.WHAT_LINENO : "LINENO", - log.WHAT_EXIT : "EXIT", - log.WHAT_ENTER : "ENTER"} - -# a pseudo caller on the caller stack. This represents -# the Python interpreter that executes the given python -# code. -root_caller = ("PythonInterpreter",0,"execute") - -class CallStack: - """A tiny Stack implementation, based on python lists""" - def __init__(self): - self.stack = [] - self.recursion_counter = {} - def push(self, elem): - """put something on the stack""" - self.stack.append(elem) - rc = self.recursion_counter.get(elem, 0) - self.recursion_counter[elem] = rc + 1 - - def pop(self): - """get the head element of the stack and remove it from the stack""" - elem = self.stack[-1:][0] - rc = self.recursion_counter.get(elem) - 1 - if rc>0: - self.recursion_counter[elem] = rc - else: - del self.recursion_counter[elem] - return self.stack.pop() - - def top(self): - """get the head element of the stack, stack is unchanged.""" - return self.stack[-1:][0] - def handleLineCost(self, tdelta): - p, c = self.stack.pop() - self.stack.append( (p,c + tdelta) ) - def size(self): - """ return how many elements the stack has""" - return len(self.stack) - - def __str__(self): - return "[stack: %s]" % self.stack - - def recursion(self, pos): - return self.recursion_counter.get(pos, 0) - #return self.recursion_dict.has_key((entry[0][0], entry[0][2])) - -def return_from_call(caller_stack, call_dict, cost_now): - """return from a function call - remove the function from the caller stack, - add the costs to the calling function. - """ - called, cost_at_enter = caller_stack.pop() - caller, caller_cost = caller_stack.top() - - #print "return_from_call: %s ruft %s" % (caller, called,) - - per_file_dict = call_dict.get(called[0], {}) - per_caller_dict = per_file_dict.get(called[2], {}) - cost_so_far, call_counter = per_caller_dict.get(caller, (0, 0)) - - if caller_stack.recursion(called): - per_caller_dict[caller] = (cost_so_far, call_counter + 1) - else: - per_caller_dict[caller] = (cost_so_far + cost_now - cost_at_enter, call_counter + 1) - - per_file_dict[called[2]] = per_caller_dict - call_dict[called[0]] = per_file_dict - - -def updateStatus(filecount): - sys.stdout.write("reading File #%d \r" % filecount) - sys.stdout.flush() -def convertProfFiles(output, inputfilenames): - """convert all the given input files into one kcachegrind - input file. - """ - call_dict = {} - cost_per_pos = {} - cost_per_function = {} - caller_stack = CallStack() - caller_stack.push((root_caller, 0)) - - total_cost = 0 - filecount = 1 - number_of_files = len(inputfilenames) - for inputfilename in inputfilenames: - updateStatus(filecount) - cost, filecount = convertHandleFilename(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount) - total_cost += cost - if (file_limit > 0) and (filecount > file_limit): - break - - print - print "total_cost: % d Ticks",total_cost - dumpResults(output, call_dict, total_cost, cost_per_pos, cost_per_function) - -def convertHandleFilename(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount): - updateStatus(filecount) - if not ((file_limit > 0) and (filecount > file_limit)): - if os.path.isdir(inputfilename): - cost, filecount = convertProfDir(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount) - elif os.path.isfile(inputfilename): - cost = convertProfFile(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function) - filecount += 1 - else: - sys.stderr.write("warn: ignoring '%s', is no file and no directory\n" % inputfilename) - cost = 0 - return (cost, filecount) - -def convertProfDir(start, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount): - cost = 0 - filenames = os.listdir(start) - for f in filenames: - if (file_limit > 0) and (filecount > file_limit): - break - full = os.path.join(start, f) - c, filecount = convertHandleFilename(full, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount) - cost += c; - return (cost, filecount) - -def handleCostPerPos(cost_per_pos, pos, current_cost): - """ - the cost per source position are managed in a dict in a dict. - - the cost are handled per file and there per function. - so, the per-file-dict contains some per-function-dicts - which sum up the cost per line (in this function and in - this file). - """ - filename = pos[0] - lineno = pos[1] - funcname = pos[2] - file_dict = cost_per_pos.get(filename, {}) - func_dict = file_dict.get(funcname, {}) - func_dict.setdefault(lineno, 0) - func_dict[lineno] += current_cost - file_dict[funcname] = func_dict - cost_per_pos[filename] = file_dict - -def convertProfFile(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function): - """convert a single input file into one kcachegrind - data. - - this is the most expensive function in this python source :-) - """ - - total_cost = 0 - try: - logreader = log.LogReader(inputfilename) - current_cost = 0 - hc = handleCostPerPos # shortcut - for item in logreader: - what, pos ,tdelta = item - (file, lineno, func) = pos - #line = "%s %s %d %s %d" % (what2text[what], file, lineno, func, tdelta) - #print line - # most common cases first - if what == log.WHAT_LINENO: - # add the current cost to the current function - hc(cost_per_pos, pos, tdelta) - total_cost += tdelta - elif what == log.WHAT_ENTER: - caller_stack.push((pos, total_cost)) - hc(cost_per_pos, pos, tdelta) - total_cost += tdelta - elif what == log.WHAT_EXIT: - hc(cost_per_pos, pos, tdelta) - total_cost += tdelta - return_from_call(caller_stack, call_dict, total_cost) - else: - assert 0, "duh: %d" % what - - - # I have no idea, why sometimes the stack is not empty - we - # have to rewind the stack to get 100% for the root_caller - while caller_stack.size() > 1: - return_from_call(caller_stack, call_dict, total_cost) - - except IOError: - print "could not open inputfile '%s', ignore this." % inputfilename - except EOFError, m: - print "EOF: %s" % (m,) - return total_cost - -def pretty_name(file, function): - #pfile = os.path.splitext(os.path.basename(file)) [0] - #return "%s_[%s]" % (function, file) - return "%s" % function - #return "%s::%s" % (file, function) - #return "%s_%s" % (pfile, function) - -class TagWriter: - def __init__(self, output): - self.output = output - self.last_values = {} - - def clearTag(self, tag): - if self.last_values.has_key(tag): - del self.last_values[ tag ] - def clear(self): - self.last_values = {} - - def write(self, tag, value): - self.output.write("%s=%s\n" % (tag, value)) - #if (not self.last_values.has_key(tag)) or self.last_values[tag] != value: - # self.last_values[ tag ] = value - # self.output.write("%s=%s\n" % (tag, value)) - -def dumpResults(output, call_dict, total_cost, cost_per_pos, cost_per_function): - """write the collected results in the format kcachegrind - could read. - """ - # the intro - output.write("events: Tick\n") - output.write("summary: %d\n" % total_cost) - output.write("cmd: your python script\n") - output.write("\n") - tagwriter = TagWriter(output) - - # now the costs per line - for file in cost_per_pos.keys(): - func_dict = cost_per_pos[file] - for func in func_dict.keys(): - line_dict = func_dict[func] - tagwriter.write("ob", file) - tagwriter.write("fn", func)# pretty_name(file, func)) ; output.write("# ^--- 2\n") - tagwriter.write("fl", file) - for line in line_dict: - output.write("%d %d\n" %( line, line_dict[line] )) - - output.write("\n\n") - # now the function calls. For each caller all the called - # functions and their costs are written. - for file in call_dict.keys(): - per_file_dict = call_dict[file] - #print "file %s -> %s" % (file, per_file_dict) - for called_x in per_file_dict.keys(): - #print "called_x:",called_x - per_caller_dict = per_file_dict[called_x] - #print "called_x %s wird gerufen von: %s" % (called_x, per_caller_dict) - for caller_x in per_caller_dict.keys(): - tagwriter.write("ob", caller_x[0]) - tagwriter.write("fn", caller_x[2])# pretty_name(caller_x[2], caller_x[0])) ; output.write("# ^--- 1\n") - tagwriter.write("fl", caller_x[0]) - tagwriter.write("cob", file) - tagwriter.write("cfn", called_x) #pretty_name(file, called_x)) - tagwriter.write("cfl", file) - cost, count = per_caller_dict[caller_x] - #print "called_x:",called_x - output.write("calls=%d\n%d %d\n" % (count, caller_x[1], cost)) - tagwriter.clear() - #tagwriter.clearTag("cob") - # is it a bug in kcachegrind, that the "cob=xxx" line has - # to be rewritten after a calls entry with costline ? - #assert cost <= total_cost, "caller_x: %s, per_caller_dict: %s " % (caller_x, per_caller_dict, ) - #output.write("calls=%d\n%d %d\n" % (count, caller_x[1], cost)) - output.write("\n") - -def run_without_optparse(): - """parse the options without optparse, use sys.argv""" - if len(sys.argv) < 4 or sys.argv[1] != "-o" : - print "usage: hotshot2cachegrind -o outputfile in1 [in2 [in3 [...]]]" - return - outputfilename = sys.argv[2] - try: - output = file(outputfilename, "w") - args = sys.argv[3:] - convertProfFiles(output, args) - output.close() - except IOError: - print "could not open '%s' for writing." % outputfilename - -def run_with_optparse(): - """parse the options with optparse""" - - global file_limit - - versiontext = "%s version: %s" % ( progname, version.split()[1], ) - parser = OptionParser(version=versiontext) - parser.add_option("-o", "--output", - action="store", type="string", dest="outputfilename", - help="write output into FILE") - parser.add_option("--file-limit", - action="store", dest="file_limit", default=0, - help="stop after given number of input files") - output = sys.stdout - close_output = 0 - (options, args) = parser.parse_args() - file_limit = int(options.file_limit) - try: - if options.outputfilename and options.outputfilename != "-": - output = file(options.outputfilename, "w") - close_output = 1 - except IOError: - print "could not open '%s' for writing." % options.outputfilename - if output: - convertProfFiles(output, args) - if close_output: - output.close() - - -def profile_myself(): - import hotshot - filename = "self.prof" - if not os.path.exists(filename): - prof = hotshot.Profile(filename, lineevents=1) - prof.runcall(run) - prof.close() - else: - print "not profiling myself, since '%s' exists, running normal" % filename - run() - -# check if optparse is available. -try: - from optparse import OptionParser - run = run_with_optparse -except ImportError: - run = run_without_optparse - -if __name__ == "__main__": - try: - run() - #profile_myself() - except KeyboardInterrupt: - sys.exit(1) diff --git a/converters/hotshot2calltree.cmake b/converters/hotshot2calltree.cmake new file mode 100644 index 0000000..c2dcc17 --- /dev/null +++ b/converters/hotshot2calltree.cmake @@ -0,0 +1,394 @@ +#!/usr/bin/env python +# _*_ coding: latin1 _*_ + +# +# Copyright (c) 2003 by WEB.DE, Karlsruhe +# Autor: Jörg Beyer +# +# hotshot2cachegrind is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# +# +# This script transforms the pstat output of the hotshot +# python profiler into the input of kcachegrind. +# +# example usage: +# modify you python script to run this code: +# +# import hotshot +# filename = "pythongrind.prof" +# prof = hotshot.Profile(filename, lineevents=1) +# prof.runcall(run) # assuming that "run" should be called. +# prof.close() +# +# it will run the "run"-method under profiling and write +# the results in a file, called "pythongrind.prof". +# +# then call this script: +# hotshot2cachegrind -o +# or here: +# hotshot2cachegrind cachegrind.out.0 pythongrind.prof +# +# then call kcachegrind: +# kcachegrind cachegrind.out.0 +# +# TODO: +# * es gibt Probleme mit rekursiven (direkt und indirekt) Aufrufen - dann +# stimmen die Kosten nicht. +# +# * einige Funktionen werden mit "?" als Name angezeigt. Evtl sind +# das nur die C/C++ extensions. +# +# * es fehlt noch ein Funktionsnamen Mangling, dass die Filenamen berücksichtigt, +# zZ sind alle __init__'s und alle run's schwer unterscheidbar :-( +# +version = "Version ${KCACHEGRIND_VERSION}" +progname = "hotshot2cachegrind" + +import os, sys +from hotshot import stats,log +import os.path + +file_limit=0 + +what2text = { + log.WHAT_ADD_INFO : "ADD_INFO", + log.WHAT_DEFINE_FUNC : "DEFINE_FUNC", + log.WHAT_DEFINE_FILE : "DEFINE_FILE", + log.WHAT_LINENO : "LINENO", + log.WHAT_EXIT : "EXIT", + log.WHAT_ENTER : "ENTER"} + +# a pseudo caller on the caller stack. This represents +# the Python interpreter that executes the given python +# code. +root_caller = ("PythonInterpreter",0,"execute") + +class CallStack: + """A tiny Stack implementation, based on python lists""" + def __init__(self): + self.stack = [] + self.recursion_counter = {} + def push(self, elem): + """put something on the stack""" + self.stack.append(elem) + rc = self.recursion_counter.get(elem, 0) + self.recursion_counter[elem] = rc + 1 + + def pop(self): + """get the head element of the stack and remove it from the stack""" + elem = self.stack[-1:][0] + rc = self.recursion_counter.get(elem) - 1 + if rc>0: + self.recursion_counter[elem] = rc + else: + del self.recursion_counter[elem] + return self.stack.pop() + + def top(self): + """get the head element of the stack, stack is unchanged.""" + return self.stack[-1:][0] + def handleLineCost(self, tdelta): + p, c = self.stack.pop() + self.stack.append( (p,c + tdelta) ) + def size(self): + """ return how many elements the stack has""" + return len(self.stack) + + def __str__(self): + return "[stack: %s]" % self.stack + + def recursion(self, pos): + return self.recursion_counter.get(pos, 0) + #return self.recursion_dict.has_key((entry[0][0], entry[0][2])) + +def return_from_call(caller_stack, call_dict, cost_now): + """return from a function call + remove the function from the caller stack, + add the costs to the calling function. + """ + called, cost_at_enter = caller_stack.pop() + caller, caller_cost = caller_stack.top() + + #print "return_from_call: %s ruft %s" % (caller, called,) + + per_file_dict = call_dict.get(called[0], {}) + per_caller_dict = per_file_dict.get(called[2], {}) + cost_so_far, call_counter = per_caller_dict.get(caller, (0, 0)) + + if caller_stack.recursion(called): + per_caller_dict[caller] = (cost_so_far, call_counter + 1) + else: + per_caller_dict[caller] = (cost_so_far + cost_now - cost_at_enter, call_counter + 1) + + per_file_dict[called[2]] = per_caller_dict + call_dict[called[0]] = per_file_dict + + +def updateStatus(filecount): + sys.stdout.write("reading File #%d \r" % filecount) + sys.stdout.flush() +def convertProfFiles(output, inputfilenames): + """convert all the given input files into one kcachegrind + input file. + """ + call_dict = {} + cost_per_pos = {} + cost_per_function = {} + caller_stack = CallStack() + caller_stack.push((root_caller, 0)) + + total_cost = 0 + filecount = 1 + number_of_files = len(inputfilenames) + for inputfilename in inputfilenames: + updateStatus(filecount) + cost, filecount = convertHandleFilename(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount) + total_cost += cost + if (file_limit > 0) and (filecount > file_limit): + break + + print + print "total_cost: % d Ticks",total_cost + dumpResults(output, call_dict, total_cost, cost_per_pos, cost_per_function) + +def convertHandleFilename(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount): + updateStatus(filecount) + if not ((file_limit > 0) and (filecount > file_limit)): + if os.path.isdir(inputfilename): + cost, filecount = convertProfDir(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount) + elif os.path.isfile(inputfilename): + cost = convertProfFile(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function) + filecount += 1 + else: + sys.stderr.write("warn: ignoring '%s', is no file and no directory\n" % inputfilename) + cost = 0 + return (cost, filecount) + +def convertProfDir(start, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount): + cost = 0 + filenames = os.listdir(start) + for f in filenames: + if (file_limit > 0) and (filecount > file_limit): + break + full = os.path.join(start, f) + c, filecount = convertHandleFilename(full, caller_stack, call_dict, cost_per_pos, cost_per_function, filecount) + cost += c; + return (cost, filecount) + +def handleCostPerPos(cost_per_pos, pos, current_cost): + """ + the cost per source position are managed in a dict in a dict. + + the cost are handled per file and there per function. + so, the per-file-dict contains some per-function-dicts + which sum up the cost per line (in this function and in + this file). + """ + filename = pos[0] + lineno = pos[1] + funcname = pos[2] + file_dict = cost_per_pos.get(filename, {}) + func_dict = file_dict.get(funcname, {}) + func_dict.setdefault(lineno, 0) + func_dict[lineno] += current_cost + file_dict[funcname] = func_dict + cost_per_pos[filename] = file_dict + +def convertProfFile(inputfilename, caller_stack, call_dict, cost_per_pos, cost_per_function): + """convert a single input file into one kcachegrind + data. + + this is the most expensive function in this python source :-) + """ + + total_cost = 0 + try: + logreader = log.LogReader(inputfilename) + current_cost = 0 + hc = handleCostPerPos # shortcut + for item in logreader: + what, pos ,tdelta = item + (file, lineno, func) = pos + #line = "%s %s %d %s %d" % (what2text[what], file, lineno, func, tdelta) + #print line + # most common cases first + if what == log.WHAT_LINENO: + # add the current cost to the current function + hc(cost_per_pos, pos, tdelta) + total_cost += tdelta + elif what == log.WHAT_ENTER: + caller_stack.push((pos, total_cost)) + hc(cost_per_pos, pos, tdelta) + total_cost += tdelta + elif what == log.WHAT_EXIT: + hc(cost_per_pos, pos, tdelta) + total_cost += tdelta + return_from_call(caller_stack, call_dict, total_cost) + else: + assert 0, "duh: %d" % what + + + # I have no idea, why sometimes the stack is not empty - we + # have to rewind the stack to get 100% for the root_caller + while caller_stack.size() > 1: + return_from_call(caller_stack, call_dict, total_cost) + + except IOError: + print "could not open inputfile '%s', ignore this." % inputfilename + except EOFError, m: + print "EOF: %s" % (m,) + return total_cost + +def pretty_name(file, function): + #pfile = os.path.splitext(os.path.basename(file)) [0] + #return "%s_[%s]" % (function, file) + return "%s" % function + #return "%s::%s" % (file, function) + #return "%s_%s" % (pfile, function) + +class TagWriter: + def __init__(self, output): + self.output = output + self.last_values = {} + + def clearTag(self, tag): + if self.last_values.has_key(tag): + del self.last_values[ tag ] + def clear(self): + self.last_values = {} + + def write(self, tag, value): + self.output.write("%s=%s\n" % (tag, value)) + #if (not self.last_values.has_key(tag)) or self.last_values[tag] != value: + # self.last_values[ tag ] = value + # self.output.write("%s=%s\n" % (tag, value)) + +def dumpResults(output, call_dict, total_cost, cost_per_pos, cost_per_function): + """write the collected results in the format kcachegrind + could read. + """ + # the intro + output.write("events: Tick\n") + output.write("summary: %d\n" % total_cost) + output.write("cmd: your python script\n") + output.write("\n") + tagwriter = TagWriter(output) + + # now the costs per line + for file in cost_per_pos.keys(): + func_dict = cost_per_pos[file] + for func in func_dict.keys(): + line_dict = func_dict[func] + tagwriter.write("ob", file) + tagwriter.write("fn", func)# pretty_name(file, func)) ; output.write("# ^--- 2\n") + tagwriter.write("fl", file) + for line in line_dict: + output.write("%d %d\n" %( line, line_dict[line] )) + + output.write("\n\n") + # now the function calls. For each caller all the called + # functions and their costs are written. + for file in call_dict.keys(): + per_file_dict = call_dict[file] + #print "file %s -> %s" % (file, per_file_dict) + for called_x in per_file_dict.keys(): + #print "called_x:",called_x + per_caller_dict = per_file_dict[called_x] + #print "called_x %s wird gerufen von: %s" % (called_x, per_caller_dict) + for caller_x in per_caller_dict.keys(): + tagwriter.write("ob", caller_x[0]) + tagwriter.write("fn", caller_x[2])# pretty_name(caller_x[2], caller_x[0])) ; output.write("# ^--- 1\n") + tagwriter.write("fl", caller_x[0]) + tagwriter.write("cob", file) + tagwriter.write("cfn", called_x) #pretty_name(file, called_x)) + tagwriter.write("cfl", file) + cost, count = per_caller_dict[caller_x] + #print "called_x:",called_x + output.write("calls=%d\n%d %d\n" % (count, caller_x[1], cost)) + tagwriter.clear() + #tagwriter.clearTag("cob") + # is it a bug in kcachegrind, that the "cob=xxx" line has + # to be rewritten after a calls entry with costline ? + #assert cost <= total_cost, "caller_x: %s, per_caller_dict: %s " % (caller_x, per_caller_dict, ) + #output.write("calls=%d\n%d %d\n" % (count, caller_x[1], cost)) + output.write("\n") + +def run_without_optparse(): + """parse the options without optparse, use sys.argv""" + if len(sys.argv) < 4 or sys.argv[1] != "-o" : + print "usage: hotshot2cachegrind -o outputfile in1 [in2 [in3 [...]]]" + return + outputfilename = sys.argv[2] + try: + output = file(outputfilename, "w") + args = sys.argv[3:] + convertProfFiles(output, args) + output.close() + except IOError: + print "could not open '%s' for writing." % outputfilename + +def run_with_optparse(): + """parse the options with optparse""" + + global file_limit + + versiontext = "%s version: %s" % ( progname, version.split()[1], ) + parser = OptionParser(version=versiontext) + parser.add_option("-o", "--output", + action="store", type="string", dest="outputfilename", + help="write output into FILE") + parser.add_option("--file-limit", + action="store", dest="file_limit", default=0, + help="stop after given number of input files") + output = sys.stdout + close_output = 0 + (options, args) = parser.parse_args() + file_limit = int(options.file_limit) + try: + if options.outputfilename and options.outputfilename != "-": + output = file(options.outputfilename, "w") + close_output = 1 + except IOError: + print "could not open '%s' for writing." % options.outputfilename + if output: + convertProfFiles(output, args) + if close_output: + output.close() + + +def profile_myself(): + import hotshot + filename = "self.prof" + if not os.path.exists(filename): + prof = hotshot.Profile(filename, lineevents=1) + prof.runcall(run) + prof.close() + else: + print "not profiling myself, since '%s' exists, running normal" % filename + run() + +# check if optparse is available. +try: + from optparse import OptionParser + run = run_with_optparse +except ImportError: + run = run_without_optparse + +if __name__ == "__main__": + try: + run() + #profile_myself() + except KeyboardInterrupt: + sys.exit(1)