diff options
Diffstat (limited to 'extra/kdesdk-kcachegrind')
-rw-r--r-- | extra/kdesdk-kcachegrind/PKGBUILD | 19 | ||||
-rw-r--r-- | extra/kdesdk-kcachegrind/kdebug-331715.patch | 827 |
2 files changed, 841 insertions, 5 deletions
diff --git a/extra/kdesdk-kcachegrind/PKGBUILD b/extra/kdesdk-kcachegrind/PKGBUILD index fa3971594..4ff1a9e3d 100644 --- a/extra/kdesdk-kcachegrind/PKGBUILD +++ b/extra/kdesdk-kcachegrind/PKGBUILD @@ -1,8 +1,8 @@ -# $Id: PKGBUILD 205772 2014-02-10 10:59:46Z andrea $ +# $Id: PKGBUILD 207070 2014-03-06 12:07:56Z andrea $ # Maintainer: Andrea Scarpino <andrea@archlinux.org> pkgname=kdesdk-kcachegrind -pkgver=4.12.2 +pkgver=4.12.3 pkgrel=1 pkgdesc='Visualization of Performance Profiling Data' url='http://kde.org/applications/development/kcachegrind/' @@ -13,11 +13,20 @@ depends=('kdebase-runtime' 'python2') makedepends=('cmake' 'automoc4') optdepends=('php: PHP support') install=${pkgname}.install -source=("http://download.kde.org/stable/${pkgver}/src/kcachegrind-${pkgver}.tar.xz") -sha1sums=('d5bd15a5b23085b6fa9f5996dcdbd2844fdb59c1') +source=("http://download.kde.org/stable/${pkgver}/src/kcachegrind-${pkgver}.tar.xz" + 'kdebug-331715.patch') +sha1sums=('0e6506bb806508ff9c8100f7696c7464fa865d1b' + '3e7f4a265079c509aa08aab92a7ceaf6208353b3') -build() { +prepare() { mkdir build + + # KDEBUG#331715 + cd kcachegrind-${pkgver} + patch -p1 -i "${srcdir}"/kdebug-331715.patch +} + +build() { cd build cmake ../kcachegrind-${pkgver} \ -DCMAKE_BUILD_TYPE=Release \ diff --git a/extra/kdesdk-kcachegrind/kdebug-331715.patch b/extra/kdesdk-kcachegrind/kdebug-331715.patch new file mode 100644 index 000000000..ec3ae93d5 --- /dev/null +++ b/extra/kdesdk-kcachegrind/kdebug-331715.patch @@ -0,0 +1,827 @@ +commit 2858c560b2b5af8a9fa4bd9692a306b5edfe264f +Author: Josef Weidendorfer <Josef.Weidendorfer@gmx.de> +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 <job@webde-ag.de> +-# +-# 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 <output> <input> +-# 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 <job@webde-ag.de> ++# ++# 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 <output> <input> ++# 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) |