blob: a87da1dcd808539f6f513f575dbbe106a5647ebd [file] [log] [blame]
#!/usr/bin/env python
# Copyright (C) 2004, 2005, 2006 Nathaniel Smith
# Copyright (C) 2007 Holger Hans Peter Freyther
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
# its contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import os, sys
# from BitBake
def mkdirhier(dir):
"""Create a directory like 'mkdir -p', but does not complain if
directory already exists like os.makedirs
"""
try:
os.makedirs(dir)
except OSError, e:
if e.errno != 17: raise e
def collect_base(src,match_array):
"""
Collect all files that match the match_array.
"""
sources = []
for root, dirs, files in os.walk(src):
if ".svn" in root:
continue
for file in files:
base,ext = os.path.splitext(file)
if ext in match_array:
sources.append( os.path.join(root, file) )
return sources
def collect_depends(src):
return collect_base(src, [".d"])
def parse_dependency_file(src, base_dir, black_list):
"""
Parse the .d files of the gcc
Wow, the first time os.path.join is doing the right thing. We might
have a relative path in the depends using os.path.join(dirname of .d, dep)
we will end up in
"""
file = open(src)
file = file.read()
file = file.replace('\\', '').replace('\n', '')
# We now have object: dependencies splitted
ar = file.split(':', 1)
obj = ar[0].strip()
dir = os.path.dirname(obj)
deps = ar[1].split(' ')
# Remove files outside WebKit, make path absolute
deps = filter(lambda x: base_dir in x, deps)
deps = map(lambda x: os.path.abspath(os.path.join(dir, x)), deps)
return (obj, dir, deps)
def collect_cov(base_path,targets):
"""
Collect gcov files, collect_sources is not used as it also creates
dirs and needs to do substituting.
Actually we will build a mapping from source file to gcov files of
interest. This is because we could have bytestream.h in many different
subdirectories. And we would endup with bla.cpp##bytestream.h and we
do not know which bytestream file was tested
"""
def find_source_file(root,cov_file):
""" Find a Source line or crash
'#Users#ich#projekte#src#threadmessage.cpp###space#dports#include#qt3#qstring.h.gcov'
'#Users#ich#projekte#src#threadmessage.cpp##..#^#src#threadmessage.cpp.gcov'
### is absolute path
##..#^# is relative path... well a gcov bug as well
## normal split file in the same directory
"""
if '###' in cov_file:
split = cov_file.split('###')
if not len(split) == 2:
raise "Unexpected split result"
filepath = split[1][:-5].replace('#',os.path.sep)
return os.path.join(os.path.sep,filepath)
elif '##..#^#' in cov_file:
split = cov_file.split('##..#^#')
if not len(split) == 2:
raise "Unexpected split result"
filepath = split[1][:-5].replace('#',os.path.sep)
return os.path.abspath(os.path.join(root,os.path.pardir,os.path.pardir,filepath))
elif '##' in cov_file:
split = cov_file.split('##')
if not len(split) == 2:
raise "Unexpected split result"
filepath = split[1][:-5].replace('#',os.path.sep)
return os.path.abspath(os.path.join(root,filepath))
elif '#' in cov_file:
# wow a not broken gcov on OSX
basename=os.path.basename(cov_file).replace('#',os.path.sep)[:-5]
return os.path.abspath(os.path.join(root,basename))
else:
raise "No source found %s" % cov_file
def sanitize_path(path):
"""
Well fix up paths once again /usr/lib/gcc/i486-linux-gnu/4.1.2/^/^/^/^/include/c++/4.1.2/bits/stl_pair.h
according to gcov '^' is a relative path, we will now build one from this one. Somehow it depends
on the gcov version if .. really gets replaced to ^....
"""
import os
split = path.split(os.path.sep)
str = ""
for part in split:
if part == '':
str = os.path.sep
elif part == '^':
str = "%s..%s" % (str,os.path.sep)
else:
str = "%s%s%s" % (str,part,os.path.sep)
return os.path.abspath(str)
gcov = {}
for root, dirs, files in os.walk(base_path):
if ".svn" in root:
continue
for file in files:
base,ext = os.path.splitext(file)
if ext in [".gcov"]:
try:
cov = os.path.join(root, file)
src = find_source_file( root, cov )
src = sanitize_path( src )
if not src in gcov:
gcov[src] = []
gcov[src].append( cov )
except Exception,e:
print "Exception on ", e
#import sys
#sys.exit(0)
pass
#print gcov
return gcov
def generate_covs(candidates):
"""
Generate gcov files in the right directory
candidtaes contains the directories we have used when
building. Each directory contains a set of files we will
try to generate gcov files for.
"""
print candidates.keys()
for dir in candidates.keys():
print "Trying in %s" % (dir)
for dep in candidates[dir].keys():
cmd = "cd %s; gcov -p -l %s" % (dir, dep)
os.system("%s > /dev/null 2>&1 " % cmd)
def analyze_coverage(sources,data,dirs,runid,base):
"""
sources actual source files relative to src_dir e.g kdelibs/kdecore/klibloader.cpp
data Where to put the stuff
dirs Where to take a look for gcov files
base The base directory for files. All files not inside base will be ignored
"""
import cov
print base
gcov = collect_cov(base,dirs)
result = cov.analyze_coverage(gcov, sources, runid, data, base)
print result
if __name__ == "__main__":
#global targets
if not len(sys.argv) == 3:
print "This script needs three parameters"
print "Call it with generate_cov RUNID ResultsDir"
sys.exit(-1)
runid = sys.argv[1]
results = sys.argv[2]
# create directories for out result
mkdirhier(results)
print "Collection Sources and preparing data tree"
base_dir = os.path.abspath(os.path.curdir)
depends = collect_depends(base_dir)
candidates = map(lambda x: parse_dependency_file(x,base_dir,[]), depends)
# Build a number of sources from the candidates. This is a Set for the poor
# Two level dict. One for
dirs = {}
files = {}
for (_,dir,deps) in candidates:
if not dir in dirs:
dirs[dir] = {}
for dep in deps:
if not dep in dirs[dir]:
dirs[dir][dep] = dep
if not dep in files:
files[dep] = dep
sources = files.keys()
print "Found %d candidates" % (len(sources))
print "Will run inefficient generation of gcov files now"
generate_covs(dirs)
print "Analyzing Gcov"
analyze_coverage(sources, results, dirs.keys(), runid, base_dir)
print "Done"