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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | ppapi/tests/clang/README » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ppapi/generate_ppapi_size_checks.py
===================================================================
--- ppapi/generate_ppapi_size_checks.py (revision 69746)
+++ ppapi/generate_ppapi_size_checks.py (working copy)
@@ -15,20 +15,9 @@
import sys
-# The string that the PrintNamesAndSizes plugin uses to indicate a type is or
-# contains a pointer.
-HAS_POINTER_STRING = "HasPointer"
-# These are types that don't include a pointer but nonetheless have
-# architecture-dependent size. They all are ultimately typedefs to 'long' or
-# 'unsigned long'. If there get to be too many types that use 'long' or
-# 'unsigned long', we can add detection of them to the plugin to automate this.
-ARCH_DEPENDENT_TYPES = set(["khronos_intptr_t",
- "khronos_uintptr_t",
- "khronos_ssize_t",
- "khronos_usize_t",
- "GLintptr",
- "GLsizeiptr"
- ])
+# The string that the PrintNamesAndSizes plugin uses to indicate a type is
+# expected to have architecture-dependent size.
+ARCH_DEPENDENT_STRING = "ArchDependentSize"
@@ -45,10 +34,42 @@
class TypeInfo:
- """A class representing information about a C++ type."""
+ """A class representing information about a C++ type. It contains the
+ following fields:
+ - kind: The Clang TypeClassName (Record, Enum, Typedef, Union, etc)
+ - name: The unmangled string name of the type.
+ - size: The size in bytes of the type.
+ - arch_dependent: True if the type may have architecture dependent size
+ according to PrintNamesAndSizes. False otherwise. Types
+ which are considered architecture-dependent from 32-bit
+ to 64-bit are pointers, longs, unsigned longs, and any
+ type that contains an architecture-dependent type.
+ - source_location: A SourceLocation describing where the type is defined.
+ - target: The target Clang was compiling when it found the type definition.
+ This is used only for diagnostic output.
+ - parsed_line: The line which Clang output which was used to create this
+ TypeInfo (as the info_string parameter to __init__). This is
+ used only for diagnostic output.
+ """
def __init__(self, info_string, target):
- [self.kind, self.name, self.size, has_pointer_string, source_file,
+ """Create a TypeInfo from a given info_string. Also store the name of the
+ target for which the TypeInfo was first created just so we can print useful
+ error information.
+ info_string is a comma-delimited string of the following form:
+ kind,name,size,arch_dependent,source_file,start_line,end_line
+ Where:
+ - kind: The Clang TypeClassName (Record, Enum, Typedef, Union, etc)
+ - name: The unmangled string name of the type.
+ - size: The size in bytes of the type.
+ - arch_dependent: 'ArchDependentSize' if the type has architecture-dependent
+ size, NotArchDependentSize otherwise.
+ - source_file: The source file in which the type is defined.
+ - first_line: The first line of the definition (counting from 0).
+ - last_line: The last line of the definition (counting from 0).
+ This should match the output of the PrintNamesAndSizes plugin.
+ """
+ [self.kind, self.name, self.size, arch_dependent_string, source_file,
start_line, end_line] = info_string.split(',')
self.target = target
self.parsed_line = info_string
@@ -56,24 +77,13 @@
self.source_location = SourceLocation(source_file,
int(start_line)-1,
int(end_line)-1)
- # If the type is one of our known special cases, mark it as architecture-
- # dependent.
- if self.name in ARCH_DEPENDENT_TYPES:
- self.arch_dependent = True
- # Otherwise, its size can be architecture dependent if it contains one or
- # more pointers (or is a pointer).
- else:
- self.arch_dependent = (has_pointer_string == HAS_POINTER_STRING)
- self.target = target
- self.parsed_line = info_string
+ self.arch_dependent = (arch_dependent_string == ARCH_DEPENDENT_STRING)
-
class FilePatch:
"""A class representing a set of line-by-line changes to a particular file.
- None
- of the changes are applied until Apply is called. All line numbers are
+ None of the changes are applied until Apply is called. All line numbers are
counted from 0.
"""
@@ -85,7 +95,9 @@
self.lines_to_add = {}
def Delete(self, start_line, end_line):
- """Make the patch delete the lines [start_line, end_line)."""
+ """Make the patch delete the lines starting with |start_line| up to but not
+ including |end_line|.
+ """
self.linenums_to_delete |= set(range(start_line, end_line))
def Add(self, text, line_number):
@@ -96,7 +108,7 @@
self.lines_to_add[line_number] = [text]
def Apply(self):
- """Apply the patch by writing it to self.filename"""
+ """Apply the patch by writing it to self.filename."""
# Read the lines of the existing file in to a list.
sourcefile = open(self.filename, "r")
file_lines = sourcefile.readlines()
@@ -163,14 +175,12 @@
typeinfo_map[typeinfo.name] = typeinfo
-def ProcessTarget(clang_command, target, arch_types, ind_types):
+def ProcessTarget(clang_command, target, types):
"""Run clang using the given clang_command for the given target string. Parse
the output to create TypeInfos for each discovered type. Insert each type in
- to the appropriate dictionary. For each type that has architecture-dependent
- size, insert it in to arch_types. Types with independent size go in to
- ind_types. If the type already exists in the appropriate map, make sure that
- the size matches what's already in the map. If not, the script terminates
- with an error message.
+ to the 'types' dictionary. If the type already exists in the types
+ dictionary, make sure that the size matches what's already in the map. If
+ not, exit with an error message.
"""
p = subprocess.Popen(clang_command + " -triple " + target,
shell=True,
@@ -178,13 +188,7 @@
lines = p.communicate()[0].split()
for line in lines:
typeinfo = TypeInfo(line, target)
- # Put types which have architecture-specific size in to arch_types. All
- # other types are 'architecture-independent' and get put in ind_types.
- # in the appropraite dictionary.
- if typeinfo.arch_dependent:
- CheckAndInsert(typeinfo, arch_types)
- else:
- CheckAndInsert(typeinfo, ind_types)
+ CheckAndInsert(typeinfo, types)
def ToAssertionCode(typeinfo):
@@ -245,6 +249,26 @@
def main(argv):
+ # See README file for example command-line invocation. This script runs the
+ # PrintNamesAndSizes Clang plugin with 'test_struct_sizes.c' as input, which
+ # should include all C headers and all existing size checks. It runs the
+ # plugin multiple times; once for each of a set of targets, some 32-bit and
+ # some 64-bit. It verifies that wherever possible, types have a consistent
+ # size on both platforms. Types that can't easily have consistent size (e.g.
+ # ones that contain a pointer) are checked to make sure they are consistent
+ # for all 32-bit platforms and consistent on all 64-bit platforms, but the
+ # sizes on 32 vs 64 are allowed to differ.
+ #
+ # Then, if all the types have consistent size as expected, compile assertions
+ # are added to the source code. Types whose size is independent of
+ # architectureacross have their compile assertions placed immediately after
+ # their definition in the C API header. Types whose size differs on 32-bit
+ # vs 64-bit have a compile assertion placed in each of:
+ # ppapi/tests/arch_dependent_sizes_32.h and
+ # ppapi/tests/arch_dependent_sizes_64.h.
+ #
+ # Note that you should always check the results of the tool to make sure
+ # they are sane.
parser = optparse.OptionParser()
parser.add_option(
'-c', '--clang-path', dest='clang_path',
@@ -293,19 +317,70 @@
# Now run clang for each target. Along the way, make sure architecture-
# dependent types are consistent sizes on all 32-bit platforms and consistent
- # on all 64-bit platforms. Any types in 'types_independent' are checked for
- # all targets to make sure their size is consistent across all of them.
+ # on all 64-bit platforms.
targets32 = options.targets32.split(',');
for target in targets32:
- ProcessTarget(clang_command, target, types32, types_independent)
+ # For each 32-bit target, run the PrintNamesAndSizes Clang plugin to get
+ # information about all types in the translation unit, and add a TypeInfo
+ # for each of them to types32. If any size mismatches are found,
+ # ProcessTarget will spit out an error and exit.
+ ProcessTarget(clang_command, target, types32)
targets64 = options.targets64.split(',');
for target in targets64:
- ProcessTarget(clang_command, target, types64, types_independent)
+ # Do the same as above for each 64-bit target; put all types in types64.
+ ProcessTarget(clang_command, target, types64)
- # This dictionary maps file names to FilePatch objects.
+ # Now for each dictionary, find types whose size are consistent regardless of
+ # architecture, and move those in to types_independent. Anywhere sizes
+ # differ, make sure they are expected to be architecture-dependent based on
+ # their structure. If we find types which could easily be consistent but
+ # aren't, spit out an error and exit.
+ types_independent = {}
+ for typename, typeinfo32 in types32.items():
+ if (typename in types64):
+ typeinfo64 = types64[typename]
+ if (typeinfo64.size == typeinfo32.size):
+ # The types are the same size, so we can treat it as arch-independent.
+ types_independent[typename] = typeinfo32
+ del types32[typename]
+ del types64[typename]
+ elif (typeinfo32.arch_dependent or typeinfo64.arch_dependent):
+ # The type is defined in such a way that it would be difficult to make
+ # its size consistent. E.g., it has pointers. We'll leave it in the
+ # arch-dependent maps so that we can put arch-dependent size checks in
+ # test code.
+ pass
+ else:
+ # The sizes don't match, but there's no reason they couldn't. It's
+ # probably due to an alignment mismatch between Win32/NaCl vs Linux32/
+ # Mac32.
+ print "Error: '" + typename + "' is", typeinfo32.size, \
+ "bytes on target '" + typeinfo32.target + \
+ "', but", typeinfo64.size, "on target '" + typeinfo64.target + "'"
+ print typeinfo32.parsed_line
+ print typeinfo64.parsed_line
+ sys.exit(1)
+ else:
+ print "WARNING: Type '", typename, "' was defined for target '",
+ print typeinfo32.target, ", but not for any 64-bit targets."
+
+ # Now we have all the information we need to generate our static assertions.
+ # Types that have consistent size across architectures will have the static
+ # assertion placed immediately after their definition. Types whose size
+ # depends on 32-bit vs 64-bit architecture will have checks placed in
+ # tests/arch_dependent_sizes_32/64.h.
+
+ # This dictionary maps file names to FilePatch objects. We will add items
+ # to it as needed. Each FilePatch represents a set of changes to make to the
+ # associated file (additions and deletions).
file_patches = {}
- # Find locations of existing macros, and just delete them all.
+ # Find locations of existing macros, and just delete them all. Note that
+ # normally, only things in 'types_independent' need to be deleted, as arch-
+ # dependent checks exist in tests/arch_dependent_sizes_32/64.h, which are
+ # always completely over-ridden. However, it's possible that a type that used
+ # to be arch-independent has changed to now be arch-dependent (e.g., because
+ # a pointer was added), and we want to delete the old check in that case.
for name, typeinfo in \
types_independent.items() + types32.items() + types64.items():
if IsMacroDefinedName(name):
@@ -318,18 +393,29 @@
# Add a compile-time assertion for each type whose size is independent of
# architecture. These assertions go immediately after the class definition.
for name, typeinfo in types_independent.items():
- # Ignore macros and types that are 0 bytes (i.e., typedefs to void)
+ # Ignore dummy types that were defined by macros and also ignore types that
+ # are 0 bytes (i.e., typedefs to void).
if not IsMacroDefinedName(name) and typeinfo.size > 0:
sourcefile = typeinfo.source_location.filename
if sourcefile not in file_patches:
file_patches[sourcefile] = FilePatch(sourcefile)
# Add the assertion code just after the definition of the type.
+ # E.g.:
+ # struct Foo {
+ # int32_t x;
+ # };
+ # PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(Foo, 4); <---Add this line
file_patches[sourcefile].Add(ToAssertionCode(typeinfo),
typeinfo.source_location.end_line+1)
+ # Apply our patches. This actually edits the files containing the definitions
+ # for the types in types_independent.
for filename, patch in file_patches.items():
patch.Apply()
+ # Write out a file of checks for 32-bit architectures and a separate file for
+ # 64-bit architectures. These only have checks for types that are
+ # architecture-dependent.
c_source_root = os.path.join(options.ppapi_root, "tests")
WriteArchSpecificCode(types32.values(),
c_source_root,
« 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