call graph using python and cscope

Roland Puntaier Roland.Puntaier at br-automation.com
Wed Jan 10 10:55:21 EST 2007


""" 
Sometimes it is nice to have the data used by cscope accessible in a 
programatic way.
The following python script extract the "functions called" information 
from cscope (function: callGraph)
and produced an html file from them.

  from csCallGraph import *
  acg=callGraph(entryFun,workingDir)

entryFun is the function to start with (e.g. main)
workingDir is the directory where cscope.out is located

As a script it can be called like:
  csCallGraph main > myprogram.html
"""

import subprocess , os, sys

def functionsCalled(entryFun,workingDir):
  cmd = "cscope -d -l -L -2%s"%entryFun
  process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, 
cwd=workingDir) 
  csoutput= process.stdout.read() 
  del process
  cslines=[arr.strip().split(' ') for arr in csoutput.split('\n') if 
len(arr.split(' '))>1]
  funsCalled={}
  for fl in cslines:
    if funsCalled.has_key(fl[0]):
      funsCalled[fl[0]]|=set([fl[1]])
    else:
      funsCalled[fl[0]]=set([fl[1]])
  allFuns=set(map(lambda x:x[1],cslines))
  return (allFuns,funsCalled)

def callGraph(entryFun,workingDir,cg={}):
  if not cg.has_key(entryFun): 
    allFuns,funsCalled=functionsCalled(entryFun,workingDir)
    cg[entryFun]=funsCalled
    for af in allFuns:
      cg=callGraph(af,workingDir,cg)
  return cg

def textCallGraph(acg):
  innerFuns=[(f,d,len(reduce(lambda x,y:x|y,d.values()))) for f,d in 
acg.items() if len(d)>0 ]
  leafFuns=[(f,d,0) for f,d in acg.items() if not len(d)>0 ]
  innerFuns.sort(lambda x,y: y[2]-x[2])
  innerLen=len(innerFuns)
  leafLen=len(leafFuns)
  title=lambda aFun: '\n' + aFun + '\n' + '-'*len(aFun)
  def ff(aFun,funsCalled):
    fileFuns=zip(funsCalled.keys(),['    '+',\n '.join(funsCalledInFile) 
for funsCalledInFile in funsCalled.values()])
    funIn=lambda f: '\n%s in '%f
    return title(aFun) + funIn(aFun) + funIn(aFun).join(map(lambda 
x:'%s:\n%s'%(x[0],x[1]),fileFuns))
  strInner='\n'.join([ff(f[0],f[1]) for f in innerFuns])
  strLeaves='\n'.join(map(lambda x:title(x[0]),leafFuns))
  return strInner+'\n'+strLeaves

def funWeights(acg):
  funWeights=dict([(f,reduce(lambda x,y:x|y,d.values())) for f,d in 
acg.items() if len(d)>0 ]+
      [(f,[]) for f,d in acg.items() if not len(d)>0 ])
  weights={}
  def calcWeights(af):
    if not weights.has_key(af):
      subFuns=funWeights[af]
      weights[af]=1
      for f in subFuns:
        calcWeights(f)
        weights[af]+=weights[f]
  for af in funWeights.keys(): calcWeights(af)
  return weights

def htmlCallGraph(acg):
  funW=funWeights(acg)
  innerFuns=[(f,d,funW[f]) for f,d in acg.items() if len(d)>0 ]
  leafFuns=[(f,d,0) for f,d in acg.items() if not len(d)>0 ]
  #innerFuns.sort(lambda x,y: y[2]-x[2]))
  def cfun(a,b):
    if b > a:
      return 1
    elif b < a:
      return -1
    return 0
  innerFuns.sort(lambda x,y: cfun(x[2],y[2]))
  innerLen=len(innerFuns)
  leafLen=len(leafFuns)
  funDict=dict(zip(map(lambda x:x[0],innerFuns)+map(lambda 
x:x[0],leafFuns),range(innerLen+leafLen)))
  title=lambda aFun: '<hr><h4><a name=#f%i'%funDict[aFun]+'>' + aFun + ' 
(%i)'%funW[aFun] + '</a></h4>\n'
  def ff(aFun,funsCalled):
    fun=lambda y:'<a href=#f%i'%funDict[y]+' 
style="text-decoration:none">'+y+'</a>'
    fileFuns=zip(funsCalled.keys(),[',\n'.join(map(fun,funsCalledInFile)) 
for funsCalledInFile in funsCalled.values()])
    funIn=lambda f: '<br><em>%s</em> in '%f
    return title(aFun) + funIn(aFun) + funIn(aFun).join(map(lambda 
x:'%s:\n%s'%(x[0],x[1]),fileFuns))
  strInner='\n'.join([ff(f[0],f[1]) for f in innerFuns])
  strLeaves='\n'.join(map(lambda x:title(x[0]),leafFuns))
  return '<html>\n<body>\n'+strInner+'\n'+strLeaves+"</body>\n</html>\n"

if __name__ == '__main__':
  if len(sys.argv) < 2:
      print 'Usage: csGragh.py entryFunction'
      sys.exit()
  entryFun=sys.argv[1]
  workingDir=os.getcwd()
  acg=callGraph(entryFun,workingDir)
  print htmlCallGraph(acg)



More information about the Python-list mailing list