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