[PATCH 3/4] gentree.py: add SmPL patch equivalence proof support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



If you have an SmPL patch that you think is ready to replace
a series of unified diff patches you can pass as an argument
to gentree.py '--prove-cocci' with the cocci file as the
an extra argument and gentree.py will try to prove equivalence
between the unified diff series and your Coccinelle SmPL patch.

You can use this as a guide when trying to replace an existing
unified diff series carried within backports with a respective
Coccinelle SmPL patch. You can use this to get a warm fuzzy or
to provide backports maintainers with a warm fuzzy of comfort
that your SmPL patch is sufficient.

In practice you might end up discovering as differences:

  a) minor space cosmetic differences between what the Coccinelle
     engine does and what we as humans typically would do
     Coccinelle has already received substantial modificaitons to
     address some of these minor spacing changes, but some might
     still exist.

  b) new code which previously was not modified, these are
     transformations which Coccinelle is providied to use,
     and hence further automating our backporting effort.
     If provide a backport of a collateral evolution in
     SmPL form it means we only have to write it once.

  c) bugs within Coccinelle

a) and b) the most common differences observed, whereas we
have yet to find c). If you find a) or c) please report the
differences on the Coccinelle development mailing list.

The algorithm used for SmPL patch equivalence proof is as follows,
using the command below:

mcgrof@cerro ~/backports (git::master)$ ./gentree.py --clean --prove-cocci \
	patches/collateral-evolutions/network/09-threaded-irq.cocci \
	/home/mcgrof/linux-next/ \
	/home/mcgrof/build/backports-20131206

When this is run gentree.py will first copy code we wish to backport
from linux-next into two separate directories:

  1) /home/mcgrof/build/backports-20131206
     We then only apply the Coccinelle SmPL patch 09-threaded-irq.cocci

  2) /home/mcgrof/build/backports-20131206.orig
     We then only apply the unified diff patches found within the directory:
     patches/collateral-evolutions/network/09-threaded-irq/

At this point we now have two treees with two different backporting
strategies. Internally when --prove-cocci is used gentree.py creates
within backports-20131206 a git to keep track of differences, this
includes application of the 09-threaded-irq.cocci patch. We take
advantage of the fact git is used then and replace all the files
within backports-20131206.orig into backports-20131206 and just
ask git for the differences.

In this particular case the diff stat shown reveals huge amount of
differences, in this case upon inspecting the changes it reveals
that using Coccinelle has enabled the generalized backport to
propagate into a lot of other device drivers, and hence doing
more automatic backport work for us. This is precicely why we
want to generalize backports in Coccinelle SmPL form when possible.

A diff stat with no output would mean that there is a perfect
equivalence.

The output of the command:

Copy original source files ...
Apply patches ...
Proving Coccinelle SmPL patch: 09-threaded-irq.cocci
Aggregating patch for Coccinelle SmPL 09-threaded-irq proof: drivers_net_wireless_b43_b43.patch
Aggregating patch for Coccinelle SmPL 09-threaded-irq proof: drivers_net_wireless_iwlwifi_pcie_internal.patch
Aggregating patch for Coccinelle SmPL 09-threaded-irq proof: drivers_net_wireless_ti_wlcore_main.patch
Aggregating patch for Coccinelle SmPL 09-threaded-irq proof: drivers_net_wireless_b43_main.patch
Aggregating patch for Coccinelle SmPL 09-threaded-irq proof: drivers_net_wireless_ti_wlcore_main_extra.patch
Aggregating patch for Coccinelle SmPL 09-threaded-irq proof: drivers_net_wireless_iwlwifi_pcie_trans.patch
Aggregating patch for Coccinelle SmPL 09-threaded-irq proof: drivers_net_wireless_ti_wlcore_wlcore.patch

Review the difference between what the tree looks like
by applying your SmPL patch, then reverting that and
only applying your the patches in the patch series.

Change directory to /home/mcgrof/build/backports-20131206 and run 'git diff'
diff stat of the changes:

 drivers/media/platform/via-camera.c           | 28 +-----------
 drivers/media/radio/radio-wl1273.c            | 28 +-----------
 drivers/media/radio/si470x/radio-si470x-i2c.c | 24 +----------
 drivers/media/radio/si470x/radio-si470x.h     |  3 --
 drivers/net/ieee802154/mrf24j40.c             | 20 ---------
 drivers/net/wireless/ath/wil6210/interrupt.c  | 62 +++------------------------
 drivers/net/wireless/ath/wil6210/wil6210.h    |  3 --
 drivers/net/wireless/b43/main.c               | 21 ++++-----
 drivers/net/wireless/cw1200/cw1200_sdio.c     | 21 ---------
 drivers/net/wireless/cw1200/cw1200_spi.c      | 27 +-----------
 drivers/net/wireless/iwlwifi/pcie/internal.h  |  3 ++
 drivers/net/wireless/iwlwifi/pcie/trans.c     | 34 ++++++---------
 drivers/net/wireless/ti/wlcore/main.c         | 26 +++++------
 drivers/nfc/microread/i2c.c                   | 27 +-----------
 drivers/nfc/pn544/i2c.c                       | 27 +-----------
 drivers/regulator/da9063-regulator.c          | 26 -----------
 drivers/regulator/lp8755.c                    | 23 +---------
 17 files changed, 52 insertions(+), 351 deletions(-)

Signed-off-by: Luis R. Rodriguez <mcgrof@xxxxxxxxxxxxxxxx>
---
 gentree.py | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 63 insertions(+), 8 deletions(-)

diff --git a/gentree.py b/gentree.py
index 636ea60..9e9293b 100755
--- a/gentree.py
+++ b/gentree.py
@@ -485,14 +485,30 @@ def apply_patches(args, desc, source_dir, patch_src, target_dir, logwrite=lambda
     a specific SmPL patch.
     """
     logwrite('Applying patches from %s to %s ...' % (patch_src, target_dir))
-    test_cocci = args.test_cocci or args.profile_cocci
+    test_cocci = args.test_cocci or args.profile_cocci or args.prove_cocci
+    # We don't skip all patches for SmPL proof, we need to use its old series
+    skip_patches = args.profile_cocci or args.test_cocci
     test_cocci_found = False
+    prove_cocci_tmp_dir = ''
+    if args.prove_cocci:
+        test_cocci = test_cocci.split('/')[-1]
+        prove_cocci_tmp_dir = target_dir + '.orig'
+        shutil.rmtree(prove_cocci_tmp_dir, ignore_errors=True)
+        copytree(target_dir, prove_cocci_tmp_dir)
     patches = []
     sempatches = []
     for root, dirs, files in os.walk(os.path.join(source_dir, patch_src)):
         for f in files:
-            if not test_cocci and f.endswith('.patch'):
-                patches.append(os.path.join(root, f))
+            if not skip_patches and f.endswith('.patch'):
+                if args.prove_cocci:
+                    # As an example if you use --prove-cocci patches/collateral-evolutions/network/09-threaded-irq.cocci
+                    # the patches under 09-threaded-irq will be used for the proof.
+                    proof_to_test_dir = test_cocci.split('/')[-1].split('.cocci')[0]
+                    if proof_to_test_dir in os.path.join(root, f):
+                        logwrite("Aggregating patch for Coccinelle SmPL %s proof: %s" % (proof_to_test_dir, f))
+                        patches.append(os.path.join(root, f))
+                else:
+                    patches.append(os.path.join(root, f))
             if f.endswith('.cocci'):
                 if test_cocci:
                     if f not in test_cocci:
@@ -502,6 +518,8 @@ def apply_patches(args, desc, source_dir, patch_src, target_dir, logwrite=lambda
                         logwrite("Testing Coccinelle SmPL patch: %s" % test_cocci)
                     elif args.profile_cocci:
                         logwrite("Profiling Coccinelle SmPL patch: %s" % test_cocci)
+                    elif args.prove_cocci:
+                        logwrite("Proving Coccinelle SmPL patch: %s" % test_cocci)
                 sempatches.append(os.path.join(root, f))
     patches.sort()
     prefix_len = len(os.path.join(source_dir, patch_src)) + 1
@@ -533,10 +551,13 @@ def apply_patches(args, desc, source_dir, patch_src, target_dir, logwrite=lambda
                 fullfn = os.path.join(target_dir, patched_file)
                 shutil.copyfile(fullfn, fullfn + '.orig_file')
 
+        patch_target_dir = target_dir
+        if args.prove_cocci:
+            patch_target_dir = prove_cocci_tmp_dir
         process = subprocess.Popen(['patch', '-p1'], stdout=subprocess.PIPE,
                                    stderr=subprocess.STDOUT, stdin=subprocess.PIPE,
                                    close_fds=True, universal_newlines=True,
-                                   cwd=target_dir)
+                                   cwd=patch_target_dir)
         output = process.communicate(input=open(pfile, 'r').read())[0]
         output = output.split('\n')
         if output[-1] == '':
@@ -578,7 +599,13 @@ def apply_patches(args, desc, source_dir, patch_src, target_dir, logwrite=lambda
             for f in files:
                 if f[-5:] == '.orig' or f[-4:] == '.rej':
                     os.unlink(os.path.join(root, f))
-        git_debug_snapshot(args, "apply %s patch %s" % (desc, print_name))
+        if patch_target_dir != target_dir:
+            for root, dirs, files in os.walk(patch_target_dir):
+                for f in files:
+                    if f[-5:] == '.orig' or f[-4:] == '.rej':
+                        os.unlink(os.path.join(root, f))
+        if not args.prove_cocci:
+            git_debug_snapshot(args, "apply %s patch %s" % (desc, print_name))
 
     sempatches.sort()
     prefix_len = len(os.path.join(source_dir, patch_src)) + 1
@@ -615,8 +642,27 @@ def apply_patches(args, desc, source_dir, patch_src, target_dir, logwrite=lambda
             for f in files:
                 if f.endswith('.cocci_backup'):
                     os.unlink(os.path.join(root, f))
+        if patch_target_dir != target_dir:
+            for root, dirs, files in os.walk(patch_target_dir):
+                for f in files:
+                    if f.endswith('.cocci_backup'):
+                        os.unlink(os.path.join(root, f))
         git_debug_snapshot(args, "apply %s SmPL patch %s" % (desc, print_name))
 
+    if args.prove_cocci:
+        copytree(prove_cocci_tmp_dir, target_dir, ignore=shutil.ignore_patterns('.git'))
+        shutil.rmtree(prove_cocci_tmp_dir, ignore_errors=True)
+        diff_stat = git.diff(tree=target_dir, extra_args=['--stat'])
+        if len(diff_stat) == 0:
+            logwrite('\nSmPL patch fully replaces patch series')
+        else:
+            logwrite('\nReview the difference between what the tree looks like ')
+            logwrite('by applying your SmPL patch, then reverting that and ')
+            logwrite('only applying your the patches in the patch series.')
+            logwrite('')
+            logwrite('Change directory to %s and run \'git diff\'' % target_dir)
+            logwrite('diff stat of the changes:\n')
+        logwrite(diff_stat)
     if test_cocci and test_cocci_found:
         logwrite('Done!')
         sys.exit(0)
@@ -675,6 +721,11 @@ def _main():
                         help='Only use the cocci file passed and pass --profile  to Coccinelle, ' +
                              'also creates a git repo on the target directory for easy inspection ' +
                              'of changes done by Coccinelle.')
+    parser.add_argument('--prove-cocci', metavar='<sp_file>', type=str, default=None,
+                        help='Only use the cocci file passed for Coccinelle and prove that it provides ' +
+                             'sufficient replacement for the patches under similar path name. Running ' +
+                             'git diff on the target directory suffices to show you what extra things' +
+                             'your SmPL patch did or what it didn\'t cover.')
     args = parser.parse_args()
 
     # When building a package we use CPTCFG as we can rely on the
@@ -721,6 +772,7 @@ def _main():
                    kup_test=args.kup_test,
                    test_cocci=args.test_cocci,
                    profile_cocci=args.profile_cocci,
+                   prove_cocci=args.prove_cocci,
                    logwrite=logwrite)
 
 def process(kerneldir, copy_list_file, git_revision=None,
@@ -730,6 +782,7 @@ def process(kerneldir, copy_list_file, git_revision=None,
             kup_test=False,
             test_cocci=None,
             profile_cocci=None,
+            prove_cocci=None,
             logwrite=lambda x:None,
             git_tracked_version=False):
     class Args(object):
@@ -738,7 +791,8 @@ def process(kerneldir, copy_list_file, git_revision=None,
                      gitdebug, verbose, extra_driver, kup,
                      kup_test,
                      test_cocci,
-                     profile_cocci):
+                     profile_cocci,
+                     prove_cocci):
             self.kerneldir = kerneldir
             self.copy_list = copy_list_file
             self.git_revision = git_revision
@@ -753,7 +807,8 @@ def process(kerneldir, copy_list_file, git_revision=None,
             self.kup_test = kup_test
             self.test_cocci = test_cocci
             self.profile_cocci = profile_cocci
-            if self.test_cocci or self.profile_cocci:
+            self.prove_cocci = prove_cocci
+            if self.test_cocci or self.profile_cocci or self.prove_cocci:
                 self.gitdebug = True
     def git_paranoia(tree=None, logwrite=lambda x:None):
         data = git.paranoia(tree)
@@ -767,7 +822,7 @@ def process(kerneldir, copy_list_file, git_revision=None,
     args = Args(kerneldir, copy_list_file,
                 git_revision, bpid, clean, refresh, base_name,
                 gitdebug, verbose, extra_driver, kup, kup_test,
-                test_cocci, profile_cocci)
+                test_cocci, profile_cocci, prove_cocci)
     rel_prep = None
 
     if bpid.integrate:
-- 
2.3.2.209.gd67f9d5.dirty

--
To unsubscribe from this list: send the line "unsubscribe backports" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux