如果问是Java的话
大体来讲,如果要静态callgraph,你只需要读入程序,解析其中的invoke指令即可。很多静态工具有提供这样的功能,譬如前面说的doxygen等简单的、或者soot等更加sophisticated的静态工具。如果是C或者C++程序的话,LLVM opt也提供这样的工具。但是对于Java来说静态的call graph是非常不准确的。因为dynamic class loading,理论上说只有invokespecial和invokestatic可以静态确定调用对象,invokevirtual或者interface的话你只能进行估计,譬如Soot对于virtual call能够给你一个可能的被调用的对象的集合,但是这个集合是不完备的(譬如动态可以加载一个什么类subclass了当前某个类,然后重载了该方法)。
如果要动态(运行时)callgraph,就势必要对程序进行instrument,在每次方法调用前后执行你自定义的routine。soot或者ASM等字节码操作工具可以让你方便地instrument,当然你可以直接修改Java虚拟机,譬如开Xint模式修改解释器。如果是C或者C++,你可以用Intel Pin,dynamorio等,或者用硬件计数器(譬如Intel Haswell系列的last branch record就可以配置为stack profile模式然后记录最近的branch,用这个也可以生成callgraph,虽然不十分准确,但是low overhead,perf就可以利用这个功能来生成callgraph)。
这些工具可以生成一个函数调用的文本文件,但是你还需要一个工具将这个文件转化为图片,这一步一般都用graphviz dot。