HvD: Adding the gprof2tree tool. I think it is particularly useful for quickly

finding your way around pieces of large codes like NWChem. I hope others also
find it useful.
This commit is contained in:
Huub Van Dam 2015-08-24 19:20:38 +00:00
parent 96f9cdef5d
commit 48bc5058c8
3 changed files with 504 additions and 0 deletions

Binary file not shown.

View file

@ -0,0 +1,43 @@
gprof2tree
==========
Author: Huub van Dam
Web: http://www.cse.scitech.ac.uk/ccg/software/gprof2tree/ [August 24, 2015]
Introduction
------------
In large scale code it takes a considerable effort to find your way around.
In addition tracing how the execution path change if particular directives
are added to the input is not easy. Yet in order to enhance a code knowing
what parts are involved in implementing a particular option is essential.
This tool addresses this issue by taking the output from the profiler gprof
and prints the call tree of that part of the program that was executed. In
particular the subroutine invocations at every level of the call tree can be
sorted. This way a diff program can be used to compare the call trees of the
same program run with different input options, highlighting the particular
parts of the code that are involved in implementing a particular option.
Alternatively it provides a quick way to get an overview of where particular
routines are used in a code.
Limitations
-----------
Important in examining call trees is the ability to prune the trees eliminating
all the shrubbery that you are not interested in. Currently you need to specify
every routine you want to suppress, there is no facility to use regular
expressions. I.e. if you are not interested in the calls to the RTDB library
you currently cannot specify that you want to stop at "rtdb_*". Instead you
have to list every rtdb_ function separately. The only help in this case is that
you can list all the rtdb_ functions in one rtdb related file, and create
similar files for each other module. You can then specify the list of files
related to all the modules that you are not interested in with the --stop-at
flag. However, this still is relatively painful compared to using regular
expressions.
More information
----------------
See the Gprof2tree.pdf file.

461
contrib/gprof2tree/gprof2tree Executable file
View file

@ -0,0 +1,461 @@
#!/usr/bin/python
import string
import sys
def find_call_tree(fp):
"""Skips the gprof output up to the point where the call tree
information starts"""
line = fp.readline()
while line and (line[0:7] != "index %" and line[0:8] != "index %"):
line = fp.readline()
if line[0:8] == "index %":
line = fp.readline()
line = fp.readline()
def parse_call_tree(fp,parents,children,times):
"""Parse the call tree information from the gprof output file and store
it in three dictionaries:
- parents: contains the routines that call a subroutine
- children: contains the routines called by a subroutine
- times: contains cpu-time,call-counts tuples for every subroutine,
child tuple."""
parse_parents = 1
parse_children = 2
mode = parse_parents
slist = []
line = fp.readline()
while line:
if len(line) <= 1:
#
# empty line: nothing to do but get next line
#
pass
#
elif line[0] == "\f":
#
# end of table found, we are done
#
return
#
elif line[0:11] == " This table":
#
# end of table found, we are done
#
return
#
elif string.find(line,"<spontaneous>") > -1:
#
# found "spontaneous" parent, add it to the current subroutine list
#
slist.append("<spontaneous>")
#
elif line[0] == " ":
#
# Found a parent or a child, the line may come in two different
# formats:
#
# 1. listing the fields: self, children, called, name
# 2. listing the fields: called, name (if recursive)
#
# E.g.:
#
# 0.00 0.00 1/1 .main [45]
# 49 .fac1 <cycle 1> [3]
#
tlist = string.split(line,None,3)
if string.find(tlist[0],".") == -1:
#
# this is a type 2 line, so resplit it accordingly
#
tlist = string.split(line,None,1)
tcput = 0.0
tcalled = tlist[0]
tname = tlist[1]
else:
#
# this is a type 1 line
#
tcput = tlist[1]
tcalled = tlist[2]
tname = tlist[3]
#
# Now strip off the index number at the end of the name
#
i = string.rfind(tname," [")
tname = tname[:i]
#
# Now strip the potential "<cycle xxx>" off
#
i = string.rfind(tname," <cycle ")
if i > -1:
tname = tname[:i]
#
# Now the name is the pure function/subroutine name
#
slist.append(tname)
if mode == parse_children:
times[(subrname,tname)] = (tcput,tcalled)
#
elif line[0] == "[":
#
# Now we have found the function/subroutine itself
# this line comes in the format:
#
# index %time self children called name
#
# E.g.:
#
# [1] 35.3 0.00 1.18 1 .dl_poly [1]
#
tlist = string.split(line,None,5)
tcput = tlist[3]
tcalled = tlist[4]
tname = tlist[5]
#
# Now strip off the index number at the end of the name
#
i = string.rfind(tname," [")
tname = tname[:i]
#
# Now strip the potential "<cycle xxx>" off
#
i = string.rfind(tname," <cycle ")
if i > -1:
tname = tname[:i]
#
subrname = tname
parents[subrname] = slist
slist = []
mode = parse_children
#
elif line[0] == "-":
#
# found the line that terminates the children section
# so switch mode to prepare for another parents section
#
children[subrname] = slist
slist = []
mode = parse_parents
#
else:
#
# I am not sure about this...
#
print "Do not know how to interpret the following line:"
print line
print "Giving up..."
sys.exit(2)
#
line = fp.readline()
def sort_call_tree(treedata):
"""Given some call tree this routine will sort the related items
(usually parents or children). This way a particular order can
be garanteed irrespective of the amount of CPU time a subroutine
consumes."""
for name in treedata:
treedata[name].sort()
def exclude_call_tree(filenames,treedata):
"""Given a number of files this routine reads the files and deletes
any names listed there from the treedata."""
for filename in filenames:
fp = open(filename,"r")
line = fp.readline()
while line:
if line[0] != "#":
slist = string.split(line)
if len(slist) > 0:
key = slist[0]
if treedata.has_key(key):
del treedata[key]
line = fp.readline()
fp.close()
def stopat_call_tree(filenames,treedata):
"""Given a number of files this routine reads the files and deletes
any children or parents of the routines listed there from the
treedata."""
for filename in filenames:
fp = open(filename,"r")
line = fp.readline()
while line:
if line[0] != "#":
slist = string.split(line)
if len(slist) > 0:
key = slist[0]
if treedata.has_key(key):
treedata[key] = []
line = fp.readline()
fp.close()
def write_call_tree(fp,indent,subrname,treedata,stack,once):
"""Given a subroutine name this routine recursively prints out its
call tree. Before printing the subroutine name it is checked that the
name actually appears in the treedata dictionary. If it does not exist
then that probably means the routine got deleted.
Finally there is the option of printing the subtree related to a
subroutine only once. This is arranged in a way similar to the
'stopat' approach by deleting all the children."""
if treedata.has_key(subrname):
if stack.count(subrname) == 2:
fp.write(indent+subrname+" (recursive)\n")
else:
fp.write(indent+subrname+"\n")
slist = treedata[subrname]
for name in slist:
if stack.count(name) < 2:
stack.append(name)
write_call_tree(fp,indent+" ",name,treedata,stack,once)
name = stack.pop()
if once:
treedata[subrname] = []
def write_call_tree_level(fp,indent,subrname,treedata,stack,level,once):
"""Given a subroutine name this routine recursively prints out its
call tree. Before printing the subroutine name it is checked that the
name actually appears in the treedata dictionary. If it does not exist
then that probably means the routine got deleted.
Finally there is the option of printing the subtree related to a
subroutine only once. This is arranged in a way similar to the
'stopat' approach by deleting all the children."""
if treedata.has_key(subrname):
if stack.count(subrname) == 2:
fp.write("%3d %s\n" % (level,indent+subrname+" (recursive)"))
else:
fp.write("%3d %s\n" % (level,indent+subrname))
slist = treedata[subrname]
for name in slist:
if stack.count(name) < 2:
stack.append(name)
write_call_tree_level(fp,indent+" ",name,treedata,stack,level+1,once)
name = stack.pop()
if once:
treedata[subrname] = []
def search_to_call_tree(fp,subrname,tolist,treedata,stack,minlevel,curlevel):
"""Given a subroutine name this routine recursively searches for parents
or children that a included in the tolist. If a routine on the tolist
is found then the call stack is printed."""
if treedata.has_key(subrname):
stack.append(subrname)
if tolist.count(subrname) >= 1:
print_to_call_tree(fp,stack,minlevel)
minlevel = curlevel+1
if stack.count(subrname) < 2:
slist = treedata[subrname]
for name in slist:
minlevel = search_to_call_tree(fp,name,tolist,treedata,stack,minlevel,curlevel+1)
if curlevel < minlevel:
minlevel = curlevel
name = stack.pop()
return minlevel
def print_to_call_tree(fp,stack,minlevel):
"""Print the call stack from minlevel down"""
indent = ""
for i in range(0,len(stack)):
if i >= minlevel:
if stack.count(stack[i]) == 2:
fp.write(indent+stack[i]+" (recursive)\n")
else:
fp.write(indent+stack[i]+"\n")
indent = indent + " "
def print_subroutine_name(fp,name):
"""Print what subroutine we are looking for."""
fp.write("Printing call tree for function/subroutine: "+name+"\n")
def check_subroutine_name(fp,name,dict):
"""Check that the subroutine we are looking for actually exists.
If not report back to the user and exit."""
if dict.has_key(name):
pass
else:
fp.write("Function/subroutine: "+name+" not found.\n")
fp.write("Please check your gprof output file.\n")
sys.exit(3)
def version_gprof2tree(fp):
"""Print out the version information."""
revision = "$Revision: 1.4 $"
date = "$Date: 2006/03/16 15:36:03 $"
rlist = string.split(revision)
dlist = string.split(date)
version = "gprof2tree version "+rlist[1]+" of "+dlist[1]+"\n"
fp.write(version)
def usage_gprof2tree(fp):
"""Print out the usage information."""
fp.write("""
Usage:
gprof2tree [--parents] [--children] [--[no]sort] [--once|--always]
[--exclude "<file1>[:<file2>[:<file3>]]"] [--help]
[--stopat "<file4>[:<file5>[:<file6>]]"] [--version]
[--to "<routine name1>[ <routine name2>[ <routine name3>]]"]
<subroutine name>
This script reads the call tree data from a gprof output and prints out
the call tree of the routine <subroutine name>. The following options
can be used
--help Print this information.
--parents Print the call moving upwards. I.e. print the routines that
call <subroutine name>.
--children Print the call moving downwards. I.e. print the routines that
routine <subroutine name> calls. This is the default.
--sort Alphabetically sort the routine names in the call tree.
This is useful if the call trees of slighty different
runs have to be compared as it reduces the differences
introduced by changes of CPU usage.
--nosort Print the routines in the call tree in the same order as
found in the gprof call tree information. Usually this means
that the routines with the highest CPU usage come first.
This is the default.
--once Print the subtree for every subroutine only once, i.e.
the first time it is encountered. In subsequent encounters
only the call is printed. This helps to greatly reduce the
length of the printed call trees and is therefore the
default.
--always Always print the tree of every subroutine encountered
limited only by the 'stopat' and 'exclude' options in effect.
--exclude Removes all the routines that are named in the specified
files from the call tree. This is useful in larger codes
where there can a lot of routines that are of no particular
interest.
--stopat Removes the call tree information of the routines that
are named in the specified files. The routine names themselves
are still printed in the call trees but not the routines
they call. This is particularly useful in larger codes which
may have deep call trees. In which one may want to know where
a routine is called but not how it goes about doing its job.
--to Prints only the paths throught the code that lead from
the desired subroutine to the routines named in the to-list.
All other paths are suppressed.
--level Print the depth level of a routine in the call tree on
every line.
--version Print the version number of gprof2tree.
Copyright 2004-2006, Huub van Dam, CCLRC Daresbury Laboratory
\n""")
sys.exit(1)
parents = {}
parents["<spontaneous>"] = []
children = {}
children["<spontaneous>"] = []
times = {}
stack = []
tolist = []
true = (0 == 0)
false = not true
narg = len(sys.argv)
iarg = 1
osort = false
oparent = false
ochild = true
oexclude = false
ostopat = false
oto = false
olevel = false
oonce = true
subrname = " "
minlevel = 0
while iarg < narg:
if sys.argv[iarg] == "--parents":
oparent = true
ochild = false
iarg = iarg + 1
elif sys.argv[iarg] == "--children":
ochild = true
oparent = false
iarg = iarg + 1
elif sys.argv[iarg] == "--sort":
osort = true
iarg = iarg + 1
elif sys.argv[iarg] == "--nosort":
osort = false
iarg = iarg + 1
elif sys.argv[iarg] == "--once":
oonce = true
iarg = iarg + 1
elif sys.argv[iarg] == "--always":
oonce = false
iarg = iarg + 1
elif sys.argv[iarg] == "--exclude":
oexclude = true
excludefiles = string.split(sys.argv[iarg+1],":")
iarg = iarg + 2
elif sys.argv[iarg] == "--stopat":
ostopat = true
stopatfiles = string.split(sys.argv[iarg+1],":")
iarg = iarg + 2
elif sys.argv[iarg] == "--to":
oto = true
tolist = string.split(sys.argv[iarg+1]," ")
iarg = iarg + 2
elif sys.argv[iarg] == "--level":
olevel = true
iarg = iarg + 1
elif sys.argv[iarg] == "--version":
version_gprof2tree(sys.stdout)
iarg = iarg + 1
elif sys.argv[iarg] == "--help":
usage_gprof2tree(sys.stdout)
iarg = iarg + 1
else:
subrname = str(sys.argv[iarg])
iarg = iarg + 1
if subrname == " ":
usage_gprof2tree(sys.stdout)
print_subroutine_name(sys.stderr,subrname)
find_call_tree(sys.stdin)
parse_call_tree(sys.stdin,parents,children,times)
check_subroutine_name(sys.stderr,subrname,children)
if ochild:
if oexclude:
exclude_call_tree(excludefiles,children)
if ostopat:
stopat_call_tree(stopatfiles,children)
if osort:
sort_call_tree(children)
if oto:
minlevel = search_to_call_tree(sys.stdout,subrname,tolist,children,stack,minlevel,0)
else:
if olevel:
write_call_tree_level(sys.stdout,"",subrname,children,stack,1,oonce)
else:
write_call_tree(sys.stdout,"",subrname,children,stack,oonce)
if oparent:
if oexclude:
exclude_call_tree(excludefiles,parents)
if ostopat:
stopat_call_tree(stopatfiles,parents)
if osort:
sort_call_tree(parents)
if oto:
minlevel = search_to_call_tree(sys.stdout,subrname,tolist,parents,stack,minlevel,0)
else:
if olevel:
write_call_tree_level(sys.stdout,"",subrname,parents,stack,1,oonce)
else:
write_call_tree(sys.stdout,"",subrname,parents,stack,oonce)