[clang] 35dd014 - [analyzer] CmpRuns.py: Decouple main functionality from argparse

Valeriy Savchenko via cfe-commits cfe-commits at lists.llvm.org
Tue Jun 16 03:31:23 PDT 2020


Author: Valeriy Savchenko
Date: 2020-06-16T13:30:01+03:00
New Revision: 35dd0147cdd0b8a145592d895d0a64eedb397917

URL: https://github.com/llvm/llvm-project/commit/35dd0147cdd0b8a145592d895d0a64eedb397917
DIFF: https://github.com/llvm/llvm-project/commit/35dd0147cdd0b8a145592d895d0a64eedb397917.diff

LOG: [analyzer] CmpRuns.py: Decouple main functionality from argparse

Summary:
It makes it much harder to use from other modules when one of the
parameters is an argparse Namespace.  This commit makes it easier
to use CmpRuns programmatically.

Differential Revision: https://reviews.llvm.org/D81566

Added: 
    

Modified: 
    clang/utils/analyzer/CmpRuns.py
    clang/utils/analyzer/SATestBuild.py

Removed: 
    


################################################################################
diff  --git a/clang/utils/analyzer/CmpRuns.py b/clang/utils/analyzer/CmpRuns.py
index 5199a87a8205..d80481da03f1 100755
--- a/clang/utils/analyzer/CmpRuns.py
+++ b/clang/utils/analyzer/CmpRuns.py
@@ -35,8 +35,9 @@
 from math import log
 from collections import defaultdict
 from copy import copy
-from typing import (Any, cast, Dict, List, Optional, Sequence, TextIO, TypeVar,
-                    Tuple, Union)
+from enum import Enum
+from typing import (Any, cast, Dict, List, NamedTuple, Optional, Sequence,
+                    TextIO, TypeVar, Tuple, Union)
 
 
 Number = Union[int, float]
@@ -58,6 +59,17 @@ class Colors:
     CLEAR = '\x1b[0m'
 
 
+class HistogramType(str, Enum):
+    RELATIVE = "relative"
+    LOG_RELATIVE = "log-relative"
+    ABSOLUTE = "absolute"
+
+
+class ResultsDirectory(NamedTuple):
+    path: str
+    root: str = ""
+
+
 class SingleRunInfo:
     """
     Information about analysis run:
@@ -65,9 +77,10 @@ class SingleRunInfo:
     root - the name of the root directory, which will be disregarded when
     determining the source file name
     """
-    def __init__(self, path: str, root: str = "", verbose_log=None):
-        self.path = path
-        self.root = root.rstrip("/\\")
+    def __init__(self, results: ResultsDirectory,
+                 verbose_log: Optional[str] = None):
+        self.path = results.path
+        self.root = results.root.rstrip("/\\")
         self.verbose_log = verbose_log
 
 
@@ -232,13 +245,13 @@ def __init__(self, run: AnalysisRun, files: List[str]):
         self.diagnostics: List[AnalysisDiagnostic] = []
 
 
-def load_results(path: str, args: argparse.Namespace, root: str = "",
-                 delete_empty: bool = True) -> AnalysisRun:
+def load_results(results: ResultsDirectory, delete_empty: bool = True,
+                 verbose_log: Optional[str] = None) -> AnalysisRun:
     """
     Backwards compatibility API.
     """
-    return load_results_from_single_run(SingleRunInfo(path, root,
-                                                      args.verbose_log),
+    return load_results_from_single_run(SingleRunInfo(results,
+                                                      verbose_log),
                                         delete_empty)
 
 
@@ -280,7 +293,8 @@ def cmp_analysis_diagnostic(d):
 
 
 def compare_results(results_old: AnalysisRun, results_new: AnalysisRun,
-                    args: argparse.Namespace) -> ComparisonResult:
+                    histogram: Optional[HistogramType] = None
+                    ) -> ComparisonResult:
     """
     compare_results - Generate a relation from diagnostics in run A to
     diagnostics in run B.
@@ -311,15 +325,15 @@ def compare_results(results_old: AnalysisRun, results_new: AnalysisRun,
         if a.get_issue_identifier() == b.get_issue_identifier():
             if a.get_path_length() != b.get_path_length():
 
-                if args.relative_path_histogram:
+                if histogram == HistogramType.RELATIVE:
                     path_
diff erence_data.append(
                         float(a.get_path_length()) / b.get_path_length())
 
-                elif args.relative_log_path_histogram:
+                elif histogram == HistogramType.LOG_RELATIVE:
                     path_
diff erence_data.append(
                         log(float(a.get_path_length()) / b.get_path_length()))
 
-                elif args.absolute_path_histogram:
+                elif histogram == HistogramType.ABSOLUTE:
                     path_
diff erence_data.append(
                         a.get_path_length() - b.get_path_length())
 
@@ -347,8 +361,7 @@ def compare_results(results_old: AnalysisRun, results_new: AnalysisRun,
     for b in neq_new:
         res.append((None, b))
 
-    if args.relative_log_path_histogram or args.relative_path_histogram or \
-            args.absolute_path_histogram:
+    if histogram:
         from matplotlib import pyplot
         pyplot.hist(path_
diff erence_data, bins=100)
         pyplot.show()
@@ -394,7 +407,8 @@ def derive_stats(results: AnalysisRun) -> Stats:
 
 # TODO: compare_results decouples comparison from the output, we should
 #       do it here as well
-def compare_stats(results_old: AnalysisRun, results_new: AnalysisRun):
+def compare_stats(results_old: AnalysisRun, results_new: AnalysisRun,
+                  out: TextIO = sys.stdout):
     stats_old = derive_stats(results_old)
     stats_new = derive_stats(results_new)
 
@@ -403,7 +417,7 @@ def compare_stats(results_old: AnalysisRun, results_new: AnalysisRun):
     keys = sorted(old_keys & new_keys)
 
     for key in keys:
-        print(key)
+        out.write(f"{key}\n")
 
         nested_keys = sorted(set(stats_old[key]) & set(stats_new[key]))
 
@@ -414,7 +428,7 @@ def compare_stats(results_old: AnalysisRun, results_new: AnalysisRun):
             report = f"{val_old:.3f} -> {val_new:.3f}"
 
             # Only apply highlighting when writing to TTY and it's not Windows
-            if sys.stdout.isatty() and os.name != 'nt':
+            if out.isatty() and os.name != 'nt':
                 if val_new != 0:
                     ratio = (val_new - val_old) / val_new
                     if ratio < -0.2:
@@ -422,39 +436,52 @@ def compare_stats(results_old: AnalysisRun, results_new: AnalysisRun):
                     elif ratio > 0.2:
                         report = Colors.RED + report + Colors.CLEAR
 
-            print(f"\t {nested_key} {report}")
+            out.write(f"\t {nested_key} {report}\n")
 
     removed_keys = old_keys - new_keys
     if removed_keys:
-        print(f"REMOVED statistics: {removed_keys}")
+        out.write(f"REMOVED statistics: {removed_keys}\n")
 
     added_keys = new_keys - old_keys
     if added_keys:
-        print(f"ADDED statistics: {added_keys}")
+        out.write(f"ADDED statistics: {added_keys}\n")
 
-    print()
+    out.write("\n")
 
 
-def dump_scan_build_results_
diff (dir_old: str, dir_new: str,
-                                 args: argparse.Namespace,
+def dump_scan_build_results_
diff (dir_old: ResultsDirectory,
+                                 dir_new: ResultsDirectory,
                                  delete_empty: bool = True,
-                                 out: TextIO = sys.stdout):
-    # Load the run results.
-    results_old = load_results(dir_old, args, args.root_old, delete_empty)
-    results_new = load_results(dir_new, args, args.root_new, delete_empty)
+                                 out: TextIO = sys.stdout,
+                                 show_stats: bool = False,
+                                 stats_only: bool = False,
+                                 histogram: Optional[HistogramType] = None,
+                                 verbose_log: Optional[str] = None):
+    """
+    Compare directories with analysis results and dump results.
+
+    :param delete_empty: delete empty plist files
+    :param out: buffer to dump comparison results to.
+    :param show_stats: compare execution stats as well.
+    :param stats_only: compare ONLY execution stats.
+    :param histogram: optional histogram type to plot path 
diff erences.
+    :param verbose_log: optional path to an additional log file.
+    """
+    results_old = load_results(dir_old, delete_empty, verbose_log)
+    results_new = load_results(dir_new, delete_empty, verbose_log)
 
-    if args.show_stats:
+    if show_stats or stats_only:
         compare_stats(results_old, results_new)
-    if args.stats_only:
+    if stats_only:
         return
 
     # Open the verbose log, if given.
-    if args.verbose_log:
-        auxLog: Optional[TextIO] = open(args.verbose_log, "w")
+    if verbose_log:
+        auxLog: Optional[TextIO] = open(verbose_log, "w")
     else:
         auxLog = None
 
-    
diff  = compare_results(results_old, results_new, args)
+    
diff  = compare_results(results_old, results_new, histogram)
     found_
diff s = 0
     total_added = 0
     total_removed = 0
@@ -513,25 +540,16 @@ def generate_option_parser():
                         "[default=None]",
                         action="store", type=str, default=None,
                         metavar="LOG")
-    parser.add_argument("--relative-path-
diff erences-histogram",
-                        action="store_true", dest="relative_path_histogram",
-                        default=False,
-                        help="Show histogram of relative paths 
diff erences. "
-                        "Requires matplotlib")
-    parser.add_argument("--relative-log-path-
diff erences-histogram",
-                        action="store_true",
-                        dest="relative_log_path_histogram", default=False,
-                        help="Show histogram of log relative paths "
-                        "
diff erences. Requires matplotlib")
-    parser.add_argument("--absolute-path-
diff erences-histogram",
-                        action="store_true", dest="absolute_path_histogram",
-                        default=False,
-                        help="Show histogram of absolute paths 
diff erences. "
-                        "Requires matplotlib")
     parser.add_argument("--stats-only", action="store_true", dest="stats_only",
                         default=False, help="Only show statistics on reports")
     parser.add_argument("--show-stats", action="store_true", dest="show_stats",
                         default=False, help="Show change in statistics")
+    parser.add_argument("--histogram", action="store", default=None,
+                        choices=[HistogramType.RELATIVE.value,
+                                 HistogramType.LOG_RELATIVE.value,
+                                 HistogramType.ABSOLUTE.value],
+                        help="Show histogram of paths 
diff erences. "
+                        "Requires matplotlib")
     parser.add_argument("old", nargs=1, help="Directory with old results")
     parser.add_argument("new", nargs=1, help="Directory with new results")
 
@@ -542,10 +560,14 @@ def main():
     parser = generate_option_parser()
     args = parser.parse_args()
 
-    dir_old = args.old[0]
-    dir_new = args.new[0]
+    dir_old = ResultsDirectory(args.old[0], args.root_old)
+    dir_new = ResultsDirectory(args.new[0], args.root_new)
 
-    dump_scan_build_results_
diff (dir_old, dir_new, args)
+    dump_scan_build_results_
diff (dir_old, dir_new,
+                                 show_stats=args.show_stats,
+                                 stats_only=args.stats_only,
+                                 histogram=args.histogram,
+                                 verbose_log=args.verbose_log)
 
 
 if __name__ == '__main__':

diff  --git a/clang/utils/analyzer/SATestBuild.py b/clang/utils/analyzer/SATestBuild.py
index 41cb5db44762..b81d09ff3188 100755
--- a/clang/utils/analyzer/SATestBuild.py
+++ b/clang/utils/analyzer/SATestBuild.py
@@ -775,12 +775,12 @@ def run_cmp_results(directory: str, strictness: int = 0) -> bool:
 
         patched_source = os.path.join(directory, PATCHED_SOURCE_DIR_NAME)
 
-        # TODO: get rid of option parser invocation here
-        args = CmpRuns.generate_option_parser().parse_args(
-            ["--root-old", "", "--root-new", patched_source, "", ""])
+        ref_results = CmpRuns.ResultsDirectory(ref_dir)
+        new_results = CmpRuns.ResultsDirectory(new_dir, patched_source)
+
         # Scan the results, delete empty plist files.
         num_
diff s, reports_in_ref, reports_in_new = \
-            CmpRuns.dump_scan_build_results_
diff (ref_dir, new_dir, args,
+            CmpRuns.dump_scan_build_results_
diff (ref_results, new_results,
                                                  delete_empty=False,
                                                  out=LOCAL.stdout)
 


        


More information about the cfe-commits mailing list