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

Side by Side Diff: tools/find_bad_images_in_skps.py

Issue 24449003: Make Jpeg decoding more fault resistant. (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: lint changes, bugfix, description changed Created 7 years, 2 months 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
OLDNEW
(Empty)
1 #!/usr/bin/env python
2
3 # Copyright 2013 Google Inc. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 """
8 This script will take as an argument either a list of skp files or a
scroggo 2013/10/01 22:04:14 The tools might better belong in their own CL. Pro
9 set of directories that contains skp files. It will then test each
10 skp file with the `render_pictures` program. If that program either
11 spits out any unexpected output or doesn't return 0, I will flag that
12 skp file as problematic. We then extract all of the embedded images
13 inside the skp and test each one of them agianst the
14 SkImageDecoder::DecodeFile function. Again, we consider any
15 extranious output or a bad return value an error. In the envent of an
scroggo 2013/10/01 22:04:14 event*
16 error, we retain the image and print out information about the error.
17 The output (on stdout) is formated as a csv document.
18
19 A copy of each bad image is left in a directory created by
20 tempfile.mkdtemp().
21 """
22
23 import glob
24 import os
25 import re
26 import shutil
27 import subprocess
28 import sys
29 import tempfile
30 import threading
31
32 import test_rendering # skia/trunk/tools. reuse FindPathToProgram()
33
34 USAGE = """
35 Usage:
36 {command} SKP_FILE [SKP_FILES]
37 {command} SKP_DIR [SKP_DIRS]\n
38 Environment variables:
39 To run multiple worker threads, set NUM_THREADS.
40 To use a different temporary storage location, set TMPDIR.
41
42 """
43
44 def execute_program(args, ignores=None):
45 """
46 Execute a process and waits for it to complete. Returns all
47 output (stderr and stdout) after (optional) filtering.
48
49 @param args is passed into subprocess.Popen().
50
51 @param ignores (optional) is a list of regular expression strings
52 that will be ignored in the output.
53
54 @returns a tuple (returncode, output)
55 """
56 if ignores is None:
57 ignores = []
58 else:
59 ignores = [re.compile(ignore) for ignore in ignores]
60 proc = subprocess.Popen(
61 args,
62 stdout=subprocess.PIPE,
63 stderr=subprocess.STDOUT)
64 output = ''.join(
65 line for line in proc.stdout
66 if not any(bool(ignore.match(line)) for ignore in ignores))
67 returncode = proc.wait()
68 return (returncode, output)
69
70
71 def list_files(paths):
72 """
73 we accept a list of directories or filenames on the command
74 line. We don't choose to recurse into directories.
scroggo 2013/10/01 22:04:14 It seems you go down one level. Technically you do
75 """
76 class NotAFileException(Exception):
77 pass
78 for path in paths:
79 for globbedpath in glob.iglob(path): # useful on win32
80 if os.path.isdir(globbedpath):
81 for filename in os.listdir(globbedpath):
82 newpath = os.path.join(globbedpath, filename)
83 if os.path.isfile(newpath):
84 yield newpath
85 elif os.path.isfile(globbedpath):
86 yield globbedpath
87 else:
88 raise NotAFileException('{} is not a file'.format(globbedpath))
89
90
91 class BadImageFinder(object):
92
93 def __init__(self, directory=None):
94 self.render_pictures = test_rendering.FindPathToProgram(
95 'render_pictures')
96 self.test_image_decoder = test_rendering.FindPathToProgram(
97 'test_image_decoder')
98 assert os.path.isfile(self.render_pictures)
99 assert os.path.isfile(self.test_image_decoder)
100 if directory is None:
101 self.saved_image_dir = tempfile.mkdtemp(prefix='skia_skp_test_')
102 else:
103 assert os.path.isdir(directory)
104 self.saved_image_dir = directory
105 self.bad_image_count = 0
106
107 def process_files(self, skp_files):
108 for path in skp_files:
109 self.process_file(path)
110
111 def process_file(self, skp_file):
112 assert self.saved_image_dir is not None
113 assert os.path.isfile(skp_file)
114 args = [self.render_pictures, '--readPath', skp_file]
115 ignores = ['^process_in', '^deserializ', '^drawing...', '^Non-defaul']
116 returncode, output = execute_program(args, ignores)
117 if (returncode == 0) and not output:
118 return
119 temp_image_dir = tempfile.mkdtemp(prefix='skia_skp_test___')
120 args = [ self.render_pictures, '--readPath', skp_file,
121 '--writePath', temp_image_dir, '--writeEncodedImages']
122 subprocess.call(args, stderr=open(os.devnull,'w'),
123 stdout=open(os.devnull,'w'))
124 for image_name in os.listdir(temp_image_dir):
125 image_path = os.path.join(temp_image_dir, image_name)
126 assert(os.path.isfile(image_path))
127 args = [self.test_image_decoder, image_path]
128 returncode, output = execute_program(args, [])
129 if (returncode == 0) and not output:
130 os.remove(image_path)
131 continue
132 try:
133 shutil.move(image_path, self.saved_image_dir)
134 except (shutil.Error,):
135 # If this happens, don't stop the entire process,
136 # just warn the user.
137 os.remove(image_path)
138 sys.stderr.write('{0} is a repeat.\n'.format(image_name))
scroggo 2013/10/01 22:04:14 How do we end up with a repeat? Is it possible tha
hal.canary 2013/10/02 16:20:06 It happened once and I'm not sure how. The only p
139 self.bad_image_count += 1
140 if returncode == 2:
141 returncode = 'SkImageDecoder::DecodeFile returns false'
142 elif returncode == 0:
143 returncode = 'extra verbosity'
144 assert output
145 elif returncode == -11:
146 returncode = 'segmentation violation'
147 else:
148 returncode = 'returncode: {}'.format(returncode)
149 output = output.strip().replace('\n',' ').replace('"','\'')
150 suffix = image_name[-3:]
151 output_line = '"{0}","{1}","{2}","{3}","{4}"\n'.format(
152 returncode, suffix, skp_file, image_name, output)
153 sys.stdout.write(output_line)
154 sys.stdout.flush()
155 os.rmdir(temp_image_dir)
156 return
157
158 def main(main_argv):
159 if not main_argv or main_argv[0] in ['-h', '-?', '-help', '--help']:
160 sys.stderr.write(USAGE.format(command=__file__))
161 return 1
162 if 'NUM_THREADS' in os.environ:
163 number_of_threads = int(os.environ['NUM_THREADS'])
164 if number_of_threads < 1:
165 number_of_threads = 1
166 else:
167 number_of_threads = 1
168 temp_dir = tempfile.mkdtemp(prefix='skia_skp_test_')
169 sys.stderr.write('Directory for bad images: {}\n'.format(temp_dir))
170 sys.stdout.write('"Error","Filetype","SKP File","Image File","Output"\n')
171 sys.stdout.flush()
172
173 finders = [
174 BadImageFinder(temp_dir) for index in xrange(number_of_threads)]
175 arguments = [[] for index in xrange(number_of_threads)]
176 for index, item in enumerate(list_files(main_argv)):
177 ## split up the given targets among the worker threads
178 arguments[index % number_of_threads].append(item)
179 threads = [
180 threading.Thread(
181 target=BadImageFinder.process_files, args=(finder,argument))
182 for finder, argument in zip(finders, arguments)]
183 for thread in threads:
184 thread.start()
185 for thread in threads:
186 thread.join()
187 number = sum(finder.bad_image_count for finder in finders)
188 sys.stderr.write('Number of bad images found: {}\n'.format(number))
189 return 0
190
191 if __name__ == '__main__':
192 exit(main(sys.argv[1:]))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698