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