Last active
March 5, 2022 05:38
-
-
Save kishorviswanathan/13d9184db5222f35921b0a10240dbc0e to your computer and use it in GitHub Desktop.
Compare or find diff for device tree files (dtb)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
import sys | |
import fdt | |
import argparse | |
# Return root node for given dtb file | |
def get_root(dtb): | |
try: | |
with open(dtb, 'rb') as infile: | |
fdt_object = fdt.parse_dtb(infile.read()) | |
root = fdt_object.get_node('/') | |
return root | |
except Exception as e: | |
print(e) | |
sys.exit(1) | |
# Get list of keys from list of objects | |
def keys(items): | |
return [p.name for p in items] | |
# Find if a node is present in another node | |
def find_node(node, name): | |
for n in keys(node.nodes): | |
if n == name: | |
return node.get_subnode(n) | |
elif (args.ignore_names) and ('@' in n and '@' in name) and (n.split('@')[1] == name.split('@')[1]): | |
return node.get_subnode(n) | |
return None | |
# Compare two nodes and return newline padded string | |
def diff_nodes(node1, node2, depth=0): | |
# Store generated dts | |
dts1 = [] | |
dts2 = [] | |
# Spacers | |
sp_header = " " * depth | |
sp_param = " " * (depth+1) | |
# Append header | |
dts1.append("%s%s {\n" % (sp_header, node1.name)) | |
dts2.append("%s%s {\n" % (sp_header, node2.name)) | |
# Compare properties | |
for p in node1.props: | |
if p.name not in keys(node2.props): | |
# Property is missing in node2 | |
dts1.append("%s%s" % (sp_param, p.to_dts())) | |
dts2.append("\n") | |
else: | |
# Property exists in both nodes | |
dts1.append("%s%s" % (sp_param, p.to_dts())) | |
dts2.append("%s%s" % | |
(sp_param, node2.get_property(p.name).to_dts())) | |
# Compare properties present in node2 but not in node1 | |
for p in node2.props: | |
if p.name not in keys(node1.props): | |
# Property is missing in node1 | |
dts1.append("\n") | |
dts2.append("%s%s" % (sp_param, p.to_dts())) | |
# Compare subnodes | |
for n in node1.nodes: | |
node = find_node(node2, n.name) | |
if not node: | |
# Subnode is missing in node2 | |
lines = n.to_dts().splitlines() | |
for line in lines: | |
dts1.append("%s%s\n" % (sp_param, line)) | |
dts2 += ["\n"] * len(lines) | |
else: | |
# Subnode exists in both nodes, recurse | |
d1, d2 = diff_nodes(n, node, depth+1) | |
dts1 += d1 | |
dts2 += d2 | |
# Compare subnodes present in node2 but not in node1 | |
for n in node2.nodes: | |
node = find_node(node1, n.name) | |
if not node: | |
# Subnode is missing in node1 | |
lines = n.to_dts().splitlines() | |
for line in lines: | |
dts2.append("%s%s\n" % (sp_param, line)) | |
dts1 += ["\n"] * len(lines) | |
# Append footer | |
dts1.append("%s};\n" % (sp_header)) | |
dts2.append("%s};\n" % (sp_header)) | |
# Return dts strings | |
return dts1, dts2 | |
# Parse arguments | |
parser = argparse.ArgumentParser( | |
description="Compare and print differences between two device trees") | |
parser.add_argument("dtb1", help="First device tree") | |
parser.add_argument("dtb2", help="Second device tree") | |
parser.add_argument("--ignore-names", action="store_true", | |
help="Ignore node names and match by unit address if exact match fails") | |
parser.add_argument("--column-width", action="store", | |
default=90, help="Width for output columns. Default: 90") | |
args = parser.parse_args() | |
# Read dtb files | |
root1 = get_root(args.dtb1) | |
root2 = get_root(args.dtb2) | |
# Compare nodes | |
out1, out2 = diff_nodes(root1, root2) | |
stats = { | |
"common": 0, | |
"unique1": 0, | |
"unique2": 0, | |
"changed": 0, | |
} | |
try: | |
# Print output | |
for i in range(len(out1)): | |
# Compare lines | |
if (out1[i] == out2[i]): | |
# Lines are the same | |
color = "" | |
symbol = " " | |
stats['common'] += 1 | |
elif (out2[i] == "\n"): | |
# Line is missing in dtb2 | |
color = "\033[31m" | |
symbol = "<" | |
stats['unique1'] += 1 | |
elif (out1[i] == "\n"): | |
# Line is missing in dtb1 | |
color = "\033[32m" | |
symbol = ">" | |
stats['unique2'] += 1 | |
else: | |
# Lines are different | |
color = "\033[36m" | |
symbol = "|" | |
stats["changed"] += 1 | |
# Print line | |
print("{}{:{width}.{width}} {} {:{width}.{width}}\033[0m".format( | |
color, | |
out1[i].replace('\n', ''), | |
symbol, | |
out2[i].replace('\n', ''), | |
width=args.column_width | |
)) | |
print("\nSummary:\nCommon Lines: {}\nChanged Lines: {}\nUnique to {}: {}\nUnique to {}: {}".format( | |
stats['common'], | |
stats['changed'], | |
args.dtb1, | |
stats['unique1'], | |
args.dtb2, | |
stats['unique2'] | |
)) | |
except BrokenPipeError: | |
pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment