#! /usr/bin/python2 import os.path import sys import shlex import re from headerutils import * header_roots = { } extra_edges = list() verbose = False verbosity = 0 nodes = list() def unpretty (name): if name[-2:] == "_h": name = name[:-2] + ".h" return name.replace("_", "-") def pretty_name (name): name = os.path.basename (name) return name.replace(".","_").replace("-","_").replace("/","_").replace("+","_"); depstring = ("In file included from", " from") # indentation indicates nesting levels of included files ignore = [ "coretypes_h", "insn_modes_h", "signop_h", "wide_int_h", "wide_int_print_h", "insn_modes_inline_h", "machmode_h", "double_int_h", "real_h", "fixed_value_h", "hash_table_h", "statistics_h", "ggc_h", "vec_h", "hashtab_h", "inchash_h", "mem_stats_traits_h", "hash_map_traits_h", "mem_stats_h", "hash_map_h", "hash_set_h", "input_h", "line_map_h", "is_a_h", "system_h", "config_h" ] def process_log_file (header, logfile): if header_roots.get (header) != None: print "Error: already processed log file: " + header + ".log" return hname = pretty_name (header) header_roots[hname] = { } sline = list(); incfrom = list() newinc = True for line in logfile: if len (line) > 21 and line[:21] in depstring: if newinc: incfrom = list() newinc = False fn = re.findall(ur".*/(.*?):", line) if len(fn) != 1: continue if fn[0][-2:] != ".h": continue n = pretty_name (fn[0]) if n not in ignore: incfrom.append (n) continue newinc = True note = re.findall (ur"^.*note: (.*)", line) if len(note) > 0: sline.append (("note", note[0])) else: err_msg = re.findall (ur"^.*: error: (.*)", line) if len(err_msg) == 1: msg = err_msg[0] if (len (re.findall("error: forward declaration", line))) != 0: continue path = re.findall (ur"^(.*?):.*error: ", line) if len(path) != 1: continue if path[0][-2:] != ".h": continue fname = pretty_name (path[0]) if fname in ignore or fname[0:3] == "gt_": continue sline.append (("error", msg, fname, incfrom)) print str(len(sline)) + " lines to process" lastline = "note" for line in sline: if line[0] != "note" and lastline[0] == "error": fname = lastline[2] msg = lastline[1] incfrom = lastline[3] string = "" ofname = fname if len(incfrom) != 0: for t in incfrom: string = string + t + " : " ee = (fname, t) if ee not in extra_edges: extra_edges.append (ee) fname = t print string if hname not in nodes: nodes.append(hname) if fname not in nodes: nodes.append (ofname) for y in incfrom: if y not in nodes: nodes.append (y) if header_roots[hname].get(fname) == None: header_roots[hname][fname] = list() if msg not in header_roots[hname][fname]: print string + ofname + " : " +msg header_roots[hname][fname].append (msg) lastline = line; dotname = "graph.dot" graphname = "graph.png" def build_dot_file (file_list): output = open(dotname, "w") output.write ("digraph incweb {\n"); for x in file_list: if os.path.exists (x) and x[-4:] == ".log": header = x[:-4] logfile = open(x).read().splitlines() process_log_file (header, logfile) elif os.path.exists (x + ".log"): logfile = open(x + ".log").read().splitlines() process_log_file (x, logfile) for n in nodes: fn = unpretty(n) label = n + " [ label = \"" + fn + "\" ];" output.write (label + "\n") if os.path.exists (fn): h = open(fn).read().splitlines() for l in h: t = find_pound_include (l, True, False) if t != "": t = pretty_name (t) if t in ignore or t[-2:] != "_h": continue if t not in nodes: nodes.append (t) ee = (t, n) if ee not in extra_edges: extra_edges.append (ee) depcount = list() for h in header_roots: for dep in header_roots[h]: label = " [ label = "+ str(len(header_roots[h][dep])) + " ];" string = h + " -> " + dep + label output.write (string + "\n"); if verbose: depcount.append ((h, dep, len(header_roots[h][dep]))) for ee in extra_edges: string = ee[0] + " -> " + ee[1] + "[ color=red ];" output.write (string + "\n"); if verbose: depcount.sort(key=lambda tup:tup[2]) for x in depcount: print " ("+str(x[2])+ ") : " + x[0] + " -> " + x[1] if (x[2] <= verbosity): for l in header_roots[x[0]][x[1]]: print " " + l output.write ("}\n"); files = list() dohelp = False edge_thresh = 0 for arg in sys.argv[1:]: if arg[0:2] == "-o": dotname = arg[2:]+".dot" graphname = arg[2:]+".png" elif arg[0:2] == "-h": dohelp = True elif arg[0:2] == "-v": verbose = True if len(arg) > 2: verbosity = int (arg[2:]) if (verbosity == 9): verbosity = 9999 elif arg[0:1] == "-": print "Unrecognized option " + arg dohelp = True else: files.append (arg) if len(sys.argv) == 1: dohelp = True if dohelp: print "Parses the log files from the reduce-headers tool to generate" print "dependency graphs for the include web for specified files." print "Usage: [-nnum] [-h] [-v[n]] [-ooutput] file1 [[file2] ... [filen]]" print " -ooutput : Specifies output to output.dot and output.png" print " Defaults to 'graph.dot and graph.png" print " -vn : verbose mode, shows the number of connections, and if n" print " is specified, show the messages if # < n. 9 is infinity" print " -h : help" else: print files build_dot_file (files) os.system ("dot -Tpng " + dotname + " -o" + graphname)