Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Visualising C struct dependencies

Tags:

c

struct

dot

In a large C project there are many structs that have other structs, or pointers to them, as fields. I want to create a directed graph to show the dependencies between the "types". An example would be

typedef struct javaStat {
    int idNo;
    struct idIdentList *className;
    struct typeModifiers *thisType;
    struct symbol thisClass;
} ...

From this I would like to generate a DOT structure, which would look like

digraph {
    javaStat -> idIdentList
    javaStat -> typeModifiers
    javaStat -> symbol
}

or, using a DOT short-hand:

digraph {
    javaStat -> {idIdentList typeModifiers symbol}
}

Of course the first and last lines can be added by hand, so the primary problem is converting the struct references to the graph "pointer" lines.

At this point I'm content with a first level solution, meaning that deeper nesting could be ignored.

I first tried a simple grep struct *.h which produced something workable:

typedef struct javaStat {
    struct idIdentList *className;
    struct typeModifiers *thisType;
    struct symbol thisClass;
typedef struct <next struct> {

This is a simple problem which a few lines of Python would solve, but are there other handy solutions, perhaps using sed, grep, awk and their brethren?

EDIT: I've realized that the reason I want to do this is because I need to find one or more structures that are at the base of the "struct tree".

like image 639
thoni56 Avatar asked Mar 11 '20 16:03

thoni56


1 Answers

Clang 9 allows for JSON representation of the AST of c file (found it in this question). JSON AST could be processed further to generate the target output.

E.g. this Python script:

#clang_ast_to_dot.py
from jsonpath_rw_ext import parse;
import sys, json;

def extract_struct_name(fieldDefinition):
  return fieldDefinition["type"]["qualType"].replace("struct", "").replace("*", "").replace(" ","")

def is_struct_field(fieldDefinition, knownStructs):
  return (fieldDefinition["kind"] == "FieldDecl" and 
          ("struct " in fieldDefinition["type"]["qualType"] or 
           extract_struct_name(fieldDefinition) in knownStructs))


data = json.load(sys.stdin)

allStructs = {}

for structDef in parse('$.inner[?(@.kind=="RecordDecl")]').find(data):
    allStructs[structDef.value["name"]]=structDef.value

print("digraph {")
for name, structDescription in allStructs.items():
    print("    %s -> {%s}"
          % (name, ", ".join(extract_struct_name(field) for field in structDescription["inner"] if is_struct_field(field, allStructs))))
print("}")

called as:

clang -Xclang -ast-dump=json MyCFile.c | python clang_ast_to_dot.py

produces:

digraph {
    javaStat -> {idIdentList, typeModifiers, symbol}
}

Of course this is a toy example, I'm sure it won't work for all cases.

like image 135
Renat Avatar answered Sep 21 '22 00:09

Renat