Introduction

During compilation, most modern compilers discover and generate a wealth of information about what the compiler did in optimizing the code, what optimizations could not be implemented (and why), how data is accessed, relationships between procedures, and much more. PGI compilers include the ability to save this information into the compiled object and/or executable file for later extraction and review using CCFF.

PGI compilers also have the ability to export information gathered from the internal representation (IR) of various compilation stages in an organized, human readable format. Like CCFF, the Program Analysis Summary Output (PASO) feature saves the information generated by Interprocedural Analysis into the compiled object or exeutable file. This feature can be used to create analysis tools. For example, to perform data flow analysis or generate call graphs.

Usage

PASO data export is enabled using the -Msummary option during compilation. PASO is supported on all PGI compilers for Linux x86-64 and Windows using the proprietary PGI back end.

By default the compilers embed the export data into the executable as a data section. This creates a section ".IPXPORT" which can be extracted using the pgextract command:

pgextract  (executable) -cz -name ".IPXPORT" (output filename)

Data Summary

The information included in an PASO export file includes:

  • Object files used in compilation, including their source information if available
    • Any Fortran modules defined in a file
    • Any functions or subroutines defined in a file or module
      • Variable declarations and types
        • Bindings to assignments from constants or other variables
      • Global or common block data usage
      • Loop nesting information
      • Function calls including all arguments
      • List of all functions that call this one
  • Library files included, with any IPA information if available

Data Format

PASO information is exported using the JSON (Javascript Object Notation) file format. This format allows the entire structure of the PASO data to be preserved; JSON parsers are available in many programming languages. See the PASO Reference for a complete description of the PASO export file format.

Example Program

Following is an example application written in Python. It takes the PASO export file as an input and generates a static call graph.

Download information: convert.py: Call Graph Generator

Usage:

./convert.py (PASO file) -simplify 

Optionally, it can also create the tree for one function:

./convert.py (PASO file) -func (function name) -simplify 

It can also trace the usage of an individual variable, overlaid onto the call graph:

 ./convert.py (PASO file) -var file:(object file) func:(host function) (variable name) -simplify

The result will be a (filename).dot file which can be imported into various graphing tools. An online grapher is available at webgraphiz.com.

Importing IPA data in python requires only a few steps:

  1. Import JSON parsing library:
    import json
  2. Open PASO text file:
    file = open("efile","r")
  3. Read text from file:
    txt = file.read()
  4. Parse JSON from text:
    data = json.loads(txt)

The PASO export data is now ready to use. The PASO data structure is essentially a tree with named branches, and is accessed by the names as defined in the reference documentation. For example, to find the names of all the files a program was compiled with, make a simple loop to iterate over the file objects and read their names:

for fileID in data["files]": 
  print data["files"][fileID]["name"]

This works because data["files"] is a structure containing named "file" objects. The first line iterates over these objects, returning the names (file IDs) of the files. Then to access the objects themselves, they can be indexed by their unique fileID. Now suppose we know that the function main resides in file1. To get the function object, traverse the tree. "file1" is a member of "files", and contains a structure called "function", which contains "main".

func = data["files"]["file1"]["functions"]["main]"

Find the calls within the function by looking in func's member "calls". This next example prints out all the calls in a function, and their arguments:

for call in func["calls"]: 
  print "name:" + call["name"] 
  for arg in call["arguments"]: 
    print arg
Click me