blob: 1a7da7e781b89bf98dadf6deeb92902d082867a0 [file] [log] [blame]
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# Copyright 2018 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Find patches that are missing from stable kernels."""
from __future__ import print_function
import argparse
import base64
import os
import sys
import config
import simpledb
import utils
class PatchFinderException(Exception):
"""Exceptions raised by PatchFinder."""
class PatchFinder(object):
"""PatchFinder locates patches that are absent in stable kernels."""
DEFAULTS = {
'414': (config.SRC_LINUX_STABLE_414_DB, 'linux-4.14.y'),
'44': (config.SRC_LINUX_STABLE_44_DB, 'linux-4.4.y'),
}
def __init__(self, kver, objfiles):
self.kver = kver
self.dbname, self.branch = PatchFinder.DEFAULTS[self.kver]
self.assert_dbs_exist()
self.db = simpledb.SimpleDB(self.dbname)
self.mainline = simpledb.SimpleDB(config.SRC_LINUX_DB)
contents = open(os.path.expanduser(objfiles)).readlines()
objfiles = [i.strip() for i in contents]
self.srcfiles = [i[2:-2] + '.c' for i in objfiles if i.endswith('.o')]
self.stats = {}
def assert_dbs_exist(self):
"Check if caches exist, else raise PatchFinderException."""
if not os.path.isfile(self.dbname):
raise PatchFinderException('%s not found, create with dbgen.py'
% (self.dbname))
if not os.path.isfile(config.SRC_LINUX_DB):
raise PatchFinderException('%s not found, create with dbgen.py'
% (config.SRC_LINUX_DB))
def commit_in_stable(self, title):
"""Returns true if |title| is a commit present in stable."""
stable_commit = self.db.find_one(title=title)
if not stable_commit:
return False
return True
def commit_is_interesting(self, files):
"""Returns true if a in |files| is used in a kernel build."""
return any(i in files for i in self.srcfiles)
def process(self):
"""PatchFinder core."""
print('Mainline commit count=%d' % (self.mainline.count()))
commits = self.mainline.all()
for counter, commit in enumerate(commits):
if (counter+1) % 100000 == 0:
print('--- %d' % (counter+1))
body, files = (base64.b64decode(i) for i in
[commit['body'], commit['files']])
files = [i for i in files.splitlines() if i]
if not utils.interesting_keyword_in(body):
continue
if self.commit_in_stable(commit['title']):
utils.incstats(self.stats, 'commit_already_in_stable')
continue
if not self.commit_is_interesting(files):
utils.incstats(self.stats, 'commit_is_not_interesting')
continue
causer = utils.fixes_stmt(body)
if not causer:
continue
cstr = utils.commitstr(causer)
if not cstr:
utils.incstats(self.stats, 'fixes_stmt_unparseable')
continue
if not self.commit_in_stable(cstr):
utils.incstats(self.stats, 'root_cause_present')
continue
print('Missing commit: %s ("%s")'
% (commit['commitid'][:10], commit['title']))
utils.incstats(self.stats, 'count')
def dump_stats(self):
"""Print stats related to missing commits."""
print(self.stats)
def get_parser():
"""Create and return an ArgumentParser instance."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--kver', type=str, required=True,
choices=['414', '44'],
help='Choose one of supported kernel versions')
parser.add_argument('--objfiles', type=str, required=True,
help='objfiles to use for filtering results')
return parser
def main(argv):
"""main."""
parser = get_parser()
opts = parser.parse_args(argv)
p = PatchFinder(opts.kver, opts.objfiles)
p.process()
p.dump_stats()
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))