Git Product home page Git Product logo

koknat / callgraph Goto Github PK

View Code? Open in Web Editor NEW
221.0 7.0 27.0 612 KB

A multi-language tool which parses source code for function definitions and calls

License: GNU General Public License v3.0

Perl 94.15% Awk 0.24% C++ 0.24% Go 0.37% Java 0.34% JavaScript 0.28% PHP 0.29% Python 0.25% Ruby 0.22% Swift 0.29% Tcl 0.34% MATLAB 0.28% R 0.24% Kotlin 0.19% Rust 0.19% Shell 0.22% Dart 0.23% Scala 0.13% Lua 1.21% BASIC 0.32%
callgraph basic fortran golang lua javascript kotlin matlab r rakulang

callgraph's Introduction

Generate static call graphs for multiple languages

A call graph shows how functions call each other within a program.
Each oval represents a function. Each arrow indicates a function call.
In the diagram below, the main program is represented by node MAIN.
It calls 6 functions, one of which calls 9 other functions.

'callGraph' parses source code for function definitions and calls, generates a call graph image, and displays it on screen.
Supported languages are:
awk, bash, basic, dart, fortran, go, lua, javascript, julia, kotlin, matlab, perl, pascal, php, python, R, raku, ruby, rust, scala, swift, and tcl.

c/c++/java are not supported, since their complex and varied syntax requires heavy machinery.

"Sample output"

Usage:
    callGraph  <files>  <options>

    If the script calls helper modules, and you want the call graph to display the modules' functions,
        list the modules explicitly on the command line:
    callGraph script.pl path/moduleA.pm path/moduleB.pm
    
Options:
    -language <lang>           By default, filename extensions are parsed for .pl .pm .tcl .py, etc.
                               If those are not found, the first line of the script (#! shebang) is inspected.
                               If neither of those give clues, use this option to specify 'pl', 'tcl', 'py', etc
                               This option is required if a directory is scanned

    -start <function>          Specify function(s) as starting point instead of the main code.
                               These are displayed in green.
                               This is useful when parsing a large script, as the generated graph can be huge.
                               In addition, the calls leading to this function are charted.
                               Functions which are not reachable from one of the starting points
                                 are not charted.
                               -start __MAIN__  can be very useful when multiple source files
                                 are specified on the command line
                               The filename can be included as well:
                                -start <file>:<function>

    -ignore <regex>            Specify function(s) to ignore.
                               This is useful when pruning the output of a large graph.
                               In particular, use it to remove logging or other helper functions which are
                                 called by many functions, and only clutter up the graph.
                               To ignore multiple functions, use this regex format:
                                   -ignore '(abc|xyz)'

    -output <filename>         Specify an output filename
                               By default, the .png file is named according to the first filename.
                               If a filename ending in .dot is given,
                                 only the intermediate .dot file is created.
                               If a filename ending in .svg is given, svg format is used
                               If a filename ending in .pdf is given, pdf format is used

    -noShow                    By default, the .png file is displayed.  This option prevents that behavior.

    -fullPath                  By default, the script strips off the path name of the input file(s).
                               This option prevents that behavior.

    -writeSubsetCode <file>    Create an output source code file which includes only the functions
                                 included in the graph.
                               This can be useful when trying to comprehend a large legacy code.

    -ymlOut <file>             Create an output YAML file which describes the following for each function:
                                   * which functions call it
                                   * which functions it calls
                               This can be useful to create your own automation or custom formatting
                               
    -ymlIn <file>              Parse a YAML file instead of parsing source files
                               The format is the same as the one created by -ymlOut

    -verbose                   Provides 2 additional functionalities:
                               
                               1) Displays the external scripts referenced within each function

                               2) For Perl/TCL, attempts to list the global variables
                                    used in each function call in the graph.
                                  Global variables are arguably not the best design paradigm,
                                    but they are found extensively in real-world legacy scripts.

                                  Perl:
                                      'my' variables will affect this determination (use strict).
                                      Does not distinguish between $var, @var and %var.

                                  TCL:
                                      Variables declared as 'global' but not used, are marked with a '*'


Usage examples:
    callGraph  example.py
    callGraph  example.pl example_helper_lib.pm
    callGraph  <directory> -language 'go'

Algorithm:
    callGraph uses a simple line-by-line algorithm, using regexes to find function definitions and calls.
    Function definitions can be detected easily, since they start with identifiers such as:
        'sub', 'def', 'proc', 'function', 'func', 'fun', or 'fn'
    Function definitions end with '}' or 'end' at the same nesting level as the definition.
    Function calls are a bit more tricky, since built-in function calls look exactly like user function calls.
        To solve this, the algorithm first assumes that anything matching 'word(...)' is a function call,
        and then discards any calls which do not have corresponding definitions.
    For example, Perl:
        sub funcA {
            ...
            if ($x) {
                print($y);
                funcB($y);
            }
            ...
        }
        sub funcB {
            ...
        }
    Since this is not a true parser, the formatting must be consistent so that nesting can be determined.
    If your script does not follow this rule, consider running it through a linter first.
    Also, don't expect miracles such as parsing dynamic function calls.
    Caveats aside, it seems to work well on garden-variety scripts spanning tens of thousands of lines,
        and has helped me unravel large pieces of legacy code to implement urgent bug fixes.

    
Acknowledgements:
    This code borrows core functionality from https://github.com/cobber/perl_call_graph

Requirements:
    GraphViz and the Perl GraphViz library must be installed:
        sudo apt install graphviz
        sudo apt install make
        sudo cpanm install GraphViz
    On Mac:
        brew install graphviz
        brew install cpanminus
        sudo cpanm GraphViz

callgraph's People

Contributors

cosmojg avatar dhaizei avatar dimitripapadopoulos avatar koknat avatar thegoatinthemachine avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

callgraph's Issues

Use GraphViz2 rather than GraphViz Perl module?

I just learned of this project today, and it's really cool!

Thought you'd be interested to know of the GraphViz2 module, which may make things easier/better in this script. Example usage:

From https://github.com/PDLPorters/pdl/blob/master/perldl#L335-L338:

  my $g = PDL::Core::pdumpgraph(PDL::Core::pdumphash($pdl));
  require GraphViz2;
  my $gv = GraphViz2->from_graph(PDL::Core::pdumpgraphvizify($g));
  $gv->run(format => $format, output_file => $file);

The general idiom is you make a Graph object, then annotate it (e.g. with a graphvizify procedure such as https://github.com/PDLPorters/pdl/blob/ac270e21cafeda8726dd64fa4b90e4a6e686c8c2/Basic/Core/Core.pm#L2394-L2439), then GraphViz2 can run with it.

I cannot run call graph on my mac

Hi,

I am a rookie in this, but I want to get this tool works.
I saw the issue of luke: #1.
I tried following his advice but that don't work.

  1. brew install graphviz. I get this error:

Warning: No available formula with the name "graphviz".
==> Searching for similarly named formulae...
Error: No similarly named formulae found.
It was migrated from homebrew/cask to homebrew/core.

  1. sudo cpan install GraphViz. I get this message:

Configuring R/RS/RSAVAGE/GraphViz-2.24.tgz with Makefile.PL
Can't exec "dot": No such file or directory at Makefile.PL line 21.
Please install Graphviz from http://www.graphviz.org/.
No 'Makefile' created RSAVAGE/GraphViz-2.24.tgz
/usr/bin/perl Makefile.PL -- NOT OK

  1. I downloaded the github from callGraph, perl_call_graph, graphviz-perl, and IPC-Run but don't know what to do with them.

Is someone that can help me?

Thanks a lot!

Could it generate file level dependency?

Hi, I have two questions around the tool, thanks for your info.

  1. Does it generate json or xml format output that could be consumed by code?
  2. Does it generate file level dependency with corresponding file path info? For example flask repo has a test file tests/test_appctx.py that has the following import statements:
import pytest
import flask
from flask.globals import app_ctx
from flask.globals import request_ctx

I am looking for output of file level dependency like the following:

"tests.test_appctx": {
    "imports": [
      "flask",
      "flask.globals",
      "pytest"
    ],
    "name": "tests.test_appctx",
    "path": "/Users/myuserid/python/flask/tests/test_appctx.py"
  },

Process also function references

Would it be possible to process also function references for kotlin?

Function can be "used" not only by direct call, but also by passing a reference to it (e.g. ::myFunction or value::myFunction). From the point of user, who want's to see a dependency graph of the functions, this would be worth to see too.

Other than this one slight deficiency, this tool works very well and greatly surpassed my expectations. Good job!

[Python] functions with the same name but different classes

it is currently not possible to differentiate methods with the same name but from different classes in the same file.

Example:

class ClassA:
    def method_name(self):
        return 0
    
class ClassB:
    def method_name(self):
        return 0


if __name__ == '__main__':
    ClassA.method_name()
    ClassB.method_name()

callGraph output:

{
    "teste/multiple_functions.py:method_name": {
        "called_by": {
            "teste/multiple_functions.py:__MAIN__": 1
        }
    },
    "teste/multiple_functions.py:__MAIN__": {
        "calls": {
            "teste/multiple_functions.py:method_name": 1
        }
    }
}

Specify path + function name with -start option

Hello @koknat thank you very much for this tool!

Is it somehow possible to specify the path to the file in combination with the function name in the -start option?
e.g. ./callGraph . -start 'src/subfolder/main.go#update' -language 'go'

I would like to generate a call graph for a specific function but the same name is used in different files of my application.

Can't install dependencies (GraphViz)

root@6f38527114dc:/# cpan install GraphViz
Loading internal logger. Log::Log4perl recommended for better logging
Reading '/root/.cpan/Metadata'
Database was generated on Tue, 29 Nov 2022 08:41:02 GMT

(error): Could not expand [GraphViz]. Check the module name.
(info): I can suggest names if you install one of Text::Levenshtein::XS, Text::Levenshtein::Damerau::XS, Text::Levenshtein, and Text::Levenshtein::Damerau::PP
(info): and you provide the -x option on invocation.
(error): Skipping GraphViz because I couldn't find a matching namespace.

class question

i find it couldn't find class,
such as
class A:
def init(self):
pass
def forward(self):
pass
only identity functions,

Running callGraph on mac

Hi there,

This is a very useful looking tool, but I'm having trouble running it on mac.

I've run brew install graphviz as well as sudo cpan install GraphViz, copied callGraph's executable into /usr/local/Cellar/graphviz/2.48.0/bin, and called it like so, with the following error:

macbook ~ % callgraph /Users/username/Developer/Project/FileToGraph.swift
ERROR: Install GraphViz and the CPAN GraphViz module to create the call graph
sudo apt-get install Graphviz
and
sudo cpan install GraphViz

Am I doing something wrong? Is it possible to use callGraph on macOS?

Thanks,
Luke

-ymlOut and -ymlIn produce different results than a normally targeted run

Expected behavior: using -ymlOut and -ymlIn should produce a .dot graph which has the same features as running callGraph against a file or directory.

Actual behavior: callGraph -ymlIn $previously_produced_yml produces a .dot graph whose nodes lack the filenames where the referenced functions exist.

javascript's fat arrow functions support

Hi,
it doesn't seem to support fat arrow functions in javascript.
My code looks like this:

const setOrderItems = (formItems) => {

callGraph: ERROR: Could not find any function definitions in your file

Scan all files in (sub)folder(s)

It seems a nice tool with the graph and all.
Would it be an interesting feature to scan all files (of a certain language) in a folder (and if wished all subfolders?).
It would require to generate a list of all functions per file per folder, as well as a list of all function-calls per function.
I was thinking of programming something like that my self but i am still in the phase of "make or find".

Does not recognise all R function definitions

When scanning code in R files, callGraph searches for function definitions using the regex

 ^(\s*)()(\w+)\s+<-\s+function\s*\(.*\)

Newlines between arguments

The . wildcard doesn't match newlines, so this fails to detect function definitions like:

example_function <- function(a_long_argument_name,
                             another_rather_long_argument_name,
                             a_third_argument)...

An alternative would be to replace . with the character set matching everything except closing parentheses ([^\)]):

 ^(\s*)()(\w+)\s+<-\s+function\s*\([^\)]*\) 

Use of = instead of ->

Whilst using <- for assignment is preferred, = may be used. Any functions defined in this way will not be recognised.

This could be fixed by replacing the <- with (<-|=).

language fortran is not supported

ERROR:  Language '' not recognized.  Specify it with  -language <language>
        For example:  -language pl
        For example:  -language py
        For example:  -language tcl
        Supported languages are:  awk bas c cpp dart for go java jl js kt lua m pas php pl py r rb rs sc sh swift tcl ts v

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.