Skip to content

Instantly share code, notes, and snippets.

@kishorviswanathan
Last active March 5, 2022 05:38
Show Gist options
  • Save kishorviswanathan/13d9184db5222f35921b0a10240dbc0e to your computer and use it in GitHub Desktop.
Save kishorviswanathan/13d9184db5222f35921b0a10240dbc0e to your computer and use it in GitHub Desktop.
Compare or find diff for device tree files (dtb)
#!/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