xref: /netbsd-src/external/gpl3/gcc.old/dist/contrib/header-tools/graph-header-logs (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
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