Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: scripts/slave/recipe_modules/isolate/resources/compare_build_artifacts.py

Issue 687723008: Optimize compare_build_artifacts.py to have an even more compact output. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2014 The Chromium Authors. All rights reserved. 2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Compare the artifacts from two builds.""" 6 """Compare the artifacts from two builds."""
7 7
8 import difflib 8 import difflib
9 import json 9 import json
10 import optparse 10 import optparse
(...skipping 16 matching lines...) Expand all
27 return False 27 return False
28 ext = os.path.splitext(f)[1] 28 ext = os.path.splitext(f)[1]
29 if ext == '.isolated': 29 if ext == '.isolated':
30 return True 30 return True
31 return ext in allowed and os.access(f, os.X_OK) 31 return ext in allowed and os.access(f, os.X_OK)
32 32
33 return set(f for f in os.listdir(build_dir) if 33 return set(f for f in os.listdir(build_dir) if
34 check(os.path.join(build_dir, f))) 34 check(os.path.join(build_dir, f)))
35 35
36 36
37 def diff_dict(a, b):
38 """Returns a yaml-like textural diff of two dict.
39
40 It is currently optimized for the .isolated format.
41 """
42 out = ''
43 for key in set(a) | set(b):
44 va = a.get(key)
45 vb = b.get(key)
46 if va.__class__ != vb.__class__:
47 out += '- %s:\n %r != %r\n' % (key, va, vb)
48 elif isinstance(va, dict):
49 c = diff_dict(va, vb)
50 if c:
51 out += '- %s:\n%s\n' % (
52 key, '\n'.join(' ' + l for l in c.splitlines()))
53 elif va != vb:
54 out += '- %s:\n %s != %s\n' % (key, va, vb)
55 return out.rstrip()
56
57
58 def diff_binary(first_filepath, second_filepath, file_len):
59 """Returns a compact binary diff if the diff is small enough."""
60 CHUNK_SIZE = 32
61 MAX_STREAMS = 10
62 diffs = 0
63 streams = []
64 offset = 0
65 with open(first_filepath, 'rb') as lhs:
66 with open(second_filepath, 'rb') as rhs:
67 while True:
68 lhs_data = lhs.read(CHUNK_SIZE)
69 rhs_data = rhs.read(CHUNK_SIZE)
70 if not lhs_data:
71 break
72 if lhs_data != rhs_data:
73 diffs += sum(l != r for l, r in zip(lhs_data, rhs_data))
74 if streams is not None:
75 if len(streams) < MAX_STREAMS:
76 streams.append((offset, lhs_data, rhs_data))
77 else:
78 streams = None
79 offset += len(lhs_data)
80 del lhs_data
81 del rhs_data
82 if not diffs:
83 return None
84 result = '%d out of %d bytes are different (%.2f%%)' % (
85 diffs, file_len, 100.0 * diffs / file_len)
86 if streams:
87 result += ''.join(
Sébastien Marchand 2014/11/04 20:29:57 Should we also print the ascii representation of t
88 '\n%9d: %s\n %s' % (a, b.encode('hex'), c.encode('hex'))
89 for a, b, c in streams)
90 return result
91
92
37 def compare_files(first_filepath, second_filepath): 93 def compare_files(first_filepath, second_filepath):
38 """Compares two binaries and return the number of differences between them. 94 """Compares two binaries and return the number of differences between them.
39 95
40 Returns None if the files are equal, a string otherwise. 96 Returns None if the files are equal, a string otherwise.
41 """ 97 """
98 if first_filepath.endswith('.isolated'):
99 with open(first_filepath, 'rb') as f:
100 lhs = json.load(f)
101 with open(second_filepath, 'rb') as f:
102 rhs = json.load(f)
103 diff = diff_dict(lhs, rhs)
104 if diff:
105 return '\n' + diff
106
42 file_len = os.stat(first_filepath).st_size 107 file_len = os.stat(first_filepath).st_size
43 if file_len != os.stat(second_filepath).st_size: 108 if file_len != os.stat(second_filepath).st_size:
44 return 'different size: %d != %d' % ( 109 return 'different size: %d != %d' % (
45 file_len, os.stat(second_filepath).st_size) 110 file_len, os.stat(second_filepath).st_size)
46 111
47 chunk_size = 1024 * 1024 112 return diff_binary(first_filepath, second_filepath, file_len)
48 diffs = 0
49 with open(first_filepath, 'rb') as lhs:
50 with open(second_filepath, 'rb') as rhs:
51 while True:
52 lhs_data = lhs.read(chunk_size)
53 rhs_data = rhs.read(chunk_size)
54 if not lhs_data:
55 break
56 diffs += sum(l != r for l, r in zip(lhs_data, rhs_data))
57 if not diffs:
58 return None
59
60 result = '%d out of %d bytes are different (%.2f%%)' % (
61 diffs, file_len, 100.0 * diffs / file_len)
62
63 if diffs and first_filepath.endswith('.isolated'):
64 # Unpack the files.
65 with open(first_filepath, 'rb') as f:
66 lhs = json.dumps(
67 json.load(f), indent=2, sort_keys=True,
68 separators=(',', ': ')).splitlines()
69 with open(second_filepath, 'rb') as f:
70 rhs = json.dumps(
71 json.load(f), indent=2, sort_keys=True,
72 separators=(',', ': ')).splitlines()
73
74 result += '\n' + '\n'.join(
75 line for line in difflib.unified_diff(lhs, rhs)
76 if not line.startswith(('---', '+++')))
77 return result
78 113
79 114
80 def compare_build_artifacts(first_dir, second_dir): 115 def compare_build_artifacts(first_dir, second_dir):
81 """Compare the artifacts from two distinct builds.""" 116 """Compare the artifacts from two distinct builds."""
82 if not os.path.isdir(first_dir): 117 if not os.path.isdir(first_dir):
83 print >> sys.stderr, '%s isn\'t a valid directory.' % first_dir 118 print >> sys.stderr, '%s isn\'t a valid directory.' % first_dir
84 return 1 119 return 1
85 if not os.path.isdir(second_dir): 120 if not os.path.isdir(second_dir):
86 print >> sys.stderr, '%s isn\'t a valid directory.' % second_dir 121 print >> sys.stderr, '%s isn\'t a valid directory.' % second_dir
87 return 1 122 return 1
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 parser.error('--first-build-dir is required') 164 parser.error('--first-build-dir is required')
130 if not options.second_build_dir: 165 if not options.second_build_dir:
131 parser.error('--second-build-dir is required') 166 parser.error('--second-build-dir is required')
132 167
133 return compare_build_artifacts(options.first_build_dir, 168 return compare_build_artifacts(options.first_build_dir,
134 options.second_build_dir) 169 options.second_build_dir)
135 170
136 171
137 if __name__ == '__main__': 172 if __name__ == '__main__':
138 sys.exit(main()) 173 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698