1#! /usr/bin/python2 2import os.path 3import sys 4import shlex 5import re 6 7from headerutils import * 8 9header_roots = { } 10extra_edges = list() 11verbose = False 12verbosity = 0 13nodes = list() 14 15def unpretty (name): 16 if name[-2:] == "_h": 17 name = name[:-2] + ".h" 18 return name.replace("_", "-") 19 20def pretty_name (name): 21 name = os.path.basename (name) 22 return name.replace(".","_").replace("-","_").replace("/","_").replace("+","_"); 23 24depstring = ("In file included from", " from") 25 26# indentation indicates nesting levels of included files 27ignore = [ "coretypes_h", 28 "machmode_h", 29 "signop_h", 30 "wide_int_h", 31 "double_int_h", 32 "real_h", 33 "fixed_value_h", 34 "hash_table_h", 35 "statistics_h", 36 "ggc_h", 37 "vec_h", 38 "hashtab_h", 39 "inchash_h", 40 "mem_stats_traits_h", 41 "hash_map_traits_h", 42 "mem_stats_h", 43 "hash_map_h", 44 "hash_set_h", 45 "input_h", 46 "line_map_h", 47 "is_a_h", 48 "system_h", 49 "config_h" ] 50 51def process_log_file (header, logfile): 52 if header_roots.get (header) != None: 53 print "Error: already processed log file: " + header + ".log" 54 return 55 hname = pretty_name (header) 56 header_roots[hname] = { } 57 58 sline = list(); 59 incfrom = list() 60 newinc = True 61 for line in logfile: 62 if len (line) > 21 and line[:21] in depstring: 63 if newinc: 64 incfrom = list() 65 newinc = False 66 fn = re.findall(ur".*/(.*?):", line) 67 if len(fn) != 1: 68 continue 69 if fn[0][-2:] != ".h": 70 continue 71 n = pretty_name (fn[0]) 72 if n not in ignore: 73 incfrom.append (n) 74 continue 75 newinc = True 76 note = re.findall (ur"^.*note: (.*)", line) 77 if len(note) > 0: 78 sline.append (("note", note[0])) 79 else: 80 err_msg = re.findall (ur"^.*: error: (.*)", line) 81 if len(err_msg) == 1: 82 msg = err_msg[0] 83 if (len (re.findall("error: forward declaration", line))) != 0: 84 continue 85 path = re.findall (ur"^(.*?):.*error: ", line) 86 if len(path) != 1: 87 continue 88 if path[0][-2:] != ".h": 89 continue 90 fname = pretty_name (path[0]) 91 if fname in ignore or fname[0:3] == "gt_": 92 continue 93 sline.append (("error", msg, fname, incfrom)) 94 95 print str(len(sline)) + " lines to process" 96 lastline = "note" 97 for line in sline: 98 if line[0] != "note" and lastline[0] == "error": 99 fname = lastline[2] 100 msg = lastline[1] 101 incfrom = lastline[3] 102 string = "" 103 ofname = fname 104 if len(incfrom) != 0: 105 for t in incfrom: 106 string = string + t + " : " 107 ee = (fname, t) 108 if ee not in extra_edges: 109 extra_edges.append (ee) 110 fname = t 111 print string 112 113 if hname not in nodes: 114 nodes.append(hname) 115 if fname not in nodes: 116 nodes.append (ofname) 117 for y in incfrom: 118 if y not in nodes: 119 nodes.append (y) 120 121 122 if header_roots[hname].get(fname) == None: 123 header_roots[hname][fname] = list() 124 if msg not in header_roots[hname][fname]: 125 print string + ofname + " : " +msg 126 header_roots[hname][fname].append (msg) 127 lastline = line; 128 129 130dotname = "graph.dot" 131graphname = "graph.png" 132 133 134def build_dot_file (file_list): 135 output = open(dotname, "w") 136 output.write ("digraph incweb {\n"); 137 for x in file_list: 138 if os.path.exists (x) and x[-4:] == ".log": 139 header = x[:-4] 140 logfile = open(x).read().splitlines() 141 process_log_file (header, logfile) 142 elif os.path.exists (x + ".log"): 143 logfile = open(x + ".log").read().splitlines() 144 process_log_file (x, logfile) 145 146 for n in nodes: 147 fn = unpretty(n) 148 label = n + " [ label = \"" + fn + "\" ];" 149 output.write (label + "\n") 150 if os.path.exists (fn): 151 h = open(fn).read().splitlines() 152 for l in h: 153 t = find_pound_include (l, True, False) 154 if t != "": 155 t = pretty_name (t) 156 if t in ignore or t[-2:] != "_h": 157 continue 158 if t not in nodes: 159 nodes.append (t) 160 ee = (t, n) 161 if ee not in extra_edges: 162 extra_edges.append (ee) 163 164 depcount = list() 165 for h in header_roots: 166 for dep in header_roots[h]: 167 label = " [ label = "+ str(len(header_roots[h][dep])) + " ];" 168 string = h + " -> " + dep + label 169 output.write (string + "\n"); 170 if verbose: 171 depcount.append ((h, dep, len(header_roots[h][dep]))) 172 173 for ee in extra_edges: 174 string = ee[0] + " -> " + ee[1] + "[ color=red ];" 175 output.write (string + "\n"); 176 177 178 if verbose: 179 depcount.sort(key=lambda tup:tup[2]) 180 for x in depcount: 181 print " ("+str(x[2])+ ") : " + x[0] + " -> " + x[1] 182 if (x[2] <= verbosity): 183 for l in header_roots[x[0]][x[1]]: 184 print " " + l 185 186 output.write ("}\n"); 187 188 189files = list() 190dohelp = False 191edge_thresh = 0 192for arg in sys.argv[1:]: 193 if arg[0:2] == "-o": 194 dotname = arg[2:]+".dot" 195 graphname = arg[2:]+".png" 196 elif arg[0:2] == "-h": 197 dohelp = True 198 elif arg[0:2] == "-v": 199 verbose = True 200 if len(arg) > 2: 201 verbosity = int (arg[2:]) 202 if (verbosity == 9): 203 verbosity = 9999 204 elif arg[0:1] == "-": 205 print "Unrecognized option " + arg 206 dohelp = True 207 else: 208 files.append (arg) 209 210if len(sys.argv) == 1: 211 dohelp = True 212 213if dohelp: 214 print "Parses the log files from the reduce-headers tool to generate" 215 print "dependency graphs for the include web for specified files." 216 print "Usage: [-nnum] [-h] [-v[n]] [-ooutput] file1 [[file2] ... [filen]]" 217 print " -ooutput : Specifies output to output.dot and output.png" 218 print " Defaults to 'graph.dot and graph.png" 219 print " -vn : verbose mode, shows the number of connections, and if n" 220 print " is specified, show the messages if # < n. 9 is infinity" 221 print " -h : help" 222else: 223 print files 224 build_dot_file (files) 225 os.system ("dot -Tpng " + dotname + " -o" + graphname) 226 227 228