mirror of
https://gitee.com/openharmony/third_party_littlefs
synced 2024-11-23 14:59:50 +00:00
a7dfae4526
- Changed readmdir.py to print the metadata pair and revision count, which is useful when debugging commit issues. - Added truncated data view to readtree.py by default. This does mean readtree.py must read all files on the filesystem to show the truncated data, hopefully this does not end up being a problem. - Made overall representation hopefully more readable, including moving superblock under the root dir, userattrs under files, fixing a gstate rendering issue. - Added rendering of soft-tails as dotted-arrows, hopefully this isn't too noisy. - Fixed explode_asserts.py off-by-1 in #line mapping caused by a strip call in the assert generation eating newlines. The script matches line numbers between the original+modified files by emitting assert statements that use the same number of lines. An off-by-1 here causes the entire file to map lines incorrectly, which can be very annoying.
384 lines
11 KiB
Python
Executable File
384 lines
11 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import re
|
|
import sys
|
|
|
|
PATTERN = ['LFS_ASSERT', 'assert']
|
|
PREFIX = 'LFS'
|
|
MAXWIDTH = 16
|
|
|
|
ASSERT = "__{PREFIX}_ASSERT_{TYPE}_{COMP}"
|
|
FAIL = """
|
|
__attribute__((unused))
|
|
static void __{prefix}_assert_fail_{type}(
|
|
const char *file, int line, const char *comp,
|
|
{ctype} lh, size_t lsize,
|
|
{ctype} rh, size_t rsize) {{
|
|
printf("%s:%d:assert: assert failed with ", file, line);
|
|
__{prefix}_assert_print_{type}(lh, lsize);
|
|
printf(", expected %s ", comp);
|
|
__{prefix}_assert_print_{type}(rh, rsize);
|
|
printf("\\n");
|
|
fflush(NULL);
|
|
raise(SIGABRT);
|
|
}}
|
|
"""
|
|
|
|
COMP = {
|
|
'==': 'eq',
|
|
'!=': 'ne',
|
|
'<=': 'le',
|
|
'>=': 'ge',
|
|
'<': 'lt',
|
|
'>': 'gt',
|
|
}
|
|
|
|
TYPE = {
|
|
'int': {
|
|
'ctype': 'intmax_t',
|
|
'fail': FAIL,
|
|
'print': """
|
|
__attribute__((unused))
|
|
static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{
|
|
(void)size;
|
|
printf("%"PRIiMAX, v);
|
|
}}
|
|
""",
|
|
'assert': """
|
|
#define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh)
|
|
do {{
|
|
__typeof__(lh) _lh = lh;
|
|
__typeof__(lh) _rh = (__typeof__(lh))rh;
|
|
if (!(_lh {op} _rh)) {{
|
|
__{prefix}_assert_fail_{type}(file, line, "{comp}",
|
|
(intmax_t)_lh, 0, (intmax_t)_rh, 0);
|
|
}}
|
|
}} while (0)
|
|
"""
|
|
},
|
|
'bool': {
|
|
'ctype': 'bool',
|
|
'fail': FAIL,
|
|
'print': """
|
|
__attribute__((unused))
|
|
static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{
|
|
(void)size;
|
|
printf("%s", v ? "true" : "false");
|
|
}}
|
|
""",
|
|
'assert': """
|
|
#define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh)
|
|
do {{
|
|
bool _lh = !!(lh);
|
|
bool _rh = !!(rh);
|
|
if (!(_lh {op} _rh)) {{
|
|
__{prefix}_assert_fail_{type}(file, line, "{comp}",
|
|
_lh, 0, _rh, 0);
|
|
}}
|
|
}} while (0)
|
|
"""
|
|
},
|
|
'mem': {
|
|
'ctype': 'const void *',
|
|
'fail': FAIL,
|
|
'print': """
|
|
__attribute__((unused))
|
|
static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{
|
|
const uint8_t *s = v;
|
|
printf("\\\"");
|
|
for (size_t i = 0; i < size && i < {maxwidth}; i++) {{
|
|
if (s[i] >= ' ' && s[i] <= '~') {{
|
|
printf("%c", s[i]);
|
|
}} else {{
|
|
printf("\\\\x%02x", s[i]);
|
|
}}
|
|
}}
|
|
if (size > {maxwidth}) {{
|
|
printf("...");
|
|
}}
|
|
printf("\\\"");
|
|
}}
|
|
""",
|
|
'assert': """
|
|
#define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh, size)
|
|
do {{
|
|
const void *_lh = lh;
|
|
const void *_rh = rh;
|
|
if (!(memcmp(_lh, _rh, size) {op} 0)) {{
|
|
__{prefix}_assert_fail_{type}(file, line, "{comp}",
|
|
_lh, size, _rh, size);
|
|
}}
|
|
}} while (0)
|
|
"""
|
|
},
|
|
'str': {
|
|
'ctype': 'const char *',
|
|
'fail': FAIL,
|
|
'print': """
|
|
__attribute__((unused))
|
|
static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{
|
|
__{prefix}_assert_print_mem(v, size);
|
|
}}
|
|
""",
|
|
'assert': """
|
|
#define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh)
|
|
do {{
|
|
const char *_lh = lh;
|
|
const char *_rh = rh;
|
|
if (!(strcmp(_lh, _rh) {op} 0)) {{
|
|
__{prefix}_assert_fail_{type}(file, line, "{comp}",
|
|
_lh, strlen(_lh), _rh, strlen(_rh));
|
|
}}
|
|
}} while (0)
|
|
"""
|
|
}
|
|
}
|
|
|
|
def mkdecls(outf, maxwidth=16):
|
|
outf.write("#include <stdio.h>\n")
|
|
outf.write("#include <stdbool.h>\n")
|
|
outf.write("#include <stdint.h>\n")
|
|
outf.write("#include <inttypes.h>\n")
|
|
outf.write("#include <signal.h>\n")
|
|
|
|
for type, desc in sorted(TYPE.items()):
|
|
format = {
|
|
'type': type.lower(), 'TYPE': type.upper(),
|
|
'ctype': desc['ctype'],
|
|
'prefix': PREFIX.lower(), 'PREFIX': PREFIX.upper(),
|
|
'maxwidth': maxwidth,
|
|
}
|
|
outf.write(re.sub('\s+', ' ',
|
|
desc['print'].strip().format(**format))+'\n')
|
|
outf.write(re.sub('\s+', ' ',
|
|
desc['fail'].strip().format(**format))+'\n')
|
|
|
|
for op, comp in sorted(COMP.items()):
|
|
format.update({
|
|
'comp': comp.lower(), 'COMP': comp.upper(),
|
|
'op': op,
|
|
})
|
|
outf.write(re.sub('\s+', ' ',
|
|
desc['assert'].strip().format(**format))+'\n')
|
|
|
|
def mkassert(type, comp, lh, rh, size=None):
|
|
format = {
|
|
'type': type.lower(), 'TYPE': type.upper(),
|
|
'comp': comp.lower(), 'COMP': comp.upper(),
|
|
'prefix': PREFIX.lower(), 'PREFIX': PREFIX.upper(),
|
|
'lh': lh.strip(' '),
|
|
'rh': rh.strip(' '),
|
|
'size': size,
|
|
}
|
|
if size:
|
|
return ((ASSERT + '(__FILE__, __LINE__, {lh}, {rh}, {size})')
|
|
.format(**format))
|
|
else:
|
|
return ((ASSERT + '(__FILE__, __LINE__, {lh}, {rh})')
|
|
.format(**format))
|
|
|
|
|
|
# simple recursive descent parser
|
|
LEX = {
|
|
'ws': [r'(?:\s|\n|#.*?\n|//.*?\n|/\*.*?\*/)+'],
|
|
'assert': PATTERN,
|
|
'string': [r'"(?:\\.|[^"])*"', r"'(?:\\.|[^'])\'"],
|
|
'arrow': ['=>'],
|
|
'paren': ['\(', '\)'],
|
|
'op': ['strcmp', 'memcmp', '->'],
|
|
'comp': ['==', '!=', '<=', '>=', '<', '>'],
|
|
'logic': ['\&\&', '\|\|'],
|
|
'sep': [':', ';', '\{', '\}', ','],
|
|
}
|
|
|
|
class ParseFailure(Exception):
|
|
def __init__(self, expected, found):
|
|
self.expected = expected
|
|
self.found = found
|
|
|
|
def __str__(self):
|
|
return "expected %r, found %s..." % (
|
|
self.expected, repr(self.found)[:70])
|
|
|
|
class Parse:
|
|
def __init__(self, inf, lexemes):
|
|
p = '|'.join('(?P<%s>%s)' % (n, '|'.join(l))
|
|
for n, l in lexemes.items())
|
|
p = re.compile(p, re.DOTALL)
|
|
data = inf.read()
|
|
tokens = []
|
|
while True:
|
|
m = p.search(data)
|
|
if m:
|
|
if m.start() > 0:
|
|
tokens.append((None, data[:m.start()]))
|
|
tokens.append((m.lastgroup, m.group()))
|
|
data = data[m.end():]
|
|
else:
|
|
tokens.append((None, data))
|
|
break
|
|
self.tokens = tokens
|
|
self.off = 0
|
|
|
|
def lookahead(self, *pattern):
|
|
if self.off < len(self.tokens):
|
|
token = self.tokens[self.off]
|
|
if token[0] in pattern or token[1] in pattern:
|
|
self.m = token[1]
|
|
return self.m
|
|
self.m = None
|
|
return self.m
|
|
|
|
def accept(self, *patterns):
|
|
m = self.lookahead(*patterns)
|
|
if m is not None:
|
|
self.off += 1
|
|
return m
|
|
|
|
def expect(self, *patterns):
|
|
m = self.accept(*patterns)
|
|
if not m:
|
|
raise ParseFailure(patterns, self.tokens[self.off:])
|
|
return m
|
|
|
|
def push(self):
|
|
return self.off
|
|
|
|
def pop(self, state):
|
|
self.off = state
|
|
|
|
def passert(p):
|
|
def pastr(p):
|
|
p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
|
|
p.expect('strcmp') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
|
|
lh = pexpr(p) ; p.accept('ws')
|
|
p.expect(',') ; p.accept('ws')
|
|
rh = pexpr(p) ; p.accept('ws')
|
|
p.expect(')') ; p.accept('ws')
|
|
comp = p.expect('comp') ; p.accept('ws')
|
|
p.expect('0') ; p.accept('ws')
|
|
p.expect(')')
|
|
return mkassert('str', COMP[comp], lh, rh)
|
|
|
|
def pamem(p):
|
|
p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
|
|
p.expect('memcmp') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
|
|
lh = pexpr(p) ; p.accept('ws')
|
|
p.expect(',') ; p.accept('ws')
|
|
rh = pexpr(p) ; p.accept('ws')
|
|
p.expect(',') ; p.accept('ws')
|
|
size = pexpr(p) ; p.accept('ws')
|
|
p.expect(')') ; p.accept('ws')
|
|
comp = p.expect('comp') ; p.accept('ws')
|
|
p.expect('0') ; p.accept('ws')
|
|
p.expect(')')
|
|
return mkassert('mem', COMP[comp], lh, rh, size)
|
|
|
|
def paint(p):
|
|
p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
|
|
lh = pexpr(p) ; p.accept('ws')
|
|
comp = p.expect('comp') ; p.accept('ws')
|
|
rh = pexpr(p) ; p.accept('ws')
|
|
p.expect(')')
|
|
return mkassert('int', COMP[comp], lh, rh)
|
|
|
|
def pabool(p):
|
|
p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
|
|
lh = pexprs(p) ; p.accept('ws')
|
|
p.expect(')')
|
|
return mkassert('bool', 'eq', lh, 'true')
|
|
|
|
def pa(p):
|
|
return p.expect('assert')
|
|
|
|
state = p.push()
|
|
lastf = None
|
|
for pa in [pastr, pamem, paint, pabool, pa]:
|
|
try:
|
|
return pa(p)
|
|
except ParseFailure as f:
|
|
p.pop(state)
|
|
lastf = f
|
|
else:
|
|
raise lastf
|
|
|
|
def pexpr(p):
|
|
res = []
|
|
while True:
|
|
if p.accept('('):
|
|
res.append(p.m)
|
|
while True:
|
|
res.append(pexprs(p))
|
|
if p.accept('sep'):
|
|
res.append(p.m)
|
|
else:
|
|
break
|
|
res.append(p.expect(')'))
|
|
elif p.lookahead('assert'):
|
|
res.append(passert(p))
|
|
elif p.accept('assert', 'ws', 'string', 'op', None):
|
|
res.append(p.m)
|
|
else:
|
|
return ''.join(res)
|
|
|
|
def pexprs(p):
|
|
res = []
|
|
while True:
|
|
res.append(pexpr(p))
|
|
if p.accept('comp', 'logic', ','):
|
|
res.append(p.m)
|
|
else:
|
|
return ''.join(res)
|
|
|
|
def pstmt(p):
|
|
ws = p.accept('ws') or ''
|
|
lh = pexprs(p)
|
|
if p.accept('=>'):
|
|
rh = pexprs(p)
|
|
return ws + mkassert('int', 'eq', lh, rh)
|
|
else:
|
|
return ws + lh
|
|
|
|
|
|
def main(args):
|
|
inf = open(args.input, 'r') if args.input else sys.stdin
|
|
outf = open(args.output, 'w') if args.output else sys.stdout
|
|
|
|
lexemes = LEX.copy()
|
|
if args.pattern:
|
|
lexemes['assert'] = args.pattern
|
|
p = Parse(inf, lexemes)
|
|
|
|
# write extra verbose asserts
|
|
mkdecls(outf, maxwidth=args.maxwidth)
|
|
if args.input:
|
|
outf.write("#line %d \"%s\"\n" % (1, args.input))
|
|
|
|
# parse and write out stmt at a time
|
|
try:
|
|
while True:
|
|
outf.write(pstmt(p))
|
|
if p.accept('sep'):
|
|
outf.write(p.m)
|
|
else:
|
|
break
|
|
except ParseFailure as f:
|
|
pass
|
|
|
|
for i in range(p.off, len(p.tokens)):
|
|
outf.write(p.tokens[i][1])
|
|
|
|
if __name__ == "__main__":
|
|
import argparse
|
|
parser = argparse.ArgumentParser(
|
|
description="Cpp step that increases assert verbosity")
|
|
parser.add_argument('input', nargs='?',
|
|
help="Input C file after cpp.")
|
|
parser.add_argument('-o', '--output', required=True,
|
|
help="Output C file.")
|
|
parser.add_argument('-p', '--pattern', action='append',
|
|
help="Patterns to search for starting an assert statement.")
|
|
parser.add_argument('--maxwidth', default=MAXWIDTH, type=int,
|
|
help="Maximum number of characters to display for strcmp and memcmp.")
|
|
main(parser.parse_args())
|