-3

Suppose I have a program P11 in a directory; it calls 2 other programs P21 & P22. Each of P21 & P22 call 2-2 other programs.

i.e.

Level-1:           |--------------P11-------------|
Level-2:  |--------P21----------|    |----------P22----------|...
Level-3:  P31                 P32    P33                   P34...
       .                   .      .                     .
       .                   .      .                     . 

Just like a tree diagram having 100s of levels, 100s of nodes at each level and 1000s of nodes in total.

I need a shell script which will give me such a tree structure.

e.g. The shell script program is – ‘MYPGM.sh’.

It takes a parameter at runtime. We can provide a program name here e.g. P11. And the script gives such a tree diagram.

Not necessary that the output structure should be in such a diagram like format as given above. We can use indexing or anything you suggest. e.g. The output file could be as –

1. P11
1.1 P21 - 1.2 P22
1.1.1 P31 - 1.1.2 P32 – 1.2.1 P33 – 1.2.2 P34

i.e. Every program's index = it's parent's index + "." + its serial number in that parent program.

Just the thing is – the number of levels & number of nodes in each level is unknown, but very large.

The program syntax used to call another program is as below.:

CALL "CHILD_PGM_NAME"

Command to find the children programs in a given program is as below.:

 egrep '^CALL| CALL ' $pgm_name > call_output | cut -d'"' -f2 call_output > call_output_cut | cat call_output_cut

I am stuck at - how to loop this command to get the above stated output file.

Can anybody suggest something?

closed as too broad by jimmij, Anthon, derobert, Networker, Braiam Dec 31 '14 at 18:07

Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • Ahem.. Those pipes are meaningless. Did you mean ; instead? And is it guaranteed to be a tree? For example, P22 may not call P21? – muru Dec 31 '14 at 11:10
  • 3
    Do you need this as text? If you want to see this data to understand it, have you considered graphviz (e.g., dot)? – derobert Dec 31 '14 at 11:38
  • Have you considered ps f and ps -H? – user86969 Dec 31 '14 at 12:16
  • Also have a look at pstree. – michas Dec 31 '14 at 12:26
  • You should provide the example input that leads to your example output. – Hauke Laging Dec 31 '14 at 15:22
3

For this problem I suggest awk, python, perl or similar. The following starting point is a perl script that enqueues the "programs" if not yet visited. The output is in graph-viz (as suggested by @derobert).

#!/usr/bin/perl

print "digraph{\n rankdir=LR \n";

my %visited = ();
while(<>){                               # for all lines in all files in @ARGV

  $visited{$ARGV}=1;                     # $ARGV = current file

  if(/\bCALL\s*"(.+?)"/) {
      print "$ARGV -> $1;\n" ;
      push @ARGV,$1  unless $visited{$1};
      $visited{$1}=1;
  }
}

print "}\n";

usage:

perl calltree p1 > x.dot
dot -Tpdf -o x.pdf x.dot

or (thank you @derobert)

dot -Tx11 x.dot

to view the graph immediately.

As @muru pointed out, typically this is not tree.

  • You may also want to point out dot -Tx11 to view immediately instead of making a PDF. – derobert Dec 31 '14 at 17:00
1

@JJoao's way is definitely the one to use. However, I wrote this very inefficient script, that might be of interest:

#! /bin/bash

SRC_DIR="/path/to/programs"
TMP_DIR="/tmp/tree"
declare -A FUNCS

iterate ()
(
    program="$1"
    FUNCS["$program"]="$TMP_DIR/$program"
    mkdir "${FUNCS[$program]}"
    grep -Po '(?<=\bCALL ")[^"]*' "$SRC_DIR/$program" | while read child
    do
        if [[ -n ${FUNCS[$child]} ]]
        then
            ln -s "${FUNCS[$child]}" "${FUNCS[$program]}/$child"
        else
            iterate "$child"
        fi
    done
)

[[ -n $1 ]] && { iterate "$1"; tree -dlo call-graph.html -H "$TMP_DIR" "$TMP_DIR"; }
rm -rf "$TMP_DIR"

It creates a directory tree, with each directory representing a function, containing symbolic links to the other directories/functions it calls. The tree program is naturally good at drawing trees (the -l option follows symbolic links unless recursion is detected, -o generates HTML output).

Of course, you should ideally be looking at Call Graph Generators for your language. This SO question lists some for C, perhaps you might be able to find one which supports your language.

Not the answer you're looking for? Browse other questions tagged or ask your own question.