explode_asserts.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. #!/usr/bin/env python3
  2. import re
  3. import sys
  4. PATTERN = ['LFS_ASSERT', 'assert']
  5. PREFIX = 'LFS'
  6. MAXWIDTH = 16
  7. ASSERT = "__{PREFIX}_ASSERT_{TYPE}_{COMP}"
  8. FAIL = """
  9. __attribute__((unused))
  10. static void __{prefix}_assert_fail_{type}(
  11. const char *file, int line, const char *comp,
  12. {ctype} lh, size_t lsize,
  13. {ctype} rh, size_t rsize) {{
  14. printf("%s:%d:assert: assert failed with ", file, line);
  15. __{prefix}_assert_print_{type}(lh, lsize);
  16. printf(", expected %s ", comp);
  17. __{prefix}_assert_print_{type}(rh, rsize);
  18. printf("\\n");
  19. fflush(NULL);
  20. raise(SIGABRT);
  21. }}
  22. """
  23. COMP = {
  24. '==': 'eq',
  25. '!=': 'ne',
  26. '<=': 'le',
  27. '>=': 'ge',
  28. '<': 'lt',
  29. '>': 'gt',
  30. }
  31. TYPE = {
  32. 'int': {
  33. 'ctype': 'intmax_t',
  34. 'fail': FAIL,
  35. 'print': """
  36. __attribute__((unused))
  37. static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{
  38. (void)size;
  39. printf("%"PRIiMAX, v);
  40. }}
  41. """,
  42. 'assert': """
  43. #define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh)
  44. do {{
  45. __typeof__(lh) _lh = lh;
  46. __typeof__(lh) _rh = (__typeof__(lh))rh;
  47. if (!(_lh {op} _rh)) {{
  48. __{prefix}_assert_fail_{type}(file, line, "{comp}",
  49. (intmax_t)_lh, 0, (intmax_t)_rh, 0);
  50. }}
  51. }} while (0)
  52. """
  53. },
  54. 'bool': {
  55. 'ctype': 'bool',
  56. 'fail': FAIL,
  57. 'print': """
  58. __attribute__((unused))
  59. static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{
  60. (void)size;
  61. printf("%s", v ? "true" : "false");
  62. }}
  63. """,
  64. 'assert': """
  65. #define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh)
  66. do {{
  67. bool _lh = !!(lh);
  68. bool _rh = !!(rh);
  69. if (!(_lh {op} _rh)) {{
  70. __{prefix}_assert_fail_{type}(file, line, "{comp}",
  71. _lh, 0, _rh, 0);
  72. }}
  73. }} while (0)
  74. """
  75. },
  76. 'mem': {
  77. 'ctype': 'const void *',
  78. 'fail': FAIL,
  79. 'print': """
  80. __attribute__((unused))
  81. static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{
  82. const uint8_t *s = v;
  83. printf("\\\"");
  84. for (size_t i = 0; i < size && i < {maxwidth}; i++) {{
  85. if (s[i] >= ' ' && s[i] <= '~') {{
  86. printf("%c", s[i]);
  87. }} else {{
  88. printf("\\\\x%02x", s[i]);
  89. }}
  90. }}
  91. if (size > {maxwidth}) {{
  92. printf("...");
  93. }}
  94. printf("\\\"");
  95. }}
  96. """,
  97. 'assert': """
  98. #define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh, size)
  99. do {{
  100. const void *_lh = lh;
  101. const void *_rh = rh;
  102. if (!(memcmp(_lh, _rh, size) {op} 0)) {{
  103. __{prefix}_assert_fail_{type}(file, line, "{comp}",
  104. _lh, size, _rh, size);
  105. }}
  106. }} while (0)
  107. """
  108. },
  109. 'str': {
  110. 'ctype': 'const char *',
  111. 'fail': FAIL,
  112. 'print': """
  113. __attribute__((unused))
  114. static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{
  115. __{prefix}_assert_print_mem(v, size);
  116. }}
  117. """,
  118. 'assert': """
  119. #define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh)
  120. do {{
  121. const char *_lh = lh;
  122. const char *_rh = rh;
  123. if (!(strcmp(_lh, _rh) {op} 0)) {{
  124. __{prefix}_assert_fail_{type}(file, line, "{comp}",
  125. _lh, strlen(_lh), _rh, strlen(_rh));
  126. }}
  127. }} while (0)
  128. """
  129. }
  130. }
  131. def openio(path, mode='r'):
  132. if path == '-':
  133. if 'r' in mode:
  134. return os.fdopen(os.dup(sys.stdin.fileno()), 'r')
  135. else:
  136. return os.fdopen(os.dup(sys.stdout.fileno()), 'w')
  137. else:
  138. return open(path, mode)
  139. def mkdecls(outf, maxwidth=16):
  140. outf.write("#include <stdio.h>\n")
  141. outf.write("#include <stdbool.h>\n")
  142. outf.write("#include <stdint.h>\n")
  143. outf.write("#include <inttypes.h>\n")
  144. outf.write("#include <signal.h>\n")
  145. for type, desc in sorted(TYPE.items()):
  146. format = {
  147. 'type': type.lower(), 'TYPE': type.upper(),
  148. 'ctype': desc['ctype'],
  149. 'prefix': PREFIX.lower(), 'PREFIX': PREFIX.upper(),
  150. 'maxwidth': maxwidth,
  151. }
  152. outf.write(re.sub('\s+', ' ',
  153. desc['print'].strip().format(**format))+'\n')
  154. outf.write(re.sub('\s+', ' ',
  155. desc['fail'].strip().format(**format))+'\n')
  156. for op, comp in sorted(COMP.items()):
  157. format.update({
  158. 'comp': comp.lower(), 'COMP': comp.upper(),
  159. 'op': op,
  160. })
  161. outf.write(re.sub('\s+', ' ',
  162. desc['assert'].strip().format(**format))+'\n')
  163. def mkassert(type, comp, lh, rh, size=None):
  164. format = {
  165. 'type': type.lower(), 'TYPE': type.upper(),
  166. 'comp': comp.lower(), 'COMP': comp.upper(),
  167. 'prefix': PREFIX.lower(), 'PREFIX': PREFIX.upper(),
  168. 'lh': lh.strip(' '),
  169. 'rh': rh.strip(' '),
  170. 'size': size,
  171. }
  172. if size:
  173. return ((ASSERT + '(__FILE__, __LINE__, {lh}, {rh}, {size})')
  174. .format(**format))
  175. else:
  176. return ((ASSERT + '(__FILE__, __LINE__, {lh}, {rh})')
  177. .format(**format))
  178. # simple recursive descent parser
  179. LEX = {
  180. 'ws': [r'(?:\s|\n|#.*?\n|//.*?\n|/\*.*?\*/)+'],
  181. 'assert': PATTERN,
  182. 'string': [r'"(?:\\.|[^"])*"', r"'(?:\\.|[^'])\'"],
  183. 'arrow': ['=>'],
  184. 'paren': ['\(', '\)'],
  185. 'op': ['strcmp', 'memcmp', '->'],
  186. 'comp': ['==', '!=', '<=', '>=', '<', '>'],
  187. 'logic': ['\&\&', '\|\|'],
  188. 'sep': [':', ';', '\{', '\}', ','],
  189. }
  190. class ParseFailure(Exception):
  191. def __init__(self, expected, found):
  192. self.expected = expected
  193. self.found = found
  194. def __str__(self):
  195. return "expected %r, found %s..." % (
  196. self.expected, repr(self.found)[:70])
  197. class Parse:
  198. def __init__(self, inf, lexemes):
  199. p = '|'.join('(?P<%s>%s)' % (n, '|'.join(l))
  200. for n, l in lexemes.items())
  201. p = re.compile(p, re.DOTALL)
  202. data = inf.read()
  203. tokens = []
  204. while True:
  205. m = p.search(data)
  206. if m:
  207. if m.start() > 0:
  208. tokens.append((None, data[:m.start()]))
  209. tokens.append((m.lastgroup, m.group()))
  210. data = data[m.end():]
  211. else:
  212. tokens.append((None, data))
  213. break
  214. self.tokens = tokens
  215. self.off = 0
  216. def lookahead(self, *pattern):
  217. if self.off < len(self.tokens):
  218. token = self.tokens[self.off]
  219. if token[0] in pattern or token[1] in pattern:
  220. self.m = token[1]
  221. return self.m
  222. self.m = None
  223. return self.m
  224. def accept(self, *patterns):
  225. m = self.lookahead(*patterns)
  226. if m is not None:
  227. self.off += 1
  228. return m
  229. def expect(self, *patterns):
  230. m = self.accept(*patterns)
  231. if not m:
  232. raise ParseFailure(patterns, self.tokens[self.off:])
  233. return m
  234. def push(self):
  235. return self.off
  236. def pop(self, state):
  237. self.off = state
  238. def passert(p):
  239. def pastr(p):
  240. p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
  241. p.expect('strcmp') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
  242. lh = pexpr(p) ; p.accept('ws')
  243. p.expect(',') ; p.accept('ws')
  244. rh = pexpr(p) ; p.accept('ws')
  245. p.expect(')') ; p.accept('ws')
  246. comp = p.expect('comp') ; p.accept('ws')
  247. p.expect('0') ; p.accept('ws')
  248. p.expect(')')
  249. return mkassert('str', COMP[comp], lh, rh)
  250. def pamem(p):
  251. p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
  252. p.expect('memcmp') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
  253. lh = pexpr(p) ; p.accept('ws')
  254. p.expect(',') ; p.accept('ws')
  255. rh = pexpr(p) ; p.accept('ws')
  256. p.expect(',') ; p.accept('ws')
  257. size = pexpr(p) ; p.accept('ws')
  258. p.expect(')') ; p.accept('ws')
  259. comp = p.expect('comp') ; p.accept('ws')
  260. p.expect('0') ; p.accept('ws')
  261. p.expect(')')
  262. return mkassert('mem', COMP[comp], lh, rh, size)
  263. def paint(p):
  264. p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
  265. lh = pexpr(p) ; p.accept('ws')
  266. comp = p.expect('comp') ; p.accept('ws')
  267. rh = pexpr(p) ; p.accept('ws')
  268. p.expect(')')
  269. return mkassert('int', COMP[comp], lh, rh)
  270. def pabool(p):
  271. p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
  272. lh = pexprs(p) ; p.accept('ws')
  273. p.expect(')')
  274. return mkassert('bool', 'eq', lh, 'true')
  275. def pa(p):
  276. return p.expect('assert')
  277. state = p.push()
  278. lastf = None
  279. for pa in [pastr, pamem, paint, pabool, pa]:
  280. try:
  281. return pa(p)
  282. except ParseFailure as f:
  283. p.pop(state)
  284. lastf = f
  285. else:
  286. raise lastf
  287. def pexpr(p):
  288. res = []
  289. while True:
  290. if p.accept('('):
  291. res.append(p.m)
  292. while True:
  293. res.append(pexprs(p))
  294. if p.accept('sep'):
  295. res.append(p.m)
  296. else:
  297. break
  298. res.append(p.expect(')'))
  299. elif p.lookahead('assert'):
  300. res.append(passert(p))
  301. elif p.accept('assert', 'ws', 'string', 'op', None):
  302. res.append(p.m)
  303. else:
  304. return ''.join(res)
  305. def pexprs(p):
  306. res = []
  307. while True:
  308. res.append(pexpr(p))
  309. if p.accept('comp', 'logic', ','):
  310. res.append(p.m)
  311. else:
  312. return ''.join(res)
  313. def pstmt(p):
  314. ws = p.accept('ws') or ''
  315. lh = pexprs(p)
  316. if p.accept('=>'):
  317. rh = pexprs(p)
  318. return ws + mkassert('int', 'eq', lh, rh)
  319. else:
  320. return ws + lh
  321. def main(args):
  322. with openio(args.input or '-', 'r') as inf:
  323. with openio(args.output or '-', 'w') as outf:
  324. lexemes = LEX.copy()
  325. if args.pattern:
  326. lexemes['assert'] = args.pattern
  327. p = Parse(inf, lexemes)
  328. # write extra verbose asserts
  329. mkdecls(outf, maxwidth=args.maxwidth)
  330. if args.input:
  331. outf.write("#line %d \"%s\"\n" % (1, args.input))
  332. # parse and write out stmt at a time
  333. try:
  334. while True:
  335. outf.write(pstmt(p))
  336. if p.accept('sep'):
  337. outf.write(p.m)
  338. else:
  339. break
  340. except ParseFailure as f:
  341. pass
  342. for i in range(p.off, len(p.tokens)):
  343. outf.write(p.tokens[i][1])
  344. if __name__ == "__main__":
  345. import argparse
  346. parser = argparse.ArgumentParser(
  347. description="Cpp step that increases assert verbosity")
  348. parser.add_argument('input', nargs='?',
  349. help="Input C file after cpp.")
  350. parser.add_argument('-o', '--output', required=True,
  351. help="Output C file.")
  352. parser.add_argument('-p', '--pattern', action='append',
  353. help="Patterns to search for starting an assert statement.")
  354. parser.add_argument('--maxwidth', default=MAXWIDTH, type=int,
  355. help="Maximum number of characters to display for strcmp and memcmp.")
  356. main(parser.parse_args())