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

Side by Side Diff: ppapi/generate_ppapi_size_checks.py

Issue 6014007: Improve documentation for the tools for generating size checks, based on comm... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 10 years 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 | ppapi/tests/clang/README » ('j') | 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/python 1 #!/usr/bin/python
2 2
3 # Copyright (c) 2010 The Chromium Authors. All rights reserved. 3 # Copyright (c) 2010 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 """This script should be run manually on occasion to make sure all PPAPI types 7 """This script should be run manually on occasion to make sure all PPAPI types
8 have appropriate size checking. 8 have appropriate size checking.
9 9
10 """ 10 """
11 11
12 import optparse 12 import optparse
13 import os 13 import os
14 import subprocess 14 import subprocess
15 import sys 15 import sys
16 16
17 17
18 # The string that the PrintNamesAndSizes plugin uses to indicate a type is or 18 # The string that the PrintNamesAndSizes plugin uses to indicate a type is
19 # contains a pointer. 19 # expected to have architecture-dependent size.
20 HAS_POINTER_STRING = "HasPointer" 20 ARCH_DEPENDENT_STRING = "ArchDependentSize"
21 # These are types that don't include a pointer but nonetheless have
22 # architecture-dependent size. They all are ultimately typedefs to 'long' or
23 # 'unsigned long'. If there get to be too many types that use 'long' or
24 # 'unsigned long', we can add detection of them to the plugin to automate this.
25 ARCH_DEPENDENT_TYPES = set(["khronos_intptr_t",
26 "khronos_uintptr_t",
27 "khronos_ssize_t",
28 "khronos_usize_t",
29 "GLintptr",
30 "GLsizeiptr"
31 ])
32 21
33 22
34 23
35 class SourceLocation: 24 class SourceLocation:
36 25
37 """A class representing the source location of a definiton.""" 26 """A class representing the source location of a definiton."""
38 27
39 def __init__(self, filename="", start_line=-1, end_line=-1): 28 def __init__(self, filename="", start_line=-1, end_line=-1):
40 self.filename = os.path.normpath(filename) 29 self.filename = os.path.normpath(filename)
41 self.start_line = start_line 30 self.start_line = start_line
42 self.end_line = end_line 31 self.end_line = end_line
43 32
44 33
45 34
46 class TypeInfo: 35 class TypeInfo:
47 36
48 """A class representing information about a C++ type.""" 37 """A class representing information about a C++ type. It contains the
38 following fields:
39 - kind: The Clang TypeClassName (Record, Enum, Typedef, Union, etc)
40 - name: The unmangled string name of the type.
41 - size: The size in bytes of the type.
42 - arch_dependent: True if the type may have architecture dependent size
43 according to PrintNamesAndSizes. False otherwise. Types
44 which are considered architecture-dependent from 32-bit
45 to 64-bit are pointers, longs, unsigned longs, and any
46 type that contains an architecture-dependent type.
47 - source_location: A SourceLocation describing where the type is defined.
48 - target: The target Clang was compiling when it found the type definition.
49 This is used only for diagnostic output.
50 - parsed_line: The line which Clang output which was used to create this
51 TypeInfo (as the info_string parameter to __init__). This is
52 used only for diagnostic output.
53 """
49 54
50 def __init__(self, info_string, target): 55 def __init__(self, info_string, target):
51 [self.kind, self.name, self.size, has_pointer_string, source_file, 56 """Create a TypeInfo from a given info_string. Also store the name of the
57 target for which the TypeInfo was first created just so we can print useful
58 error information.
59 info_string is a comma-delimited string of the following form:
60 kind,name,size,arch_dependent,source_file,start_line,end_line
61 Where:
62 - kind: The Clang TypeClassName (Record, Enum, Typedef, Union, etc)
63 - name: The unmangled string name of the type.
64 - size: The size in bytes of the type.
65 - arch_dependent: 'ArchDependentSize' if the type has architecture-dependent
66 size, NotArchDependentSize otherwise.
67 - source_file: The source file in which the type is defined.
68 - first_line: The first line of the definition (counting from 0).
69 - last_line: The last line of the definition (counting from 0).
70 This should match the output of the PrintNamesAndSizes plugin.
71 """
72 [self.kind, self.name, self.size, arch_dependent_string, source_file,
52 start_line, end_line] = info_string.split(',') 73 start_line, end_line] = info_string.split(',')
53 self.target = target 74 self.target = target
54 self.parsed_line = info_string 75 self.parsed_line = info_string
55 # Note that Clang counts line numbers from 1, but we want to count from 0. 76 # Note that Clang counts line numbers from 1, but we want to count from 0.
56 self.source_location = SourceLocation(source_file, 77 self.source_location = SourceLocation(source_file,
57 int(start_line)-1, 78 int(start_line)-1,
58 int(end_line)-1) 79 int(end_line)-1)
59 # If the type is one of our known special cases, mark it as architecture- 80 self.arch_dependent = (arch_dependent_string == ARCH_DEPENDENT_STRING)
60 # dependent.
61 if self.name in ARCH_DEPENDENT_TYPES:
62 self.arch_dependent = True
63 # Otherwise, its size can be architecture dependent if it contains one or
64 # more pointers (or is a pointer).
65 else:
66 self.arch_dependent = (has_pointer_string == HAS_POINTER_STRING)
67 self.target = target
68 self.parsed_line = info_string
69
70 81
71 82
72 class FilePatch: 83 class FilePatch:
73 84
74 """A class representing a set of line-by-line changes to a particular file. 85 """A class representing a set of line-by-line changes to a particular file.
75 None 86 None of the changes are applied until Apply is called. All line numbers are
76 of the changes are applied until Apply is called. All line numbers are
77 counted from 0. 87 counted from 0.
78 """ 88 """
79 89
80 def __init__(self, filename): 90 def __init__(self, filename):
81 self.filename = filename 91 self.filename = filename
82 self.linenums_to_delete = set() 92 self.linenums_to_delete = set()
83 # A dictionary from line number to an array of strings to be inserted at 93 # A dictionary from line number to an array of strings to be inserted at
84 # that line number. 94 # that line number.
85 self.lines_to_add = {} 95 self.lines_to_add = {}
86 96
87 def Delete(self, start_line, end_line): 97 def Delete(self, start_line, end_line):
88 """Make the patch delete the lines [start_line, end_line).""" 98 """Make the patch delete the lines starting with |start_line| up to but not
99 including |end_line|.
100 """
89 self.linenums_to_delete |= set(range(start_line, end_line)) 101 self.linenums_to_delete |= set(range(start_line, end_line))
90 102
91 def Add(self, text, line_number): 103 def Add(self, text, line_number):
92 """Add the given text before the text on the given line number.""" 104 """Add the given text before the text on the given line number."""
93 if line_number in self.lines_to_add: 105 if line_number in self.lines_to_add:
94 self.lines_to_add[line_number].append(text) 106 self.lines_to_add[line_number].append(text)
95 else: 107 else:
96 self.lines_to_add[line_number] = [text] 108 self.lines_to_add[line_number] = [text]
97 109
98 def Apply(self): 110 def Apply(self):
99 """Apply the patch by writing it to self.filename""" 111 """Apply the patch by writing it to self.filename."""
100 # Read the lines of the existing file in to a list. 112 # Read the lines of the existing file in to a list.
101 sourcefile = open(self.filename, "r") 113 sourcefile = open(self.filename, "r")
102 file_lines = sourcefile.readlines() 114 file_lines = sourcefile.readlines()
103 sourcefile.close() 115 sourcefile.close()
104 # Now apply the patch. Our strategy is to keep the array at the same size, 116 # Now apply the patch. Our strategy is to keep the array at the same size,
105 # and just edit strings in the file_lines list as necessary. When we delete 117 # and just edit strings in the file_lines list as necessary. When we delete
106 # lines, we just blank the line and keep it in the list. When we add lines, 118 # lines, we just blank the line and keep it in the list. When we add lines,
107 # we just prepend the added source code to the start of the existing line at 119 # we just prepend the added source code to the start of the existing line at
108 # that line number. This way, all the line numbers we cached from calls to 120 # that line number. This way, all the line numbers we cached from calls to
109 # Add and Delete remain valid list indices, and we don't have to worry about 121 # Add and Delete remain valid list indices, and we don't have to worry about
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
156 print typeinfo_map[typeinfo.name].parsed_line 168 print typeinfo_map[typeinfo.name].parsed_line
157 print typeinfo.parsed_line 169 print typeinfo.parsed_line
158 sys.exit(1) 170 sys.exit(1)
159 else: 171 else:
160 # It's already in the map and the sizes match. 172 # It's already in the map and the sizes match.
161 pass 173 pass
162 else: 174 else:
163 typeinfo_map[typeinfo.name] = typeinfo 175 typeinfo_map[typeinfo.name] = typeinfo
164 176
165 177
166 def ProcessTarget(clang_command, target, arch_types, ind_types): 178 def ProcessTarget(clang_command, target, types):
167 """Run clang using the given clang_command for the given target string. Parse 179 """Run clang using the given clang_command for the given target string. Parse
168 the output to create TypeInfos for each discovered type. Insert each type in 180 the output to create TypeInfos for each discovered type. Insert each type in
169 to the appropriate dictionary. For each type that has architecture-dependent 181 to the 'types' dictionary. If the type already exists in the types
170 size, insert it in to arch_types. Types with independent size go in to 182 dictionary, make sure that the size matches what's already in the map. If
171 ind_types. If the type already exists in the appropriate map, make sure that 183 not, exit with an error message.
172 the size matches what's already in the map. If not, the script terminates
173 with an error message.
174 """ 184 """
175 p = subprocess.Popen(clang_command + " -triple " + target, 185 p = subprocess.Popen(clang_command + " -triple " + target,
176 shell=True, 186 shell=True,
177 stdout=subprocess.PIPE) 187 stdout=subprocess.PIPE)
178 lines = p.communicate()[0].split() 188 lines = p.communicate()[0].split()
179 for line in lines: 189 for line in lines:
180 typeinfo = TypeInfo(line, target) 190 typeinfo = TypeInfo(line, target)
181 # Put types which have architecture-specific size in to arch_types. All 191 CheckAndInsert(typeinfo, types)
182 # other types are 'architecture-independent' and get put in ind_types.
183 # in the appropraite dictionary.
184 if typeinfo.arch_dependent:
185 CheckAndInsert(typeinfo, arch_types)
186 else:
187 CheckAndInsert(typeinfo, ind_types)
188 192
189 193
190 def ToAssertionCode(typeinfo): 194 def ToAssertionCode(typeinfo):
191 """Convert the TypeInfo to an appropriate C compile assertion. 195 """Convert the TypeInfo to an appropriate C compile assertion.
192 If it's a struct (Record in Clang terminology), we want a line like this: 196 If it's a struct (Record in Clang terminology), we want a line like this:
193 PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(<name>, <size>);\n 197 PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(<name>, <size>);\n
194 Enums: 198 Enums:
195 PP_COMPILE_ASSERT_ENUM_SIZE_IN_BYTES(<name>, <size>);\n 199 PP_COMPILE_ASSERT_ENUM_SIZE_IN_BYTES(<name>, <size>);\n
196 Typedefs: 200 Typedefs:
197 PP_COMPILE_ASSERT_SIZE_IN_BYTES(<name>, <size>);\n 201 PP_COMPILE_ASSERT_SIZE_IN_BYTES(<name>, <size>);\n
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
238 outfile.write(COPYRIGHT_STRING_C) 242 outfile.write(COPYRIGHT_STRING_C)
239 outfile.write('#ifndef ' + header_guard + '\n') 243 outfile.write('#ifndef ' + header_guard + '\n')
240 outfile.write('#define ' + header_guard + '\n\n') 244 outfile.write('#define ' + header_guard + '\n\n')
241 outfile.write('#include "ppapi/tests/test_struct_sizes.c"\n\n') 245 outfile.write('#include "ppapi/tests/test_struct_sizes.c"\n\n')
242 for line in assertion_lines: 246 for line in assertion_lines:
243 outfile.write(line) 247 outfile.write(line)
244 outfile.write('\n#endif /* ' + header_guard + ' */\n') 248 outfile.write('\n#endif /* ' + header_guard + ' */\n')
245 249
246 250
247 def main(argv): 251 def main(argv):
252 # See README file for example command-line invocation. This script runs the
253 # PrintNamesAndSizes Clang plugin with 'test_struct_sizes.c' as input, which
254 # should include all C headers and all existing size checks. It runs the
255 # plugin multiple times; once for each of a set of targets, some 32-bit and
256 # some 64-bit. It verifies that wherever possible, types have a consistent
257 # size on both platforms. Types that can't easily have consistent size (e.g.
258 # ones that contain a pointer) are checked to make sure they are consistent
259 # for all 32-bit platforms and consistent on all 64-bit platforms, but the
260 # sizes on 32 vs 64 are allowed to differ.
261 #
262 # Then, if all the types have consistent size as expected, compile assertions
263 # are added to the source code. Types whose size is independent of
264 # architectureacross have their compile assertions placed immediately after
265 # their definition in the C API header. Types whose size differs on 32-bit
266 # vs 64-bit have a compile assertion placed in each of:
267 # ppapi/tests/arch_dependent_sizes_32.h and
268 # ppapi/tests/arch_dependent_sizes_64.h.
269 #
270 # Note that you should always check the results of the tool to make sure
271 # they are sane.
248 parser = optparse.OptionParser() 272 parser = optparse.OptionParser()
249 parser.add_option( 273 parser.add_option(
250 '-c', '--clang-path', dest='clang_path', 274 '-c', '--clang-path', dest='clang_path',
251 default=(''), 275 default=(''),
252 help='the path to the clang binary (default is to get it from your path)') 276 help='the path to the clang binary (default is to get it from your path)')
253 parser.add_option( 277 parser.add_option(
254 '-p', '--plugin', dest='plugin', 278 '-p', '--plugin', dest='plugin',
255 default='tests/clang/libPrintNamesAndSizes.so', 279 default='tests/clang/libPrintNamesAndSizes.so',
256 help='The path to the PrintNamesAndSizes plugin library.') 280 help='The path to the PrintNamesAndSizes plugin library.')
257 parser.add_option( 281 parser.add_option(
(...skipping 28 matching lines...) Expand all
286 # Types that have size dependent on architecture, for 64-bit 310 # Types that have size dependent on architecture, for 64-bit
287 types64 = {} 311 types64 = {}
288 # Note that types32 and types64 should contain the same types, but with 312 # Note that types32 and types64 should contain the same types, but with
289 # different sizes. 313 # different sizes.
290 314
291 # Types whose size should be consistent regardless of architecture. 315 # Types whose size should be consistent regardless of architecture.
292 types_independent = {} 316 types_independent = {}
293 317
294 # Now run clang for each target. Along the way, make sure architecture- 318 # Now run clang for each target. Along the way, make sure architecture-
295 # dependent types are consistent sizes on all 32-bit platforms and consistent 319 # dependent types are consistent sizes on all 32-bit platforms and consistent
296 # on all 64-bit platforms. Any types in 'types_independent' are checked for 320 # on all 64-bit platforms.
297 # all targets to make sure their size is consistent across all of them.
298 targets32 = options.targets32.split(','); 321 targets32 = options.targets32.split(',');
299 for target in targets32: 322 for target in targets32:
300 ProcessTarget(clang_command, target, types32, types_independent) 323 # For each 32-bit target, run the PrintNamesAndSizes Clang plugin to get
324 # information about all types in the translation unit, and add a TypeInfo
325 # for each of them to types32. If any size mismatches are found,
326 # ProcessTarget will spit out an error and exit.
327 ProcessTarget(clang_command, target, types32)
301 targets64 = options.targets64.split(','); 328 targets64 = options.targets64.split(',');
302 for target in targets64: 329 for target in targets64:
303 ProcessTarget(clang_command, target, types64, types_independent) 330 # Do the same as above for each 64-bit target; put all types in types64.
331 ProcessTarget(clang_command, target, types64)
304 332
305 # This dictionary maps file names to FilePatch objects. 333 # Now for each dictionary, find types whose size are consistent regardless of
334 # architecture, and move those in to types_independent. Anywhere sizes
335 # differ, make sure they are expected to be architecture-dependent based on
336 # their structure. If we find types which could easily be consistent but
337 # aren't, spit out an error and exit.
338 types_independent = {}
339 for typename, typeinfo32 in types32.items():
340 if (typename in types64):
341 typeinfo64 = types64[typename]
342 if (typeinfo64.size == typeinfo32.size):
343 # The types are the same size, so we can treat it as arch-independent.
344 types_independent[typename] = typeinfo32
345 del types32[typename]
346 del types64[typename]
347 elif (typeinfo32.arch_dependent or typeinfo64.arch_dependent):
348 # The type is defined in such a way that it would be difficult to make
349 # its size consistent. E.g., it has pointers. We'll leave it in the
350 # arch-dependent maps so that we can put arch-dependent size checks in
351 # test code.
352 pass
353 else:
354 # The sizes don't match, but there's no reason they couldn't. It's
355 # probably due to an alignment mismatch between Win32/NaCl vs Linux32/
356 # Mac32.
357 print "Error: '" + typename + "' is", typeinfo32.size, \
358 "bytes on target '" + typeinfo32.target + \
359 "', but", typeinfo64.size, "on target '" + typeinfo64.target + "'"
360 print typeinfo32.parsed_line
361 print typeinfo64.parsed_line
362 sys.exit(1)
363 else:
364 print "WARNING: Type '", typename, "' was defined for target '",
365 print typeinfo32.target, ", but not for any 64-bit targets."
366
367 # Now we have all the information we need to generate our static assertions.
368 # Types that have consistent size across architectures will have the static
369 # assertion placed immediately after their definition. Types whose size
370 # depends on 32-bit vs 64-bit architecture will have checks placed in
371 # tests/arch_dependent_sizes_32/64.h.
372
373 # This dictionary maps file names to FilePatch objects. We will add items
374 # to it as needed. Each FilePatch represents a set of changes to make to the
375 # associated file (additions and deletions).
306 file_patches = {} 376 file_patches = {}
307 377
308 # Find locations of existing macros, and just delete them all. 378 # Find locations of existing macros, and just delete them all. Note that
379 # normally, only things in 'types_independent' need to be deleted, as arch-
380 # dependent checks exist in tests/arch_dependent_sizes_32/64.h, which are
381 # always completely over-ridden. However, it's possible that a type that used
382 # to be arch-independent has changed to now be arch-dependent (e.g., because
383 # a pointer was added), and we want to delete the old check in that case.
309 for name, typeinfo in \ 384 for name, typeinfo in \
310 types_independent.items() + types32.items() + types64.items(): 385 types_independent.items() + types32.items() + types64.items():
311 if IsMacroDefinedName(name): 386 if IsMacroDefinedName(name):
312 sourcefile = typeinfo.source_location.filename 387 sourcefile = typeinfo.source_location.filename
313 if sourcefile not in file_patches: 388 if sourcefile not in file_patches:
314 file_patches[sourcefile] = FilePatch(sourcefile) 389 file_patches[sourcefile] = FilePatch(sourcefile)
315 file_patches[sourcefile].Delete(typeinfo.source_location.start_line, 390 file_patches[sourcefile].Delete(typeinfo.source_location.start_line,
316 typeinfo.source_location.end_line+1) 391 typeinfo.source_location.end_line+1)
317 392
318 # Add a compile-time assertion for each type whose size is independent of 393 # Add a compile-time assertion for each type whose size is independent of
319 # architecture. These assertions go immediately after the class definition. 394 # architecture. These assertions go immediately after the class definition.
320 for name, typeinfo in types_independent.items(): 395 for name, typeinfo in types_independent.items():
321 # Ignore macros and types that are 0 bytes (i.e., typedefs to void) 396 # Ignore dummy types that were defined by macros and also ignore types that
397 # are 0 bytes (i.e., typedefs to void).
322 if not IsMacroDefinedName(name) and typeinfo.size > 0: 398 if not IsMacroDefinedName(name) and typeinfo.size > 0:
323 sourcefile = typeinfo.source_location.filename 399 sourcefile = typeinfo.source_location.filename
324 if sourcefile not in file_patches: 400 if sourcefile not in file_patches:
325 file_patches[sourcefile] = FilePatch(sourcefile) 401 file_patches[sourcefile] = FilePatch(sourcefile)
326 # Add the assertion code just after the definition of the type. 402 # Add the assertion code just after the definition of the type.
403 # E.g.:
404 # struct Foo {
405 # int32_t x;
406 # };
407 # PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(Foo, 4); <---Add this line
327 file_patches[sourcefile].Add(ToAssertionCode(typeinfo), 408 file_patches[sourcefile].Add(ToAssertionCode(typeinfo),
328 typeinfo.source_location.end_line+1) 409 typeinfo.source_location.end_line+1)
329 410
411 # Apply our patches. This actually edits the files containing the definitions
412 # for the types in types_independent.
330 for filename, patch in file_patches.items(): 413 for filename, patch in file_patches.items():
331 patch.Apply() 414 patch.Apply()
332 415
416 # Write out a file of checks for 32-bit architectures and a separate file for
417 # 64-bit architectures. These only have checks for types that are
418 # architecture-dependent.
333 c_source_root = os.path.join(options.ppapi_root, "tests") 419 c_source_root = os.path.join(options.ppapi_root, "tests")
334 WriteArchSpecificCode(types32.values(), 420 WriteArchSpecificCode(types32.values(),
335 c_source_root, 421 c_source_root,
336 "arch_dependent_sizes_32.h") 422 "arch_dependent_sizes_32.h")
337 WriteArchSpecificCode(types64.values(), 423 WriteArchSpecificCode(types64.values(),
338 c_source_root, 424 c_source_root,
339 "arch_dependent_sizes_64.h") 425 "arch_dependent_sizes_64.h")
340 426
341 return 0 427 return 0
342 428
343 429
344 if __name__ == '__main__': 430 if __name__ == '__main__':
345 sys.exit(main(sys.argv[1:])) 431 sys.exit(main(sys.argv[1:]))
346 432
OLDNEW
« no previous file with comments | « no previous file | ppapi/tests/clang/README » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698