| #!/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" |