#!/bin/rc # fcalls: print static call graph # see also: calls(1) rfork e flagfmt='c,n,f function,m maxdepth' args='[file ...]' maxdepth=64 noexternal=0 root='' showcallers=0 fn usage{ aux/usage exit usage } fn isnum{ echo $1 | grep -s '^[0-9]+$' } if(! ifs=() eval `{aux/getflags $*}) usage if(~ $flagn 1) noexternal=1 if(~ $flagc 1) showcallers=1 if(~ $#flagf 1) root=$flagf if(~ $#flagm 1){ if(! isnum $flagm) usage maxdepth=$flagm } if(~ $#* 0) *=/fd/0 { for(i) 6c -S -o /dev/null $i } | awk ' BEGIN { maxdepth = '$maxdepth' noexternal = '$noexternal' root = "'$root'" showcallers = '$showcallers' } $1 == "TEXT" { f = $2 sub("<>.*", "", f) sub("\\+.*", "", f) delete seen internal[f]++ } $1 == "CALL" { c = $2 sub("^,", "", c) sub("<>.*", "", c) sub("\\+.*", "", c) if(seen[c]) next seen[c]++ calls[f] = calls[f] " " c callers[c] = callers[c] " " f } END { if(root){ if(showcallers){ n = split(callers[root], list) for(i = 1; i <= n; i++) print list[i] }else printcalls(root, 0) }else for(f in calls) printcalls(f, 0) } function printcalls(f, depth, visited, list, i, n){ ext = !internal[f] if(ext && noexternal) return for(i = 0; i < depth; i++) printf("\t") printf("%s", f) if(ext){ print " *" return } if(visited[f]){ print " ..." return } print "" if(depth == maxdepth){ printf("maxdepth=%d reached!\n", maxdepth) return } n = split(calls[f], list) if(n == 0) return if(depth == 0) delete visited visited[f]++ for(i = 1; i <= n; i++) printcalls(list[i], depth+1, visited) delete visited[f] } '