Index: scripts/slave/recipe_modules/isolate/resources/compare_build_artifacts.py |
diff --git a/scripts/slave/recipe_modules/isolate/resources/compare_build_artifacts.py b/scripts/slave/recipe_modules/isolate/resources/compare_build_artifacts.py |
index 3fd28b1b2722b9a0897b98c787d0c5c5544b777c..ff3866cc689e563a221cdb682ccca6909a172824 100755 |
--- a/scripts/slave/recipe_modules/isolate/resources/compare_build_artifacts.py |
+++ b/scripts/slave/recipe_modules/isolate/resources/compare_build_artifacts.py |
@@ -9,7 +9,9 @@ import difflib |
import json |
import optparse |
import os |
+import struct |
import sys |
+import time |
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) |
@@ -34,47 +36,86 @@ def get_files_to_compare(build_dir): |
check(os.path.join(build_dir, f))) |
-def compare_files(first_filepath, second_filepath): |
- """Compares two binaries and return the number of differences between them. |
+def diff_dict(a, b): |
+ """Returns a yaml-like textural diff of two dict. |
- Returns None if the files are equal, a string otherwise. |
+ It is currently optimized for the .isolated format. |
""" |
- file_len = os.stat(first_filepath).st_size |
- if file_len != os.stat(second_filepath).st_size: |
- return 'different size: %d != %d' % ( |
- file_len, os.stat(second_filepath).st_size) |
- |
- chunk_size = 1024 * 1024 |
+ out = '' |
+ for key in set(a) | set(b): |
+ va = a.get(key) |
+ vb = b.get(key) |
+ if va.__class__ != vb.__class__: |
+ out += '- %s:\n %r != %r\n' % (key, va, vb) |
+ elif isinstance(va, dict): |
+ c = diff_dict(va, vb) |
+ if c: |
+ out += '- %s:\n%s\n' % ( |
+ key, '\n'.join(' ' + l for l in c.splitlines())) |
+ elif va != vb: |
+ out += '- %s:\n %s != %s\n' % (key, va, vb) |
+ return out.rstrip() |
+ |
+ |
+def diff_binary(first_filepath, second_filepath, file_len): |
+ """Returns a compact binary diff if the diff is small enough.""" |
+ CHUNK_SIZE = 32 |
+ MAX_STREAMS = 10 |
diffs = 0 |
+ streams = [] |
+ offset = 0 |
with open(first_filepath, 'rb') as lhs: |
with open(second_filepath, 'rb') as rhs: |
while True: |
- lhs_data = lhs.read(chunk_size) |
- rhs_data = rhs.read(chunk_size) |
+ lhs_data = lhs.read(CHUNK_SIZE) |
+ rhs_data = rhs.read(CHUNK_SIZE) |
if not lhs_data: |
break |
- diffs += sum(l != r for l, r in zip(lhs_data, rhs_data)) |
+ if lhs_data != rhs_data: |
+ diffs += sum(l != r for l, r in zip(lhs_data, rhs_data)) |
+ if streams is not None: |
+ if len(streams) < MAX_STREAMS: |
+ streams.append((offset, lhs_data, rhs_data)) |
+ else: |
+ streams = None |
+ offset += len(lhs_data) |
+ del lhs_data |
+ del rhs_data |
if not diffs: |
return None |
- |
result = '%d out of %d bytes are different (%.2f%%)' % ( |
diffs, file_len, 100.0 * diffs / file_len) |
+ if streams: |
+ encode = lambda text: ''.join(i if 31 < ord(i) < 128 else '.' for i in text) |
+ for offset, lhs_data, rhs_data in streams: |
+ lhs_line = '%s \'%s\'' % (lhs_data.encode('hex'), encode(lhs_data)) |
+ rhs_line = '%s \'%s\'' % (rhs_data.encode('hex'), encode(rhs_data)) |
+ diff = list(difflib.Differ().compare([lhs_line], [rhs_line]))[-1][2:-1] |
+ result += '\n0x%-8x: %s\n %s\n %s' % ( |
+ offset, lhs_line, rhs_line, diff) |
+ return result |
+ |
- if diffs and first_filepath.endswith('.isolated'): |
- # Unpack the files. |
+def compare_files(first_filepath, second_filepath): |
+ """Compares two binaries and return the number of differences between them. |
+ |
+ Returns None if the files are equal, a string otherwise. |
+ """ |
+ if first_filepath.endswith('.isolated'): |
with open(first_filepath, 'rb') as f: |
- lhs = json.dumps( |
- json.load(f), indent=2, sort_keys=True, |
- separators=(',', ': ')).splitlines() |
+ lhs = json.load(f) |
with open(second_filepath, 'rb') as f: |
- rhs = json.dumps( |
- json.load(f), indent=2, sort_keys=True, |
- separators=(',', ': ')).splitlines() |
+ rhs = json.load(f) |
+ diff = diff_dict(lhs, rhs) |
+ if diff: |
+ return '\n' + diff |
- result += '\n' + '\n'.join( |
- line for line in difflib.unified_diff(lhs, rhs) |
- if not line.startswith(('---', '+++'))) |
- return result |
+ file_len = os.stat(first_filepath).st_size |
+ if file_len != os.stat(second_filepath).st_size: |
+ return 'different size: %d != %d' % ( |
+ file_len, os.stat(second_filepath).st_size) |
+ |
+ return diff_binary(first_filepath, second_filepath, file_len) |
def compare_build_artifacts(first_dir, second_dir): |
@@ -99,6 +140,9 @@ def compare_build_artifacts(first_dir, second_dir): |
print >> sys.stderr, '\n'.join(' ' + i for i in sorted(diff)) |
res += len(diff) |
+ epoch_hex = struct.pack('<I', int(time.time())).encode('hex') |
+ print('Epoch: %s' % |
+ ' '.join(epoch_hex[i:i+2] for i in xrange(0, len(epoch_hex), 2))) |
max_filepath_len = max(len(n) for n in first_list & second_list) |
for f in sorted(first_list & second_list): |
first_file = os.path.join(first_dir, f) |