summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbuild-aux/stack.c.gen52
l---------libmisc/include/assert.h1
2 files changed, 44 insertions, 9 deletions
diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen
index 80bea85..ae05a78 100755
--- a/build-aux/stack.c.gen
+++ b/build-aux/stack.c.gen
@@ -96,8 +96,9 @@ def parse_vcg(reader: typing.TextIO) -> typing.Iterator[VCGElem]:
class Node:
- # from .title (`static` functions are prefixed with the
- # compilation unit .c file, which is fine, we'll just leave it).
+ # from .title (`static` and `__weak` functions are prefixed with
+ # the compilation unit .c file. For static functions that's fine,
+ # but we'll have to handle it specially for __weak.).
funcname: str
# .label is "{funcname}\n{location}\n{nstatic} bytes (static}\n{ndynamic} dynamic objects"
location: str
@@ -108,16 +109,33 @@ class Node:
calls: set[str]
+re_location = re.compile(r"(?P<filename>.+):(?P<row>[0-9]+):(?P<col>[0-9]+)")
+
+
+def read_source(location: str) -> str:
+ m = re_location.fullmatch(location)
+ if not m:
+ raise ValueError(f"unexpected label value {repr(location)}")
+ filename = m.group("filename")
+ row = int(m.group("row")) - 1
+ col = int(m.group("col")) - 1
+ with open(m.group("filename"), "r") as fh:
+ return fh.readlines()[row][col:].rstrip()
+
+
def main(ci_fnames: list[str]) -> None:
- re_label = re.compile(
+ re_node_label = re.compile(
r"(?P<funcname>[^\n]+)\n"
+ r"(?P<location>[^\n]+:[0-9]+:[0-9]+)\n"
+ r"(?P<nstatic>[0-9]+) bytes \(static\)\n"
+ r"(?P<ndynamic>[0-9]+) dynamic objects",
flags=re.MULTILINE,
)
+ re_call_vcall = re.compile(r"VCALL\((?P<obj>[^,]+), (?P<meth>[^,)]+)[,)].*")
+ re_call_other = re.compile(r"(?P<func>[^(]+)\(.*")
graph: dict[str, Node] = dict()
+ qualified: dict[str, set[str]] = dict()
def handle_elem(elem: VCGElem) -> None:
match elem.typ:
@@ -131,7 +149,7 @@ def main(ci_fnames: list[str]) -> None:
node.funcname = v
case "label":
if elem.attrs.get("shape", "") != "ellipse":
- m = re_label.fullmatch(v)
+ m = re_node_label.fullmatch(v)
if not m:
raise ValueError(
f"unexpected label value {repr(v)}"
@@ -149,6 +167,11 @@ def main(ci_fnames: list[str]) -> None:
if node.funcname in graph:
raise ValueError(f"duplicate node {repr(node.funcname)}")
graph[node.funcname] = node
+ if ":" in node.funcname:
+ _, shortname = node.funcname.rsplit(":", 1)
+ if shortname not in qualified:
+ qualified[shortname] = set()
+ qualified[shortname].add(node.funcname)
case "edge":
caller: str | None = None
callee: str | None = None
@@ -166,6 +189,12 @@ def main(ci_fnames: list[str]) -> None:
raise ValueError(f"incomplete edge: {repr(elem.attrs)}")
if caller not in graph:
raise ValueError(f"unknown caller: {caller}")
+ if callee == "__indirect_call":
+ callstr = read_source(elem.attrs.get("label", ""))
+ if m := re_call_vcall.fullmatch(callstr):
+ callee += f":{m.group('obj')}->vtable->{m.group('meth')}"
+ elif m := re_call_other.fullmatch(callstr):
+ callee += f":{m.group('func')}"
graph[caller].calls.add(callee)
case _:
raise ValueError(f"unknown elem type {repr(elem.typ)}")
@@ -183,7 +212,11 @@ def main(ci_fnames: list[str]) -> None:
def nstatic(funcname: str, chain: list[str] = []) -> int:
if funcname not in graph:
if f"__wrap_{funcname}" in graph:
+ # Handle `ld --wrap` functions
funcname = f"__wrap_{funcname}"
+ elif funcname in qualified and len(qualified[funcname]) == 1:
+ # Handle `__weak` functions
+ funcname = sorted(qualified[funcname])[0]
else:
missing.add(funcname)
return 0
@@ -196,18 +229,19 @@ def main(ci_fnames: list[str]) -> None:
)
namelen = max(len(name) for name in graph if name.endswith("_cr"))
- print(("=" * namelen) + " =======")
+ numlen = max(len(str(nstatic(name))) for name in graph if name.endswith("_cr"))
+ print(("=" * namelen) + " " + "=" * numlen)
for funcname in graph:
if funcname.endswith("_cr"):
- print(f"{funcname.ljust(namelen)} {nstatic(funcname)}")
+ print(f"{funcname.ljust(namelen)} {str(nstatic(funcname)).rjust(numlen)}")
- print(("=" * namelen) + " =======")
+ print(("=" * namelen) + " " + "=" * numlen)
for funcname in sorted(missing):
- print(f"{funcname}\tmissing")
+ print(f"warning: missing: {funcname}")
for cycle in sorted(cycles):
- print(f"cycle: {cycle}")
+ print(f"warning: cycle: {cycle}")
print("*/")
diff --git a/libmisc/include/assert.h b/libmisc/include/assert.h
new file mode 120000
index 0000000..8473e36
--- /dev/null
+++ b/libmisc/include/assert.h
@@ -0,0 +1 @@
+libmisc/assert.h \ No newline at end of file