webengine/osswebengine/WebKitTools/CodeCoverage/run-generate-coverage-data
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 #!/usr/bin/env python
       
     2 
       
     3 # Copyright (C) 2004, 2005, 2006 Nathaniel Smith
       
     4 # Copyright (C) 2007 Holger Hans Peter Freyther
       
     5 #
       
     6 # Redistribution and use in source and binary forms, with or without
       
     7 # modification, are permitted provided that the following conditions
       
     8 # are met:
       
     9 #
       
    10 # 1.  Redistributions of source code must retain the above copyright
       
    11 #     notice, this list of conditions and the following disclaimer. 
       
    12 # 2.  Redistributions in binary form must reproduce the above copyright
       
    13 #     notice, this list of conditions and the following disclaimer in the
       
    14 #     documentation and/or other materials provided with the distribution. 
       
    15 # 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
       
    16 #     its contributors may be used to endorse or promote products derived
       
    17 #     from this software without specific prior written permission. 
       
    18 #
       
    19 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
       
    20 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
    21 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
       
    22 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
       
    23 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
       
    24 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
       
    25 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
       
    26 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
       
    28 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    29 
       
    30 import os, sys
       
    31 
       
    32 # from BitBake
       
    33 def mkdirhier(dir):
       
    34     """Create a directory like 'mkdir -p', but does not complain if
       
    35     directory already exists like os.makedirs
       
    36     """
       
    37     try:
       
    38         os.makedirs(dir)
       
    39     except OSError, e:
       
    40         if e.errno != 17: raise e
       
    41 
       
    42 def collect_base(src,match_array):
       
    43     """
       
    44     Collect all files that match the match_array.
       
    45     """
       
    46 
       
    47     sources = []
       
    48     for root, dirs, files in os.walk(src):
       
    49         if ".svn" in root:
       
    50             continue
       
    51 
       
    52         for file in files:
       
    53             base,ext = os.path.splitext(file)
       
    54             if ext in match_array:
       
    55                 sources.append( os.path.join(root, file) )
       
    56 
       
    57     return sources
       
    58 
       
    59 def collect_depends(src):
       
    60     return collect_base(src, [".d"])
       
    61 
       
    62 def parse_dependency_file(src, base_dir, black_list):
       
    63     """
       
    64     Parse the .d files of the gcc
       
    65 
       
    66     Wow, the first time os.path.join is doing the right thing. We might
       
    67     have a relative path in the depends using os.path.join(dirname of .d, dep)
       
    68     we will end up in 
       
    69     """
       
    70     file = open(src)
       
    71     file = file.read()
       
    72     file = file.replace('\\', '').replace('\n', '')
       
    73 
       
    74     # We now have object: dependencies splitted
       
    75     ar  = file.split(':', 1)
       
    76     obj = ar[0].strip()
       
    77     dir = os.path.dirname(obj)
       
    78     deps = ar[1].split(' ')
       
    79 
       
    80     # Remove files outside WebKit, make path absolute
       
    81     deps = filter(lambda x: base_dir in x, deps)
       
    82     deps = map(lambda x: os.path.abspath(os.path.join(dir, x)), deps)
       
    83     return (obj, dir, deps)
       
    84 
       
    85 def collect_cov(base_path,targets):
       
    86     """
       
    87     Collect gcov files, collect_sources is not used as it also creates
       
    88     dirs and needs to do substituting.
       
    89     Actually we will build a mapping from source file to gcov files of
       
    90     interest. This is because we could have bytestream.h in many different
       
    91     subdirectories. And we would endup with bla.cpp##bytestream.h and we
       
    92     do not know which bytestream file was tested
       
    93     """
       
    94     def find_source_file(root,cov_file):
       
    95         """ Find a Source line or crash
       
    96 
       
    97         '#Users#ich#projekte#src#threadmessage.cpp###space#dports#include#qt3#qstring.h.gcov'
       
    98         '#Users#ich#projekte#src#threadmessage.cpp##..#^#src#threadmessage.cpp.gcov'
       
    99 
       
   100         ### is absolute path
       
   101         ##..#^# is relative path... well a gcov bug as well
       
   102         ##  normal split file in the same directory
       
   103         """
       
   104         if '###' in cov_file:
       
   105             split = cov_file.split('###')
       
   106             if not len(split) == 2:
       
   107                 raise "Unexpected split result"
       
   108             filepath = split[1][:-5].replace('#',os.path.sep)
       
   109             return os.path.join(os.path.sep,filepath)
       
   110         elif '##..#^#' in cov_file: 
       
   111             split = cov_file.split('##..#^#')
       
   112             if not len(split) == 2:
       
   113                 raise "Unexpected split result"
       
   114             filepath = split[1][:-5].replace('#',os.path.sep)
       
   115             return os.path.abspath(os.path.join(root,os.path.pardir,os.path.pardir,filepath))
       
   116         elif '##' in cov_file:
       
   117             split = cov_file.split('##')
       
   118             if not len(split) == 2:
       
   119                 raise "Unexpected split result"
       
   120             filepath = split[1][:-5].replace('#',os.path.sep)
       
   121             return os.path.abspath(os.path.join(root,filepath))
       
   122         elif '#' in cov_file:
       
   123             # wow a not broken gcov on OSX
       
   124             basename=os.path.basename(cov_file).replace('#',os.path.sep)[:-5]
       
   125             return os.path.abspath(os.path.join(root,basename))
       
   126 
       
   127         else:
       
   128             raise "No source found %s" % cov_file
       
   129 
       
   130     def sanitize_path(path):
       
   131         """
       
   132         Well fix up paths once again /usr/lib/gcc/i486-linux-gnu/4.1.2/^/^/^/^/include/c++/4.1.2/bits/stl_pair.h
       
   133         according to gcov '^' is a relative path, we will now build one from this one. Somehow it depends
       
   134         on the gcov version if .. really gets replaced to ^....
       
   135         """
       
   136         import os
       
   137         split = path.split(os.path.sep)
       
   138         str = ""
       
   139         for part in split:
       
   140             if part == '':
       
   141                 str = os.path.sep
       
   142             elif part == '^':
       
   143                 str = "%s..%s" % (str,os.path.sep)
       
   144             else:
       
   145                 str = "%s%s%s" % (str,part,os.path.sep)
       
   146         return os.path.abspath(str)
       
   147 
       
   148 
       
   149     gcov = {}
       
   150     for root, dirs, files in os.walk(base_path):
       
   151         if ".svn" in root:
       
   152             continue
       
   153         for file in files:
       
   154             base,ext = os.path.splitext(file)
       
   155             if ext in [".gcov"]:
       
   156                 try:
       
   157                     cov = os.path.join(root, file)
       
   158                     src = find_source_file( root, cov )
       
   159                     src = sanitize_path( src )
       
   160 
       
   161                     if not src in gcov:
       
   162                         gcov[src] = []
       
   163                     gcov[src].append( cov )
       
   164                 except Exception,e:
       
   165                     print "Exception on ", e
       
   166                     #import sys
       
   167                     #sys.exit(0)
       
   168                     pass
       
   169 
       
   170     #print gcov
       
   171     return gcov
       
   172 
       
   173 def generate_covs(candidates):
       
   174     """
       
   175     Generate gcov files in the right directory
       
   176 
       
   177     candidtaes contains the directories we have used when
       
   178     building. Each directory contains a set of files we will
       
   179     try to generate gcov files for.
       
   180     """
       
   181     print candidates.keys()
       
   182     for dir in candidates.keys():
       
   183         print "Trying in %s" % (dir)
       
   184         for dep in candidates[dir].keys():
       
   185             cmd = "cd %s; gcov -p -l %s" % (dir, dep)
       
   186             os.system("%s > /dev/null 2>&1 " % cmd)
       
   187 
       
   188 
       
   189 def analyze_coverage(sources,data,dirs,runid,base):
       
   190     """
       
   191     sources actual source files relative to src_dir e.g kdelibs/kdecore/klibloader.cpp
       
   192     data    Where to put the stuff
       
   193     dirs    Where to take a look for gcov files
       
   194     base    The base directory for files. All files not inside base will be ignored
       
   195     """
       
   196     import cov
       
   197     print base
       
   198     gcov = collect_cov(base,dirs)
       
   199     result = cov.analyze_coverage(gcov, sources, runid, data, base)
       
   200     print result
       
   201 
       
   202 if __name__ == "__main__":
       
   203     #global targets
       
   204     if not len(sys.argv) == 3:
       
   205         print "This script needs three parameters"
       
   206         print "Call it with generate_cov RUNID ResultsDir"
       
   207         sys.exit(-1)
       
   208     runid = sys.argv[1]
       
   209     results = sys.argv[2]
       
   210 
       
   211     # create directories for out result
       
   212     mkdirhier(results)
       
   213 
       
   214     print "Collection Sources and preparing data tree"
       
   215     base_dir = os.path.abspath(os.path.curdir)
       
   216     depends = collect_depends(base_dir)
       
   217     candidates = map(lambda x: parse_dependency_file(x,base_dir,[]), depends)
       
   218 
       
   219     # Build a number of sources from the candidates. This is a Set for the poor
       
   220     # Two level dict. One for 
       
   221     dirs = {}
       
   222     files = {}
       
   223     for (_,dir,deps) in candidates:
       
   224         if not dir in dirs:
       
   225             dirs[dir] = {}
       
   226         for dep in deps:
       
   227             if not dep in dirs[dir]:
       
   228                 dirs[dir][dep] = dep
       
   229             if not dep in files:
       
   230                 files[dep] = dep
       
   231 
       
   232     sources = files.keys()
       
   233 
       
   234     print "Found %d candidates" % (len(sources))
       
   235     print "Will run inefficient generation of gcov files now"
       
   236     generate_covs(dirs)
       
   237 
       
   238     print "Analyzing Gcov"
       
   239     analyze_coverage(sources, results, dirs.keys(), runid, base_dir)
       
   240     print "Done"