views:

89

answers:

3

Anyone know of a script to get a listing that will be able to tell me the most frequently called functions for a C project?

method1 391
method2 23
method3 12

Even better have it be customizable to require a keyword in the method name "get".

I'm trying not to reinvent the wheel and write a simple script to do it myself. Probably an easy way to do this with grep or awk. I can write a regular expression to match a function call name. An Eclipse Plugin would be ideal.

I'm not looking for a profiler. The reason I want such a method is I need to optimize program space for an embedded project. I tend to encapsulate my member variables with getters but may need to use externs and access the most used members directly to cut down on space.

+1  A: 

One approach would be to run Doxygen over the source base. You would need to configure it to extract all functions and classes, unless you are already using Doxygen, since by default it ignores undocumented entities.

If you also have AT&T GraphViz installed, you can get pretty call and caller graphs drawn for each function. But I don't think there is a table that summarizes that by number of calls.

However, there are a couple of non-document output formats that can be selected, including Perl module and XML. It should be possible to parse one of those to develop the list you want, and it is almost certainly easier to parse that information than to hack together enough of a C++ front end to get the right answer by brute force.

There is also an XML backend for GCC floating around somewhere that essentially dumps the syntax tree in XML... I've tripped over it recently, but don't recall specifically where that was.

RBerteig
I appreciate your suggestion. I think getting Doxygen setup etc would take longer then writing a little java app which would take 15 min.
blak3r
The ulterior motive is that setting up Doxygen often has long-term benefits for a project...
RBerteig
A: 

If "most frequently" means the actual number of calls in some run of the program, DTrace could be the tool you are looking for, assuming you have a Solaris, FreeBSD, or OSX box for development work. See also Code Spelunking redux for a nice description of Doxygen and DTrace.

Jouni K. Seppänen
+1  A: 

Here is a simple java script which provides the output I was looking for. By customizing the two REGEX's at the top of the file it could be used to generate statistics on the occurrences of other patterns as well.

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

public class MethodCallCount {
    // This is the regex which is applied to each line to test if there is a method call on it.
    private static String REGEX_METHODCALL = "(?:\\s*)([a-zA-Z0-9_]+)\\((.*)"; 
    // Only looks in files with .c extention
    private static String REGEX_FILEEXTS = ".*.c";
    private static boolean VERBOSE_OUTPUT = false;
    private static Map<String,Integer> patternMap = new HashMap<String,Integer>();

    // Process all files and directories under dir
    public static void visitAllDirsAndFiles(File dir) {

        if( !dir.isDirectory()) {
         if( dir.getName().matches(REGEX_FILEEXTS) ) {
          if( VERBOSE_OUTPUT ) { System.out.println("Processing File: " + dir.getName() ); }
          processFile(dir);
         }
        }
        else if( !dir.getName().equals(".svn") ) {
         String[] children = dir.list();
            for (int i=0; i<children.length; i++) {
                visitAllDirsAndFiles(new File(dir, children[i]));
            }
        }
    }

    // Process only directories under dir
    public static void visitAllDirs(File dir) {
        if (dir.isDirectory()) {
            processFile(dir);

            String[] children = dir.list();
            for (int i=0; i<children.length; i++) {
                visitAllDirs(new File(dir, children[i]));
            }
        }
    }

    // Process only files under dir
    public static void visitAllFiles(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (int i=0; i<children.length; i++) {
                visitAllFiles(new File(dir, children[i]));
            }
        } else {
            processFile(dir);
        }
    }

    public static void processMethod( String pMethod ) {
        if( VERBOSE_OUTPUT ) { System.out.println("found: " + pMethod); }
        if( patternMap.containsKey( pMethod ) ) {
      Integer cnt = patternMap.get( pMethod );
      cnt = cnt + 1;
      patternMap.put(pMethod, cnt );
     }
     else {
      patternMap.put( pMethod.toString(), 1);
     }     
    }


    public static void processLine( String pLine ) {
     Pattern methodMatcher = Pattern.compile( REGEX_METHODCALL );
     java.util.regex.Matcher matcher = methodMatcher.matcher( pLine );

     if( matcher.matches() ) {
      if( VERBOSE_OUTPUT ) { System.out.println("processing " + matcher.group(1) ); }
      processMethod( matcher.group(1) );     
      processLine( matcher.group(2) );
     }
    }

    public static void processFile( File pFile ) {
     BufferedReader fin;
     try {
      fin = new BufferedReader( new InputStreamReader( new FileInputStream(pFile) ) );
      String l = null;
      while( (l=fin.readLine()) != null ) {
       processLine( l );
      }
     } 
     catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }     
    }

    /**
     * @param args[0] is the directory to run this on.  Otherwise current directory is used.
     */
    public static void main(String[] args) {

     String searchDirPath = System.getProperty("user.dir");
     if( args.length > 0 ) {
      searchDirPath = args[0];
     }
     else {
      System.out.println("No argument specified... searching for *.map in: " + searchDirPath );
     }

     File searchDir = new File( searchDirPath );
     visitAllDirsAndFiles(searchDir);

     // Print Stats.
     int callCnt = 0;
        Set<String> patternSet = patternMap.keySet();
        for( String p : patternSet ) {
            System.out.println( patternMap.get(p) + "\t" + p );
            callCnt += patternMap.get(p);
        }
        System.out.println("Unique Methods: " + patternMap.size());
     System.out.println("Calls Detected: " + callCnt );
     System.out.println("Copy and paste output above into excel and then sort columns");
        System.out.println("DONE.");
    }
}
blak3r