[v2,7/7] devtools: add script to generate DPDK dependency graphs

Message ID 20240802124411.485430-8-bruce.richardson@intel.com (mailing list archive)
State New
Delegated to: Thomas Monjalon
Headers
Series record and rework component dependencies |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/github-robot: build success github build: passed
ci/iol-mellanox-Performance success Performance Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-broadcom-Functional success Functional Testing PASS
ci/Intel-compilation success Compilation OK
ci/iol-abi-testing success Testing PASS
ci/intel-Testing success Testing PASS
ci/iol-compile-amd64-testing success Testing PASS
ci/iol-unit-amd64-testing success Testing PASS
ci/iol-compile-arm64-testing fail Testing issues
ci/iol-unit-arm64-testing success Testing PASS
ci/intel-Functional success Functional PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-sample-apps-testing success Testing PASS

Commit Message

Bruce Richardson Aug. 2, 2024, 12:44 p.m. UTC
Rather than the single monolithic graph that would be output from the
deps.dot file in a build directory, we can post-process that to generate
simpler graphs for different tasks. This new "draw_dependency_graphs"
script takes the "deps.dot" as input and generates an output file that
has the nodes categorized, filtering them based off the requested node
or category. For example, use "--component net_ice" to show the
dependency tree from that driver, or "--category lib" to show just the
library dependency tree.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
 devtools/draw-dependency-graphs.py | 136 +++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)
 create mode 100755 devtools/draw-dependency-graphs.py
  

Patch

diff --git a/devtools/draw-dependency-graphs.py b/devtools/draw-dependency-graphs.py
new file mode 100755
index 0000000000..826d89d5ce
--- /dev/null
+++ b/devtools/draw-dependency-graphs.py
@@ -0,0 +1,136 @@ 
+#! /usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2024 Intel Corporation
+
+import argparse
+import ast
+
+
+driver_classes = [
+    "baseband",
+    "bus",
+    "common",
+    "compress",
+    "crypto",
+    "dma",
+    "event",
+    "gpu",
+    "mempool",
+    "ml",
+    "net",
+    "raw",
+    "regex",
+    "vdpa",
+]
+
+
+def component_type(name: str):
+    if name.startswith("dpdk-"):
+        return "app"
+
+    nameparts = name.split("_", 1)
+    if len(nameparts) > 1 and nameparts[0] in driver_classes:
+        return f"drivers/{nameparts[0]}"
+
+    return "lib"
+
+
+def read_deps_list(lines: list[str]):
+    deps_data = {}
+    for ln in lines:
+        if ln.startswith("digraph") or ln == "}":
+            continue
+
+        if "->" in ln:
+            component, deps = [s.strip() for s in ln.split("->")]
+            deps = ast.literal_eval(deps)
+        else:
+            component, deps = ln, {}
+
+        component = component.strip('"')
+        comp_class = component_type(component)
+
+        if comp_class not in deps_data.keys():
+            deps_data[comp_class] = {}
+        deps_data[comp_class][component] = deps
+    return deps_data
+
+
+def create_classified_graph(deps_data: dict[dict[list[str]]]):
+    yield ("digraph dpdk_dependencies {\n  overlap=false\n  model=subset\n")
+    for n, category in enumerate(deps_data.keys()):
+        yield (f'  subgraph cluster_{n} {{\n    label = "{category}"\n')
+        for component in deps_data[category].keys():
+            yield (
+                f'    "{component}" -> {deps_data[category][component]}\n'.replace(
+                    "'", '"'
+                )
+            )
+        yield ("  }\n")
+    yield ("}\n")
+
+
+def get_deps_for_component(
+    dep_data: dict[dict[list[str]]], component: str, comp_deps: set
+):
+    categories = dep_data.keys()
+    comp_deps.add(component)
+    for cat in categories:
+        if component in dep_data[cat].keys():
+            for dep in dep_data[cat][component]:
+                get_deps_for_component(dep_data, dep, comp_deps)
+
+
+def filter_deps(
+    dep_data: dict[dict[list[str]]], component: list[str]
+) -> dict[dict[list[str]]]:
+    components = set()
+    for comp in component:
+        get_deps_for_component(dep_data, comp, components)
+
+    retval = {}
+    for category in dep_data.keys():
+        for comp in dep_data[category].keys():
+            if comp in components:
+                if category not in retval:
+                    retval[category] = {}
+                retval[category][comp] = dep_data[category][comp]
+    return retval
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        description="Utility to generate dependency tree graphs for DPDK"
+    )
+    parser.add_argument(
+        "--component",
+        type=str,
+        help="Only output hierarchy from specified component down.",
+    )
+    parser.add_argument(
+        "--category",
+        type=str,
+        help="Output hierarchy for all components in given category, e.g. lib, app, drivers/net, etc.",
+    )
+    parser.add_argument(
+        "input_file",
+        type=argparse.FileType("r"),
+        help="Path to the deps.dot file from a DPDK build directory",
+    )
+    parser.add_argument(
+        "output_file",
+        type=argparse.FileType("w"),
+        help="Path to the desired output dot file",
+    )
+    args = parser.parse_args()
+
+    deps = read_deps_list([ln.strip() for ln in args.input_file.readlines()])
+    if args.component:
+        deps = filter_deps(deps, [args.component])
+    elif args.category:
+        deps = filter_deps(deps, deps[args.category].keys())
+    args.output_file.writelines(create_classified_graph(deps))
+
+
+if __name__ == "__main__":
+    main()