]> git.proxmox.com Git - mirror_frr.git/blame - python/tiabwarfo.py
ospfd: fix memory leak on SPF calculation
[mirror_frr.git] / python / tiabwarfo.py
CommitLineData
36a8fdfd
DL
1# FRR DWARF structure definition extractor
2#
3# Copyright (C) 2020 David Lamparter for NetDEF, Inc.
4#
5# This program is free software; you can redistribute it and/or modify it
6# under the terms of the GNU General Public License as published by the Free
7# Software Foundation; either version 2 of the License, or (at your option)
8# any later version.
9#
10# This program is distributed in the hope that it will be useful, but WITHOUT
11# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13# more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; see the file COPYING; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19import sys
20import os
21import subprocess
22import re
23import argparse
36a8fdfd
DL
24import json
25
00f0c399
DL
26structs = [
27 "xref",
28 "xref_logmsg",
29 "xref_threadsched",
30 "xref_install_element",
31 "xrefdata",
32 "xrefdata_logmsg",
33 "cmd_element",
34]
35
36
37def extract(filename="lib/.libs/libfrr.so"):
38 """
36a8fdfd
DL
39 Convert output from "pahole" to JSON.
40
41 Example pahole output:
42 $ pahole -C xref lib/.libs/libfrr.so
43 struct xref {
44 struct xrefdata * xrefdata; /* 0 8 */
45 enum xref_type type; /* 8 4 */
46 int line; /* 12 4 */
47 const char * file; /* 16 8 */
48 const char * func; /* 24 8 */
49
50 /* size: 32, cachelines: 1, members: 5 */
51 /* last cacheline: 32 bytes */
52 };
00f0c399
DL
53 """
54 pahole = subprocess.check_output(
55 ["pahole", "-C", ",".join(structs), filename]
56 ).decode("UTF-8")
36a8fdfd 57
00f0c399
DL
58 struct_re = re.compile(r"^struct ([^ ]+) \{([^\}]+)};", flags=re.M | re.S)
59 field_re = re.compile(
60 r"^\s*(?P<type>[^;\(]+)\s+(?P<name>[^;\[\]]+)(?:\[(?P<array>\d+)\])?;\s*\/\*(?P<comment>.*)\*\/\s*$"
61 )
62 comment_re = re.compile(r"^\s*\/\*.*\*\/\s*$")
36a8fdfd
DL
63
64 pastructs = struct_re.findall(pahole)
65 out = {}
66
a971f071
DL
67 for sname, data in pastructs:
68 this = out.setdefault(sname, {})
00f0c399 69 fields = this.setdefault("fields", [])
36a8fdfd
DL
70
71 lines = data.strip().splitlines()
72
a971f071
DL
73 next_offs = 0
74
36a8fdfd 75 for line in lines:
00f0c399 76 if line.strip() == "":
36a8fdfd
DL
77 continue
78 m = comment_re.match(line)
79 if m is not None:
80 continue
81
82 m = field_re.match(line)
83 if m is not None:
00f0c399 84 offs, size = m.group("comment").strip().split()
36a8fdfd
DL
85 offs = int(offs)
86 size = int(size)
00f0c399
DL
87 typ_ = m.group("type").strip()
88 name = m.group("name")
36a8fdfd 89
00f0c399 90 if name.startswith("(*"):
36a8fdfd 91 # function pointer
00f0c399
DL
92 typ_ = typ_ + " *"
93 name = name[2:].split(")")[0]
36a8fdfd
DL
94
95 data = {
00f0c399
DL
96 "name": name,
97 "type": typ_,
98 # 'offset': offs,
99 # 'size': size,
36a8fdfd 100 }
00f0c399
DL
101 if m.group("array"):
102 data["array"] = int(m.group("array"))
36a8fdfd
DL
103
104 fields.append(data)
a971f071 105 if offs != next_offs:
00f0c399
DL
106 raise ValueError(
107 "%d padding bytes before struct %s.%s"
108 % (offs - next_offs, sname, name)
109 )
a971f071 110 next_offs = offs + size
36a8fdfd
DL
111 continue
112
00f0c399 113 raise ValueError("cannot process line: %s" % line)
36a8fdfd
DL
114
115 return out
116
00f0c399 117
36a8fdfd 118class FieldApplicator(object):
00f0c399 119 """
36a8fdfd
DL
120 Fill ELFDissectStruct fields list from pahole/JSON
121
122 Uses the JSON file created by the above code to fill in the struct fields
123 in subclasses of ELFDissectStruct.
00f0c399 124 """
36a8fdfd
DL
125
126 # only what we really need. add more as needed.
127 packtypes = {
00f0c399
DL
128 "int": "i",
129 "uint8_t": "B",
130 "uint16_t": "H",
131 "uint32_t": "I",
132 "char": "s",
36a8fdfd
DL
133 }
134
135 def __init__(self, data):
136 self.data = data
137 self.classes = []
138 self.clsmap = {}
139
140 def add(self, cls):
141 self.classes.append(cls)
142 self.clsmap[cls.struct] = cls
143
144 def resolve(self, cls):
145 out = []
00f0c399
DL
146 # offset = 0
147
148 fieldrename = getattr(cls, "fieldrename", {})
36a8fdfd 149
36a8fdfd
DL
150 def mkname(n):
151 return (fieldrename.get(n, n),)
152
00f0c399
DL
153 for field in self.data[cls.struct]["fields"]:
154 typs = field["type"].split()
155 typs = [i for i in typs if i not in ["const"]]
36a8fdfd 156
a971f071
DL
157 # this will break reuse of xrefstructs.json across 32bit & 64bit
158 # platforms
159
00f0c399 160 # if field['offset'] != offset:
a971f071
DL
161 # assert offset < field['offset']
162 # out.append(('_pad', '%ds' % (field['offset'] - offset,)))
36a8fdfd
DL
163
164 # pretty hacky C types handling, but covers what we need
165
166 ptrlevel = 0
00f0c399 167 while typs[-1] == "*":
36a8fdfd
DL
168 typs.pop(-1)
169 ptrlevel += 1
170
171 if ptrlevel > 0:
00f0c399 172 packtype = ("P", None)
36a8fdfd 173 if ptrlevel == 1:
00f0c399
DL
174 if typs[0] == "char":
175 packtype = ("P", str)
176 elif typs[0] == "struct" and typs[1] in self.clsmap:
177 packtype = ("P", self.clsmap[typs[1]])
178 elif typs[0] == "enum":
179 packtype = ("I",)
36a8fdfd
DL
180 elif typs[0] in self.packtypes:
181 packtype = (self.packtypes[typs[0]],)
00f0c399 182 elif typs[0] == "struct":
36a8fdfd
DL
183 if typs[1] in self.clsmap:
184 packtype = (self.clsmap[typs[1]],)
185 else:
00f0c399
DL
186 raise ValueError(
187 "embedded struct %s not in extracted data" % (typs[1],)
188 )
36a8fdfd 189 else:
00f0c399
DL
190 raise ValueError(
191 "cannot decode field %s in struct %s (%s)"
192 % (cls.struct, field["name"], field["type"])
193 )
194
195 if "array" in field and typs[0] == "char":
196 packtype = ("%ds" % field["array"],)
197 out.append(mkname(field["name"]) + packtype)
198 elif "array" in field:
199 for i in range(0, field["array"]):
200 out.append(mkname("%s_%d" % (field["name"], i)) + packtype)
36a8fdfd 201 else:
00f0c399 202 out.append(mkname(field["name"]) + packtype)
36a8fdfd 203
00f0c399 204 # offset = field['offset'] + field['size']
36a8fdfd
DL
205
206 cls.fields = out
207
208 def __call__(self):
209 for cls in self.classes:
210 self.resolve(cls)
211
00f0c399 212
36a8fdfd 213def main():
00f0c399
DL
214 argp = argparse.ArgumentParser(description="FRR DWARF structure extractor")
215 argp.add_argument(
216 "-o",
217 dest="output",
218 type=str,
219 help="write JSON output",
220 default="python/xrefstructs.json",
221 )
222 argp.add_argument(
223 "-i",
224 dest="input",
225 type=str,
226 help="ELF file to read",
227 default="lib/.libs/libfrr.so",
228 )
36a8fdfd
DL
229 args = argp.parse_args()
230
231 out = extract(args.input)
00f0c399 232 with open(args.output + ".tmp", "w") as fd:
36a8fdfd 233 json.dump(out, fd, indent=2, sort_keys=True)
00f0c399
DL
234 os.rename(args.output + ".tmp", args.output)
235
36a8fdfd 236
00f0c399 237if __name__ == "__main__":
36a8fdfd 238 main()