scripts: gen_handles: output dependency graph

Output the final dependency graph as a `.dot` file, which when rendered
by graphviz can be easier to comprehend than the text descriptions.

This output is optional in that it will not be generated if `graphviz`
is not installed.

Signed-off-by: Jordan Yates <jordan.yates@data61.csiro.au>
This commit is contained in:
Jordan Yates 2022-07-10 13:46:17 +10:00 committed by Kumar Gala
parent 8d17e857a1
commit 29942475c5
4 changed files with 37 additions and 0 deletions

View file

@ -872,6 +872,7 @@ if(CONFIG_HAS_DTS)
${PYTHON_EXECUTABLE}
${ZEPHYR_BASE}/scripts/build/gen_handles.py
--output-source dev_handles.c
--output-graphviz dev_graph.dot
--num-dynamic-devices ${number_of_dynamic_devices}
--kernel $<TARGET_FILE:${ZEPHYR_LINK_STAGE_EXECUTABLE}>
--zephyr-base ${ZEPHYR_BASE}

View file

@ -254,3 +254,25 @@ class ZephyrElf:
# Link injected devices to each other
self._link_injected(devices_by_ord)
def device_dependency_graph(self, title, comment):
"""
Construct a graphviz Digraph of the relationships between devices.
"""
import graphviz
dot = graphviz.Digraph(title, comment=comment)
# Split iteration so nodes and edges are grouped in source
for dev in self.devices:
if dev.ordinal == DeviceOrdinals.DEVICE_HANDLE_NULL:
text = '{:s}\\nHandle: {:d}'.format(dev.sym.name, dev.handle)
else:
n = self.edt.dep_ord2node[dev.ordinal]
label = n.labels[0] if n.labels else n.label
text = '{:s}\\nOrdinal: {:d} | Handle: {:d}\\n{:s}'.format(
label, dev.ordinal, dev.handle, n.path
)
dot.node(str(dev.ordinal), text)
for dev in self.devices:
for sup in dev.devs_supports:
dot.edge(str(dev.ordinal), str(sup.ordinal))
return dot

View file

@ -51,6 +51,8 @@ def parse_args():
type=int, help="Input number of dynamic devices allowed")
parser.add_argument("-o", "--output-source", required=True,
help="Output source file")
parser.add_argument("-g", "--output-graphviz",
help="Output file for graphviz dependency graph")
parser.add_argument("-z", "--zephyr-base",
help="Path to current Zephyr base. If this argument \
is not provided the environment will be checked for \
@ -116,6 +118,15 @@ def main():
parsed_elf = ZephyrElf(args.kernel, edt, args.start_symbol)
if args.output_graphviz:
# Try and output the dependency tree
try:
dot = parsed_elf.device_dependency_graph('Device dependency graph', args.kernel)
with open(args.output_graphviz, 'w') as f:
f.write(dot.source)
except ImportError:
pass
with open(args.output_source, "w") as fp:
fp.write('#include <zephyr/device.h>\n')
fp.write('#include <zephyr/toolchain.h>\n')

View file

@ -26,3 +26,6 @@ grpcio-tools
# used by scripts/release/bug_bash.py for generating top ten bug squashers
PyGithub
# used to generate devicetree dependency graphs
graphviz