1*3ac0a46fSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*3ac0a46fSAndroid Build Coastguard Worker# Copyright 2017 The PDFium Authors 3*3ac0a46fSAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*3ac0a46fSAndroid Build Coastguard Worker# found in the LICENSE file. 5*3ac0a46fSAndroid Build Coastguard Worker"""Compares the performance of two versions of the pdfium code.""" 6*3ac0a46fSAndroid Build Coastguard Worker 7*3ac0a46fSAndroid Build Coastguard Workerimport argparse 8*3ac0a46fSAndroid Build Coastguard Workerimport functools 9*3ac0a46fSAndroid Build Coastguard Workerimport glob 10*3ac0a46fSAndroid Build Coastguard Workerimport json 11*3ac0a46fSAndroid Build Coastguard Workerimport multiprocessing 12*3ac0a46fSAndroid Build Coastguard Workerimport os 13*3ac0a46fSAndroid Build Coastguard Workerimport re 14*3ac0a46fSAndroid Build Coastguard Workerimport shutil 15*3ac0a46fSAndroid Build Coastguard Workerimport subprocess 16*3ac0a46fSAndroid Build Coastguard Workerimport sys 17*3ac0a46fSAndroid Build Coastguard Workerimport tempfile 18*3ac0a46fSAndroid Build Coastguard Worker 19*3ac0a46fSAndroid Build Coastguard Workerfrom common import GetBooleanGnArg 20*3ac0a46fSAndroid Build Coastguard Workerfrom common import PrintErr 21*3ac0a46fSAndroid Build Coastguard Workerfrom common import RunCommandPropagateErr 22*3ac0a46fSAndroid Build Coastguard Workerfrom githelper import GitHelper 23*3ac0a46fSAndroid Build Coastguard Workerfrom safetynet_conclusions import ComparisonConclusions 24*3ac0a46fSAndroid Build Coastguard Workerfrom safetynet_conclusions import PrintConclusionsDictHumanReadable 25*3ac0a46fSAndroid Build Coastguard Workerfrom safetynet_conclusions import RATING_IMPROVEMENT 26*3ac0a46fSAndroid Build Coastguard Workerfrom safetynet_conclusions import RATING_REGRESSION 27*3ac0a46fSAndroid Build Coastguard Workerfrom safetynet_image import ImageComparison 28*3ac0a46fSAndroid Build Coastguard Worker 29*3ac0a46fSAndroid Build Coastguard Worker 30*3ac0a46fSAndroid Build Coastguard Workerdef RunSingleTestCaseParallel(this, run_label, build_dir, test_case): 31*3ac0a46fSAndroid Build Coastguard Worker result = this.RunSingleTestCase(run_label, build_dir, test_case) 32*3ac0a46fSAndroid Build Coastguard Worker return (test_case, result) 33*3ac0a46fSAndroid Build Coastguard Worker 34*3ac0a46fSAndroid Build Coastguard Worker 35*3ac0a46fSAndroid Build Coastguard Workerclass CompareRun: 36*3ac0a46fSAndroid Build Coastguard Worker """A comparison between two branches of pdfium.""" 37*3ac0a46fSAndroid Build Coastguard Worker 38*3ac0a46fSAndroid Build Coastguard Worker def __init__(self, args): 39*3ac0a46fSAndroid Build Coastguard Worker self.git = GitHelper() 40*3ac0a46fSAndroid Build Coastguard Worker self.args = args 41*3ac0a46fSAndroid Build Coastguard Worker self._InitPaths() 42*3ac0a46fSAndroid Build Coastguard Worker 43*3ac0a46fSAndroid Build Coastguard Worker def _InitPaths(self): 44*3ac0a46fSAndroid Build Coastguard Worker if self.args.this_repo: 45*3ac0a46fSAndroid Build Coastguard Worker self.safe_script_dir = self.args.build_dir 46*3ac0a46fSAndroid Build Coastguard Worker else: 47*3ac0a46fSAndroid Build Coastguard Worker self.safe_script_dir = os.path.join('testing', 'tools') 48*3ac0a46fSAndroid Build Coastguard Worker 49*3ac0a46fSAndroid Build Coastguard Worker self.safe_measure_script_path = os.path.abspath( 50*3ac0a46fSAndroid Build Coastguard Worker os.path.join(self.safe_script_dir, 'safetynet_measure.py')) 51*3ac0a46fSAndroid Build Coastguard Worker 52*3ac0a46fSAndroid Build Coastguard Worker input_file_re = re.compile('^.+[.]pdf$') 53*3ac0a46fSAndroid Build Coastguard Worker self.test_cases = [] 54*3ac0a46fSAndroid Build Coastguard Worker for input_path in self.args.input_paths: 55*3ac0a46fSAndroid Build Coastguard Worker if os.path.isfile(input_path): 56*3ac0a46fSAndroid Build Coastguard Worker self.test_cases.append(input_path) 57*3ac0a46fSAndroid Build Coastguard Worker elif os.path.isdir(input_path): 58*3ac0a46fSAndroid Build Coastguard Worker for file_dir, _, filename_list in os.walk(input_path): 59*3ac0a46fSAndroid Build Coastguard Worker for input_filename in filename_list: 60*3ac0a46fSAndroid Build Coastguard Worker if input_file_re.match(input_filename): 61*3ac0a46fSAndroid Build Coastguard Worker file_path = os.path.join(file_dir, input_filename) 62*3ac0a46fSAndroid Build Coastguard Worker if os.path.isfile(file_path): 63*3ac0a46fSAndroid Build Coastguard Worker self.test_cases.append(file_path) 64*3ac0a46fSAndroid Build Coastguard Worker 65*3ac0a46fSAndroid Build Coastguard Worker self.after_build_dir = self.args.build_dir 66*3ac0a46fSAndroid Build Coastguard Worker if self.args.build_dir_before: 67*3ac0a46fSAndroid Build Coastguard Worker self.before_build_dir = self.args.build_dir_before 68*3ac0a46fSAndroid Build Coastguard Worker else: 69*3ac0a46fSAndroid Build Coastguard Worker self.before_build_dir = self.after_build_dir 70*3ac0a46fSAndroid Build Coastguard Worker 71*3ac0a46fSAndroid Build Coastguard Worker def Run(self): 72*3ac0a46fSAndroid Build Coastguard Worker """Runs comparison by checking out branches, building and measuring them. 73*3ac0a46fSAndroid Build Coastguard Worker 74*3ac0a46fSAndroid Build Coastguard Worker Returns: 75*3ac0a46fSAndroid Build Coastguard Worker Exit code for the script. 76*3ac0a46fSAndroid Build Coastguard Worker """ 77*3ac0a46fSAndroid Build Coastguard Worker if self.args.this_repo: 78*3ac0a46fSAndroid Build Coastguard Worker self._FreezeMeasureScript() 79*3ac0a46fSAndroid Build Coastguard Worker 80*3ac0a46fSAndroid Build Coastguard Worker if self.args.branch_after: 81*3ac0a46fSAndroid Build Coastguard Worker if self.args.this_repo: 82*3ac0a46fSAndroid Build Coastguard Worker before, after = self._ProfileTwoOtherBranchesInThisRepo( 83*3ac0a46fSAndroid Build Coastguard Worker self.args.branch_before, self.args.branch_after) 84*3ac0a46fSAndroid Build Coastguard Worker else: 85*3ac0a46fSAndroid Build Coastguard Worker before, after = self._ProfileTwoOtherBranches(self.args.branch_before, 86*3ac0a46fSAndroid Build Coastguard Worker self.args.branch_after) 87*3ac0a46fSAndroid Build Coastguard Worker elif self.args.branch_before: 88*3ac0a46fSAndroid Build Coastguard Worker if self.args.this_repo: 89*3ac0a46fSAndroid Build Coastguard Worker before, after = self._ProfileCurrentAndOtherBranchInThisRepo( 90*3ac0a46fSAndroid Build Coastguard Worker self.args.branch_before) 91*3ac0a46fSAndroid Build Coastguard Worker else: 92*3ac0a46fSAndroid Build Coastguard Worker before, after = self._ProfileCurrentAndOtherBranch( 93*3ac0a46fSAndroid Build Coastguard Worker self.args.branch_before) 94*3ac0a46fSAndroid Build Coastguard Worker else: 95*3ac0a46fSAndroid Build Coastguard Worker if self.args.this_repo: 96*3ac0a46fSAndroid Build Coastguard Worker before, after = self._ProfileLocalChangesAndCurrentBranchInThisRepo() 97*3ac0a46fSAndroid Build Coastguard Worker else: 98*3ac0a46fSAndroid Build Coastguard Worker before, after = self._ProfileLocalChangesAndCurrentBranch() 99*3ac0a46fSAndroid Build Coastguard Worker 100*3ac0a46fSAndroid Build Coastguard Worker conclusions = self._DrawConclusions(before, after) 101*3ac0a46fSAndroid Build Coastguard Worker conclusions_dict = conclusions.GetOutputDict() 102*3ac0a46fSAndroid Build Coastguard Worker conclusions_dict.setdefault('metadata', {})['profiler'] = self.args.profiler 103*3ac0a46fSAndroid Build Coastguard Worker 104*3ac0a46fSAndroid Build Coastguard Worker self._PrintConclusions(conclusions_dict) 105*3ac0a46fSAndroid Build Coastguard Worker 106*3ac0a46fSAndroid Build Coastguard Worker self._CleanUp(conclusions) 107*3ac0a46fSAndroid Build Coastguard Worker 108*3ac0a46fSAndroid Build Coastguard Worker if self.args.png_dir: 109*3ac0a46fSAndroid Build Coastguard Worker image_comparison = ImageComparison( 110*3ac0a46fSAndroid Build Coastguard Worker self.after_build_dir, self.args.png_dir, ('before', 'after'), 111*3ac0a46fSAndroid Build Coastguard Worker self.args.num_workers, self.args.png_threshold) 112*3ac0a46fSAndroid Build Coastguard Worker image_comparison.Run(open_in_browser=not self.args.machine_readable) 113*3ac0a46fSAndroid Build Coastguard Worker 114*3ac0a46fSAndroid Build Coastguard Worker return 0 115*3ac0a46fSAndroid Build Coastguard Worker 116*3ac0a46fSAndroid Build Coastguard Worker def _FreezeMeasureScript(self): 117*3ac0a46fSAndroid Build Coastguard Worker """Freezes a version of the measuring script. 118*3ac0a46fSAndroid Build Coastguard Worker 119*3ac0a46fSAndroid Build Coastguard Worker This is needed to make sure we are comparing the pdfium library changes and 120*3ac0a46fSAndroid Build Coastguard Worker not script changes that may happen between the two branches. 121*3ac0a46fSAndroid Build Coastguard Worker """ 122*3ac0a46fSAndroid Build Coastguard Worker self.__FreezeFile(os.path.join('testing', 'tools', 'safetynet_measure.py')) 123*3ac0a46fSAndroid Build Coastguard Worker self.__FreezeFile(os.path.join('testing', 'tools', 'common.py')) 124*3ac0a46fSAndroid Build Coastguard Worker 125*3ac0a46fSAndroid Build Coastguard Worker def __FreezeFile(self, filename): 126*3ac0a46fSAndroid Build Coastguard Worker RunCommandPropagateErr(['cp', filename, self.safe_script_dir], 127*3ac0a46fSAndroid Build Coastguard Worker exit_status_on_error=1) 128*3ac0a46fSAndroid Build Coastguard Worker 129*3ac0a46fSAndroid Build Coastguard Worker def _ProfileTwoOtherBranchesInThisRepo(self, before_branch, after_branch): 130*3ac0a46fSAndroid Build Coastguard Worker """Profiles two branches that are not the current branch. 131*3ac0a46fSAndroid Build Coastguard Worker 132*3ac0a46fSAndroid Build Coastguard Worker This is done in the local repository and changes may not be restored if the 133*3ac0a46fSAndroid Build Coastguard Worker script fails or is interrupted. 134*3ac0a46fSAndroid Build Coastguard Worker 135*3ac0a46fSAndroid Build Coastguard Worker after_branch does not need to descend from before_branch, they will be 136*3ac0a46fSAndroid Build Coastguard Worker measured the same way 137*3ac0a46fSAndroid Build Coastguard Worker 138*3ac0a46fSAndroid Build Coastguard Worker Args: 139*3ac0a46fSAndroid Build Coastguard Worker before_branch: One branch to profile. 140*3ac0a46fSAndroid Build Coastguard Worker after_branch: Other branch to profile. 141*3ac0a46fSAndroid Build Coastguard Worker 142*3ac0a46fSAndroid Build Coastguard Worker Returns: 143*3ac0a46fSAndroid Build Coastguard Worker A tuple (before, after), where each of before and after is a dict 144*3ac0a46fSAndroid Build Coastguard Worker mapping a test case name to the profiling values for that test case 145*3ac0a46fSAndroid Build Coastguard Worker in the given branch. 146*3ac0a46fSAndroid Build Coastguard Worker """ 147*3ac0a46fSAndroid Build Coastguard Worker branch_to_restore = self.git.GetCurrentBranchName() 148*3ac0a46fSAndroid Build Coastguard Worker 149*3ac0a46fSAndroid Build Coastguard Worker self._StashLocalChanges() 150*3ac0a46fSAndroid Build Coastguard Worker 151*3ac0a46fSAndroid Build Coastguard Worker self._CheckoutBranch(after_branch) 152*3ac0a46fSAndroid Build Coastguard Worker self._BuildCurrentBranch(self.after_build_dir) 153*3ac0a46fSAndroid Build Coastguard Worker after = self._MeasureCurrentBranch('after', self.after_build_dir) 154*3ac0a46fSAndroid Build Coastguard Worker 155*3ac0a46fSAndroid Build Coastguard Worker self._CheckoutBranch(before_branch) 156*3ac0a46fSAndroid Build Coastguard Worker self._BuildCurrentBranch(self.before_build_dir) 157*3ac0a46fSAndroid Build Coastguard Worker before = self._MeasureCurrentBranch('before', self.before_build_dir) 158*3ac0a46fSAndroid Build Coastguard Worker 159*3ac0a46fSAndroid Build Coastguard Worker self._CheckoutBranch(branch_to_restore) 160*3ac0a46fSAndroid Build Coastguard Worker self._RestoreLocalChanges() 161*3ac0a46fSAndroid Build Coastguard Worker 162*3ac0a46fSAndroid Build Coastguard Worker return before, after 163*3ac0a46fSAndroid Build Coastguard Worker 164*3ac0a46fSAndroid Build Coastguard Worker def _ProfileTwoOtherBranches(self, before_branch, after_branch): 165*3ac0a46fSAndroid Build Coastguard Worker """Profiles two branches that are not the current branch. 166*3ac0a46fSAndroid Build Coastguard Worker 167*3ac0a46fSAndroid Build Coastguard Worker This is done in new, cloned repositories, therefore it is safer but slower 168*3ac0a46fSAndroid Build Coastguard Worker and requires downloads. 169*3ac0a46fSAndroid Build Coastguard Worker 170*3ac0a46fSAndroid Build Coastguard Worker after_branch does not need to descend from before_branch, they will be 171*3ac0a46fSAndroid Build Coastguard Worker measured the same way 172*3ac0a46fSAndroid Build Coastguard Worker 173*3ac0a46fSAndroid Build Coastguard Worker Args: 174*3ac0a46fSAndroid Build Coastguard Worker before_branch: One branch to profile. 175*3ac0a46fSAndroid Build Coastguard Worker after_branch: Other branch to profile. 176*3ac0a46fSAndroid Build Coastguard Worker 177*3ac0a46fSAndroid Build Coastguard Worker Returns: 178*3ac0a46fSAndroid Build Coastguard Worker A tuple (before, after), where each of before and after is a dict 179*3ac0a46fSAndroid Build Coastguard Worker mapping a test case name to the profiling values for that test case 180*3ac0a46fSAndroid Build Coastguard Worker in the given branch. 181*3ac0a46fSAndroid Build Coastguard Worker """ 182*3ac0a46fSAndroid Build Coastguard Worker after = self._ProfileSeparateRepo('after', self.after_build_dir, 183*3ac0a46fSAndroid Build Coastguard Worker after_branch) 184*3ac0a46fSAndroid Build Coastguard Worker before = self._ProfileSeparateRepo('before', self.before_build_dir, 185*3ac0a46fSAndroid Build Coastguard Worker before_branch) 186*3ac0a46fSAndroid Build Coastguard Worker return before, after 187*3ac0a46fSAndroid Build Coastguard Worker 188*3ac0a46fSAndroid Build Coastguard Worker def _ProfileCurrentAndOtherBranchInThisRepo(self, other_branch): 189*3ac0a46fSAndroid Build Coastguard Worker """Profiles the current branch (with uncommitted changes) and another one. 190*3ac0a46fSAndroid Build Coastguard Worker 191*3ac0a46fSAndroid Build Coastguard Worker This is done in the local repository and changes may not be restored if the 192*3ac0a46fSAndroid Build Coastguard Worker script fails or is interrupted. 193*3ac0a46fSAndroid Build Coastguard Worker 194*3ac0a46fSAndroid Build Coastguard Worker The current branch does not need to descend from other_branch. 195*3ac0a46fSAndroid Build Coastguard Worker 196*3ac0a46fSAndroid Build Coastguard Worker Args: 197*3ac0a46fSAndroid Build Coastguard Worker other_branch: Other branch to profile that is not the current. 198*3ac0a46fSAndroid Build Coastguard Worker 199*3ac0a46fSAndroid Build Coastguard Worker Returns: 200*3ac0a46fSAndroid Build Coastguard Worker A tuple (before, after), where each of before and after is a dict 201*3ac0a46fSAndroid Build Coastguard Worker mapping a test case name to the profiling values for that test case 202*3ac0a46fSAndroid Build Coastguard Worker in the given branch. The current branch is considered to be "after" and 203*3ac0a46fSAndroid Build Coastguard Worker the other branch is considered to be "before". 204*3ac0a46fSAndroid Build Coastguard Worker """ 205*3ac0a46fSAndroid Build Coastguard Worker branch_to_restore = self.git.GetCurrentBranchName() 206*3ac0a46fSAndroid Build Coastguard Worker 207*3ac0a46fSAndroid Build Coastguard Worker self._BuildCurrentBranch(self.after_build_dir) 208*3ac0a46fSAndroid Build Coastguard Worker after = self._MeasureCurrentBranch('after', self.after_build_dir) 209*3ac0a46fSAndroid Build Coastguard Worker 210*3ac0a46fSAndroid Build Coastguard Worker self._StashLocalChanges() 211*3ac0a46fSAndroid Build Coastguard Worker 212*3ac0a46fSAndroid Build Coastguard Worker self._CheckoutBranch(other_branch) 213*3ac0a46fSAndroid Build Coastguard Worker self._BuildCurrentBranch(self.before_build_dir) 214*3ac0a46fSAndroid Build Coastguard Worker before = self._MeasureCurrentBranch('before', self.before_build_dir) 215*3ac0a46fSAndroid Build Coastguard Worker 216*3ac0a46fSAndroid Build Coastguard Worker self._CheckoutBranch(branch_to_restore) 217*3ac0a46fSAndroid Build Coastguard Worker self._RestoreLocalChanges() 218*3ac0a46fSAndroid Build Coastguard Worker 219*3ac0a46fSAndroid Build Coastguard Worker return before, after 220*3ac0a46fSAndroid Build Coastguard Worker 221*3ac0a46fSAndroid Build Coastguard Worker def _ProfileCurrentAndOtherBranch(self, other_branch): 222*3ac0a46fSAndroid Build Coastguard Worker """Profiles the current branch (with uncommitted changes) and another one. 223*3ac0a46fSAndroid Build Coastguard Worker 224*3ac0a46fSAndroid Build Coastguard Worker This is done in new, cloned repositories, therefore it is safer but slower 225*3ac0a46fSAndroid Build Coastguard Worker and requires downloads. 226*3ac0a46fSAndroid Build Coastguard Worker 227*3ac0a46fSAndroid Build Coastguard Worker The current branch does not need to descend from other_branch. 228*3ac0a46fSAndroid Build Coastguard Worker 229*3ac0a46fSAndroid Build Coastguard Worker Args: 230*3ac0a46fSAndroid Build Coastguard Worker other_branch: Other branch to profile that is not the current. None will 231*3ac0a46fSAndroid Build Coastguard Worker compare to the same branch. 232*3ac0a46fSAndroid Build Coastguard Worker 233*3ac0a46fSAndroid Build Coastguard Worker Returns: 234*3ac0a46fSAndroid Build Coastguard Worker A tuple (before, after), where each of before and after is a dict 235*3ac0a46fSAndroid Build Coastguard Worker mapping a test case name to the profiling values for that test case 236*3ac0a46fSAndroid Build Coastguard Worker in the given branch. The current branch is considered to be "after" and 237*3ac0a46fSAndroid Build Coastguard Worker the other branch is considered to be "before". 238*3ac0a46fSAndroid Build Coastguard Worker """ 239*3ac0a46fSAndroid Build Coastguard Worker self._BuildCurrentBranch(self.after_build_dir) 240*3ac0a46fSAndroid Build Coastguard Worker after = self._MeasureCurrentBranch('after', self.after_build_dir) 241*3ac0a46fSAndroid Build Coastguard Worker 242*3ac0a46fSAndroid Build Coastguard Worker before = self._ProfileSeparateRepo('before', self.before_build_dir, 243*3ac0a46fSAndroid Build Coastguard Worker other_branch) 244*3ac0a46fSAndroid Build Coastguard Worker 245*3ac0a46fSAndroid Build Coastguard Worker return before, after 246*3ac0a46fSAndroid Build Coastguard Worker 247*3ac0a46fSAndroid Build Coastguard Worker def _ProfileLocalChangesAndCurrentBranchInThisRepo(self): 248*3ac0a46fSAndroid Build Coastguard Worker """Profiles the current branch with and without uncommitted changes. 249*3ac0a46fSAndroid Build Coastguard Worker 250*3ac0a46fSAndroid Build Coastguard Worker This is done in the local repository and changes may not be restored if the 251*3ac0a46fSAndroid Build Coastguard Worker script fails or is interrupted. 252*3ac0a46fSAndroid Build Coastguard Worker 253*3ac0a46fSAndroid Build Coastguard Worker Returns: 254*3ac0a46fSAndroid Build Coastguard Worker A tuple (before, after), where each of before and after is a dict 255*3ac0a46fSAndroid Build Coastguard Worker mapping a test case name to the profiling values for that test case 256*3ac0a46fSAndroid Build Coastguard Worker using the given version. The current branch without uncommitted changes is 257*3ac0a46fSAndroid Build Coastguard Worker considered to be "before" and with uncommitted changes is considered to be 258*3ac0a46fSAndroid Build Coastguard Worker "after". 259*3ac0a46fSAndroid Build Coastguard Worker """ 260*3ac0a46fSAndroid Build Coastguard Worker self._BuildCurrentBranch(self.after_build_dir) 261*3ac0a46fSAndroid Build Coastguard Worker after = self._MeasureCurrentBranch('after', self.after_build_dir) 262*3ac0a46fSAndroid Build Coastguard Worker 263*3ac0a46fSAndroid Build Coastguard Worker pushed = self._StashLocalChanges() 264*3ac0a46fSAndroid Build Coastguard Worker if not pushed and not self.args.build_dir_before: 265*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Warning: No local changes to compare') 266*3ac0a46fSAndroid Build Coastguard Worker 267*3ac0a46fSAndroid Build Coastguard Worker before_build_dir = self.before_build_dir 268*3ac0a46fSAndroid Build Coastguard Worker 269*3ac0a46fSAndroid Build Coastguard Worker self._BuildCurrentBranch(before_build_dir) 270*3ac0a46fSAndroid Build Coastguard Worker before = self._MeasureCurrentBranch('before', before_build_dir) 271*3ac0a46fSAndroid Build Coastguard Worker 272*3ac0a46fSAndroid Build Coastguard Worker self._RestoreLocalChanges() 273*3ac0a46fSAndroid Build Coastguard Worker 274*3ac0a46fSAndroid Build Coastguard Worker return before, after 275*3ac0a46fSAndroid Build Coastguard Worker 276*3ac0a46fSAndroid Build Coastguard Worker def _ProfileLocalChangesAndCurrentBranch(self): 277*3ac0a46fSAndroid Build Coastguard Worker """Profiles the current branch with and without uncommitted changes. 278*3ac0a46fSAndroid Build Coastguard Worker 279*3ac0a46fSAndroid Build Coastguard Worker This is done in new, cloned repositories, therefore it is safer but slower 280*3ac0a46fSAndroid Build Coastguard Worker and requires downloads. 281*3ac0a46fSAndroid Build Coastguard Worker 282*3ac0a46fSAndroid Build Coastguard Worker Returns: 283*3ac0a46fSAndroid Build Coastguard Worker A tuple (before, after), where each of before and after is a dict 284*3ac0a46fSAndroid Build Coastguard Worker mapping a test case name to the profiling values for that test case 285*3ac0a46fSAndroid Build Coastguard Worker using the given version. The current branch without uncommitted changes is 286*3ac0a46fSAndroid Build Coastguard Worker considered to be "before" and with uncommitted changes is considered to be 287*3ac0a46fSAndroid Build Coastguard Worker "after". 288*3ac0a46fSAndroid Build Coastguard Worker """ 289*3ac0a46fSAndroid Build Coastguard Worker return self._ProfileCurrentAndOtherBranch(other_branch=None) 290*3ac0a46fSAndroid Build Coastguard Worker 291*3ac0a46fSAndroid Build Coastguard Worker def _ProfileSeparateRepo(self, run_label, relative_build_dir, branch): 292*3ac0a46fSAndroid Build Coastguard Worker """Profiles a branch in a a temporary git repository. 293*3ac0a46fSAndroid Build Coastguard Worker 294*3ac0a46fSAndroid Build Coastguard Worker Args: 295*3ac0a46fSAndroid Build Coastguard Worker run_label: String to differentiate this version of the code in output 296*3ac0a46fSAndroid Build Coastguard Worker files from other versions. 297*3ac0a46fSAndroid Build Coastguard Worker relative_build_dir: Path to the build dir in the current working dir to 298*3ac0a46fSAndroid Build Coastguard Worker clone build args from. 299*3ac0a46fSAndroid Build Coastguard Worker branch: Branch to checkout in the new repository. None will 300*3ac0a46fSAndroid Build Coastguard Worker profile the same branch checked out in the original repo. 301*3ac0a46fSAndroid Build Coastguard Worker Returns: 302*3ac0a46fSAndroid Build Coastguard Worker A dict mapping each test case name to the profiling values for that 303*3ac0a46fSAndroid Build Coastguard Worker test case. 304*3ac0a46fSAndroid Build Coastguard Worker """ 305*3ac0a46fSAndroid Build Coastguard Worker build_dir = self._CreateTempRepo('repo_%s' % run_label, relative_build_dir, 306*3ac0a46fSAndroid Build Coastguard Worker branch) 307*3ac0a46fSAndroid Build Coastguard Worker 308*3ac0a46fSAndroid Build Coastguard Worker self._BuildCurrentBranch(build_dir) 309*3ac0a46fSAndroid Build Coastguard Worker return self._MeasureCurrentBranch(run_label, build_dir) 310*3ac0a46fSAndroid Build Coastguard Worker 311*3ac0a46fSAndroid Build Coastguard Worker def _CreateTempRepo(self, dir_name, relative_build_dir, branch): 312*3ac0a46fSAndroid Build Coastguard Worker """Clones a temporary git repository out of the current working dir. 313*3ac0a46fSAndroid Build Coastguard Worker 314*3ac0a46fSAndroid Build Coastguard Worker Args: 315*3ac0a46fSAndroid Build Coastguard Worker dir_name: Name for the temporary repository directory 316*3ac0a46fSAndroid Build Coastguard Worker relative_build_dir: Path to the build dir in the current working dir to 317*3ac0a46fSAndroid Build Coastguard Worker clone build args from. 318*3ac0a46fSAndroid Build Coastguard Worker branch: Branch to checkout in the new repository. None will keep checked 319*3ac0a46fSAndroid Build Coastguard Worker out the same branch as the local repo. 320*3ac0a46fSAndroid Build Coastguard Worker Returns: 321*3ac0a46fSAndroid Build Coastguard Worker Path to the build directory of the new repository. 322*3ac0a46fSAndroid Build Coastguard Worker """ 323*3ac0a46fSAndroid Build Coastguard Worker cwd = os.getcwd() 324*3ac0a46fSAndroid Build Coastguard Worker 325*3ac0a46fSAndroid Build Coastguard Worker repo_dir = tempfile.mkdtemp(suffix='-%s' % dir_name) 326*3ac0a46fSAndroid Build Coastguard Worker src_dir = os.path.join(repo_dir, 'pdfium') 327*3ac0a46fSAndroid Build Coastguard Worker 328*3ac0a46fSAndroid Build Coastguard Worker self.git.CloneLocal(os.getcwd(), src_dir) 329*3ac0a46fSAndroid Build Coastguard Worker 330*3ac0a46fSAndroid Build Coastguard Worker if branch is not None: 331*3ac0a46fSAndroid Build Coastguard Worker os.chdir(src_dir) 332*3ac0a46fSAndroid Build Coastguard Worker self.git.Checkout(branch) 333*3ac0a46fSAndroid Build Coastguard Worker 334*3ac0a46fSAndroid Build Coastguard Worker os.chdir(repo_dir) 335*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Syncing...') 336*3ac0a46fSAndroid Build Coastguard Worker 337*3ac0a46fSAndroid Build Coastguard Worker cmd = [ 338*3ac0a46fSAndroid Build Coastguard Worker 'gclient', 'config', '--unmanaged', 339*3ac0a46fSAndroid Build Coastguard Worker 'https://pdfium.googlesource.com/pdfium.git' 340*3ac0a46fSAndroid Build Coastguard Worker ] 341*3ac0a46fSAndroid Build Coastguard Worker if self.args.cache_dir: 342*3ac0a46fSAndroid Build Coastguard Worker cmd.append('--cache-dir=%s' % self.args.cache_dir) 343*3ac0a46fSAndroid Build Coastguard Worker RunCommandPropagateErr(cmd, exit_status_on_error=1) 344*3ac0a46fSAndroid Build Coastguard Worker 345*3ac0a46fSAndroid Build Coastguard Worker RunCommandPropagateErr(['gclient', 'sync', '--force'], 346*3ac0a46fSAndroid Build Coastguard Worker exit_status_on_error=1) 347*3ac0a46fSAndroid Build Coastguard Worker 348*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Done.') 349*3ac0a46fSAndroid Build Coastguard Worker 350*3ac0a46fSAndroid Build Coastguard Worker build_dir = os.path.join(src_dir, relative_build_dir) 351*3ac0a46fSAndroid Build Coastguard Worker os.makedirs(build_dir) 352*3ac0a46fSAndroid Build Coastguard Worker os.chdir(src_dir) 353*3ac0a46fSAndroid Build Coastguard Worker 354*3ac0a46fSAndroid Build Coastguard Worker source_gn_args = os.path.join(cwd, relative_build_dir, 'args.gn') 355*3ac0a46fSAndroid Build Coastguard Worker dest_gn_args = os.path.join(build_dir, 'args.gn') 356*3ac0a46fSAndroid Build Coastguard Worker shutil.copy(source_gn_args, dest_gn_args) 357*3ac0a46fSAndroid Build Coastguard Worker 358*3ac0a46fSAndroid Build Coastguard Worker RunCommandPropagateErr(['gn', 'gen', relative_build_dir], 359*3ac0a46fSAndroid Build Coastguard Worker exit_status_on_error=1) 360*3ac0a46fSAndroid Build Coastguard Worker 361*3ac0a46fSAndroid Build Coastguard Worker os.chdir(cwd) 362*3ac0a46fSAndroid Build Coastguard Worker 363*3ac0a46fSAndroid Build Coastguard Worker return build_dir 364*3ac0a46fSAndroid Build Coastguard Worker 365*3ac0a46fSAndroid Build Coastguard Worker def _CheckoutBranch(self, branch): 366*3ac0a46fSAndroid Build Coastguard Worker PrintErr("Checking out branch '%s'" % branch) 367*3ac0a46fSAndroid Build Coastguard Worker self.git.Checkout(branch) 368*3ac0a46fSAndroid Build Coastguard Worker 369*3ac0a46fSAndroid Build Coastguard Worker def _StashLocalChanges(self): 370*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Stashing local changes') 371*3ac0a46fSAndroid Build Coastguard Worker return self.git.StashPush() 372*3ac0a46fSAndroid Build Coastguard Worker 373*3ac0a46fSAndroid Build Coastguard Worker def _RestoreLocalChanges(self): 374*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Restoring local changes') 375*3ac0a46fSAndroid Build Coastguard Worker self.git.StashPopAll() 376*3ac0a46fSAndroid Build Coastguard Worker 377*3ac0a46fSAndroid Build Coastguard Worker def _BuildCurrentBranch(self, build_dir): 378*3ac0a46fSAndroid Build Coastguard Worker """Synchronizes and builds the current version of pdfium. 379*3ac0a46fSAndroid Build Coastguard Worker 380*3ac0a46fSAndroid Build Coastguard Worker Args: 381*3ac0a46fSAndroid Build Coastguard Worker build_dir: String with path to build directory 382*3ac0a46fSAndroid Build Coastguard Worker """ 383*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Syncing...') 384*3ac0a46fSAndroid Build Coastguard Worker RunCommandPropagateErr(['gclient', 'sync', '--force'], 385*3ac0a46fSAndroid Build Coastguard Worker exit_status_on_error=1) 386*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Done.') 387*3ac0a46fSAndroid Build Coastguard Worker 388*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Building...') 389*3ac0a46fSAndroid Build Coastguard Worker cmd = ['ninja', '-C', build_dir, 'pdfium_test'] 390*3ac0a46fSAndroid Build Coastguard Worker if GetBooleanGnArg('use_goma', build_dir): 391*3ac0a46fSAndroid Build Coastguard Worker cmd.extend(['-j', '250']) 392*3ac0a46fSAndroid Build Coastguard Worker RunCommandPropagateErr(cmd, stdout_has_errors=True, exit_status_on_error=1) 393*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Done.') 394*3ac0a46fSAndroid Build Coastguard Worker 395*3ac0a46fSAndroid Build Coastguard Worker def _MeasureCurrentBranch(self, run_label, build_dir): 396*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Measuring...') 397*3ac0a46fSAndroid Build Coastguard Worker if self.args.num_workers > 1 and len(self.test_cases) > 1: 398*3ac0a46fSAndroid Build Coastguard Worker results = self._RunAsync(run_label, build_dir) 399*3ac0a46fSAndroid Build Coastguard Worker else: 400*3ac0a46fSAndroid Build Coastguard Worker results = self._RunSync(run_label, build_dir) 401*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Done.') 402*3ac0a46fSAndroid Build Coastguard Worker 403*3ac0a46fSAndroid Build Coastguard Worker return results 404*3ac0a46fSAndroid Build Coastguard Worker 405*3ac0a46fSAndroid Build Coastguard Worker def _RunSync(self, run_label, build_dir): 406*3ac0a46fSAndroid Build Coastguard Worker """Profiles the test cases synchronously. 407*3ac0a46fSAndroid Build Coastguard Worker 408*3ac0a46fSAndroid Build Coastguard Worker Args: 409*3ac0a46fSAndroid Build Coastguard Worker run_label: String to differentiate this version of the code in output 410*3ac0a46fSAndroid Build Coastguard Worker files from other versions. 411*3ac0a46fSAndroid Build Coastguard Worker build_dir: String with path to build directory 412*3ac0a46fSAndroid Build Coastguard Worker 413*3ac0a46fSAndroid Build Coastguard Worker Returns: 414*3ac0a46fSAndroid Build Coastguard Worker A dict mapping each test case name to the profiling values for that 415*3ac0a46fSAndroid Build Coastguard Worker test case. 416*3ac0a46fSAndroid Build Coastguard Worker """ 417*3ac0a46fSAndroid Build Coastguard Worker results = {} 418*3ac0a46fSAndroid Build Coastguard Worker 419*3ac0a46fSAndroid Build Coastguard Worker for test_case in self.test_cases: 420*3ac0a46fSAndroid Build Coastguard Worker result = self.RunSingleTestCase(run_label, build_dir, test_case) 421*3ac0a46fSAndroid Build Coastguard Worker if result is not None: 422*3ac0a46fSAndroid Build Coastguard Worker results[test_case] = result 423*3ac0a46fSAndroid Build Coastguard Worker 424*3ac0a46fSAndroid Build Coastguard Worker return results 425*3ac0a46fSAndroid Build Coastguard Worker 426*3ac0a46fSAndroid Build Coastguard Worker def _RunAsync(self, run_label, build_dir): 427*3ac0a46fSAndroid Build Coastguard Worker """Profiles the test cases asynchronously. 428*3ac0a46fSAndroid Build Coastguard Worker 429*3ac0a46fSAndroid Build Coastguard Worker Uses as many workers as configured by --num-workers. 430*3ac0a46fSAndroid Build Coastguard Worker 431*3ac0a46fSAndroid Build Coastguard Worker Args: 432*3ac0a46fSAndroid Build Coastguard Worker run_label: String to differentiate this version of the code in output 433*3ac0a46fSAndroid Build Coastguard Worker files from other versions. 434*3ac0a46fSAndroid Build Coastguard Worker build_dir: String with path to build directory 435*3ac0a46fSAndroid Build Coastguard Worker 436*3ac0a46fSAndroid Build Coastguard Worker Returns: 437*3ac0a46fSAndroid Build Coastguard Worker A dict mapping each test case name to the profiling values for that 438*3ac0a46fSAndroid Build Coastguard Worker test case. 439*3ac0a46fSAndroid Build Coastguard Worker """ 440*3ac0a46fSAndroid Build Coastguard Worker results = {} 441*3ac0a46fSAndroid Build Coastguard Worker pool = multiprocessing.Pool(self.args.num_workers) 442*3ac0a46fSAndroid Build Coastguard Worker worker_func = functools.partial(RunSingleTestCaseParallel, self, run_label, 443*3ac0a46fSAndroid Build Coastguard Worker build_dir) 444*3ac0a46fSAndroid Build Coastguard Worker 445*3ac0a46fSAndroid Build Coastguard Worker try: 446*3ac0a46fSAndroid Build Coastguard Worker # The timeout is a workaround for http://bugs.python.org/issue8296 447*3ac0a46fSAndroid Build Coastguard Worker # which prevents KeyboardInterrupt from working. 448*3ac0a46fSAndroid Build Coastguard Worker one_year_in_seconds = 3600 * 24 * 365 449*3ac0a46fSAndroid Build Coastguard Worker worker_results = ( 450*3ac0a46fSAndroid Build Coastguard Worker pool.map_async(worker_func, self.test_cases).get(one_year_in_seconds)) 451*3ac0a46fSAndroid Build Coastguard Worker for worker_result in worker_results: 452*3ac0a46fSAndroid Build Coastguard Worker test_case, result = worker_result 453*3ac0a46fSAndroid Build Coastguard Worker if result is not None: 454*3ac0a46fSAndroid Build Coastguard Worker results[test_case] = result 455*3ac0a46fSAndroid Build Coastguard Worker except KeyboardInterrupt: 456*3ac0a46fSAndroid Build Coastguard Worker pool.terminate() 457*3ac0a46fSAndroid Build Coastguard Worker sys.exit(1) 458*3ac0a46fSAndroid Build Coastguard Worker else: 459*3ac0a46fSAndroid Build Coastguard Worker pool.close() 460*3ac0a46fSAndroid Build Coastguard Worker 461*3ac0a46fSAndroid Build Coastguard Worker pool.join() 462*3ac0a46fSAndroid Build Coastguard Worker 463*3ac0a46fSAndroid Build Coastguard Worker return results 464*3ac0a46fSAndroid Build Coastguard Worker 465*3ac0a46fSAndroid Build Coastguard Worker def RunSingleTestCase(self, run_label, build_dir, test_case): 466*3ac0a46fSAndroid Build Coastguard Worker """Profiles a single test case. 467*3ac0a46fSAndroid Build Coastguard Worker 468*3ac0a46fSAndroid Build Coastguard Worker Args: 469*3ac0a46fSAndroid Build Coastguard Worker run_label: String to differentiate this version of the code in output 470*3ac0a46fSAndroid Build Coastguard Worker files from other versions. 471*3ac0a46fSAndroid Build Coastguard Worker build_dir: String with path to build directory 472*3ac0a46fSAndroid Build Coastguard Worker test_case: Path to the test case. 473*3ac0a46fSAndroid Build Coastguard Worker 474*3ac0a46fSAndroid Build Coastguard Worker Returns: 475*3ac0a46fSAndroid Build Coastguard Worker The measured profiling value for that test case. 476*3ac0a46fSAndroid Build Coastguard Worker """ 477*3ac0a46fSAndroid Build Coastguard Worker command = [ 478*3ac0a46fSAndroid Build Coastguard Worker self.safe_measure_script_path, test_case, 479*3ac0a46fSAndroid Build Coastguard Worker '--build-dir=%s' % build_dir 480*3ac0a46fSAndroid Build Coastguard Worker ] 481*3ac0a46fSAndroid Build Coastguard Worker 482*3ac0a46fSAndroid Build Coastguard Worker if self.args.interesting_section: 483*3ac0a46fSAndroid Build Coastguard Worker command.append('--interesting-section') 484*3ac0a46fSAndroid Build Coastguard Worker 485*3ac0a46fSAndroid Build Coastguard Worker if self.args.profiler: 486*3ac0a46fSAndroid Build Coastguard Worker command.append('--profiler=%s' % self.args.profiler) 487*3ac0a46fSAndroid Build Coastguard Worker 488*3ac0a46fSAndroid Build Coastguard Worker profile_file_path = self._GetProfileFilePath(run_label, test_case) 489*3ac0a46fSAndroid Build Coastguard Worker if profile_file_path: 490*3ac0a46fSAndroid Build Coastguard Worker command.append('--output-path=%s' % profile_file_path) 491*3ac0a46fSAndroid Build Coastguard Worker 492*3ac0a46fSAndroid Build Coastguard Worker if self.args.png_dir: 493*3ac0a46fSAndroid Build Coastguard Worker command.append('--png') 494*3ac0a46fSAndroid Build Coastguard Worker 495*3ac0a46fSAndroid Build Coastguard Worker if self.args.pages: 496*3ac0a46fSAndroid Build Coastguard Worker command.extend(['--pages', self.args.pages]) 497*3ac0a46fSAndroid Build Coastguard Worker 498*3ac0a46fSAndroid Build Coastguard Worker output = RunCommandPropagateErr(command) 499*3ac0a46fSAndroid Build Coastguard Worker 500*3ac0a46fSAndroid Build Coastguard Worker if output is None: 501*3ac0a46fSAndroid Build Coastguard Worker return None 502*3ac0a46fSAndroid Build Coastguard Worker 503*3ac0a46fSAndroid Build Coastguard Worker if self.args.png_dir: 504*3ac0a46fSAndroid Build Coastguard Worker self._MoveImages(test_case, run_label) 505*3ac0a46fSAndroid Build Coastguard Worker 506*3ac0a46fSAndroid Build Coastguard Worker # Get the time number as output, making sure it's just a number 507*3ac0a46fSAndroid Build Coastguard Worker output = output.strip() 508*3ac0a46fSAndroid Build Coastguard Worker if re.match('^[0-9]+$', output): 509*3ac0a46fSAndroid Build Coastguard Worker return int(output) 510*3ac0a46fSAndroid Build Coastguard Worker 511*3ac0a46fSAndroid Build Coastguard Worker return None 512*3ac0a46fSAndroid Build Coastguard Worker 513*3ac0a46fSAndroid Build Coastguard Worker def _MoveImages(self, test_case, run_label): 514*3ac0a46fSAndroid Build Coastguard Worker png_dir = os.path.join(self.args.png_dir, run_label) 515*3ac0a46fSAndroid Build Coastguard Worker if not os.path.exists(png_dir): 516*3ac0a46fSAndroid Build Coastguard Worker os.makedirs(png_dir) 517*3ac0a46fSAndroid Build Coastguard Worker 518*3ac0a46fSAndroid Build Coastguard Worker test_case_dir, test_case_filename = os.path.split(test_case) 519*3ac0a46fSAndroid Build Coastguard Worker test_case_png_matcher = '%s.*.png' % test_case_filename 520*3ac0a46fSAndroid Build Coastguard Worker for output_png in glob.glob( 521*3ac0a46fSAndroid Build Coastguard Worker os.path.join(test_case_dir, test_case_png_matcher)): 522*3ac0a46fSAndroid Build Coastguard Worker shutil.move(output_png, png_dir) 523*3ac0a46fSAndroid Build Coastguard Worker 524*3ac0a46fSAndroid Build Coastguard Worker def _GetProfileFilePath(self, run_label, test_case): 525*3ac0a46fSAndroid Build Coastguard Worker if self.args.output_dir: 526*3ac0a46fSAndroid Build Coastguard Worker output_filename = ( 527*3ac0a46fSAndroid Build Coastguard Worker 'callgrind.out.%s.%s' % (test_case.replace('/', '_'), run_label)) 528*3ac0a46fSAndroid Build Coastguard Worker return os.path.join(self.args.output_dir, output_filename) 529*3ac0a46fSAndroid Build Coastguard Worker return None 530*3ac0a46fSAndroid Build Coastguard Worker 531*3ac0a46fSAndroid Build Coastguard Worker def _DrawConclusions(self, times_before_branch, times_after_branch): 532*3ac0a46fSAndroid Build Coastguard Worker """Draws conclusions comparing results of test runs in two branches. 533*3ac0a46fSAndroid Build Coastguard Worker 534*3ac0a46fSAndroid Build Coastguard Worker Args: 535*3ac0a46fSAndroid Build Coastguard Worker times_before_branch: A dict mapping each test case name to the 536*3ac0a46fSAndroid Build Coastguard Worker profiling values for that test case in the branch to be considered 537*3ac0a46fSAndroid Build Coastguard Worker as the baseline. 538*3ac0a46fSAndroid Build Coastguard Worker times_after_branch: A dict mapping each test case name to the 539*3ac0a46fSAndroid Build Coastguard Worker profiling values for that test case in the branch to be considered 540*3ac0a46fSAndroid Build Coastguard Worker as the new version. 541*3ac0a46fSAndroid Build Coastguard Worker 542*3ac0a46fSAndroid Build Coastguard Worker Returns: 543*3ac0a46fSAndroid Build Coastguard Worker ComparisonConclusions with all test cases processed. 544*3ac0a46fSAndroid Build Coastguard Worker """ 545*3ac0a46fSAndroid Build Coastguard Worker conclusions = ComparisonConclusions(self.args.threshold_significant) 546*3ac0a46fSAndroid Build Coastguard Worker 547*3ac0a46fSAndroid Build Coastguard Worker for test_case in sorted(self.test_cases): 548*3ac0a46fSAndroid Build Coastguard Worker before = times_before_branch.get(test_case) 549*3ac0a46fSAndroid Build Coastguard Worker after = times_after_branch.get(test_case) 550*3ac0a46fSAndroid Build Coastguard Worker conclusions.ProcessCase(test_case, before, after) 551*3ac0a46fSAndroid Build Coastguard Worker 552*3ac0a46fSAndroid Build Coastguard Worker return conclusions 553*3ac0a46fSAndroid Build Coastguard Worker 554*3ac0a46fSAndroid Build Coastguard Worker def _PrintConclusions(self, conclusions_dict): 555*3ac0a46fSAndroid Build Coastguard Worker """Prints the conclusions as the script output. 556*3ac0a46fSAndroid Build Coastguard Worker 557*3ac0a46fSAndroid Build Coastguard Worker Depending on the script args, this can output a human or a machine-readable 558*3ac0a46fSAndroid Build Coastguard Worker version of the conclusions. 559*3ac0a46fSAndroid Build Coastguard Worker 560*3ac0a46fSAndroid Build Coastguard Worker Args: 561*3ac0a46fSAndroid Build Coastguard Worker conclusions_dict: Dict to print returned from 562*3ac0a46fSAndroid Build Coastguard Worker ComparisonConclusions.GetOutputDict(). 563*3ac0a46fSAndroid Build Coastguard Worker """ 564*3ac0a46fSAndroid Build Coastguard Worker if self.args.machine_readable: 565*3ac0a46fSAndroid Build Coastguard Worker print(json.dumps(conclusions_dict)) 566*3ac0a46fSAndroid Build Coastguard Worker else: 567*3ac0a46fSAndroid Build Coastguard Worker PrintConclusionsDictHumanReadable( 568*3ac0a46fSAndroid Build Coastguard Worker conclusions_dict, colored=True, key=self.args.case_order) 569*3ac0a46fSAndroid Build Coastguard Worker 570*3ac0a46fSAndroid Build Coastguard Worker def _CleanUp(self, conclusions): 571*3ac0a46fSAndroid Build Coastguard Worker """Removes profile output files for uninteresting cases. 572*3ac0a46fSAndroid Build Coastguard Worker 573*3ac0a46fSAndroid Build Coastguard Worker Cases without significant regressions or improvements and considered 574*3ac0a46fSAndroid Build Coastguard Worker uninteresting. 575*3ac0a46fSAndroid Build Coastguard Worker 576*3ac0a46fSAndroid Build Coastguard Worker Args: 577*3ac0a46fSAndroid Build Coastguard Worker conclusions: A ComparisonConclusions. 578*3ac0a46fSAndroid Build Coastguard Worker """ 579*3ac0a46fSAndroid Build Coastguard Worker if not self.args.output_dir: 580*3ac0a46fSAndroid Build Coastguard Worker return 581*3ac0a46fSAndroid Build Coastguard Worker 582*3ac0a46fSAndroid Build Coastguard Worker if self.args.profiler != 'callgrind': 583*3ac0a46fSAndroid Build Coastguard Worker return 584*3ac0a46fSAndroid Build Coastguard Worker 585*3ac0a46fSAndroid Build Coastguard Worker for case_result in conclusions.GetCaseResults().values(): 586*3ac0a46fSAndroid Build Coastguard Worker if case_result.rating not in [RATING_REGRESSION, RATING_IMPROVEMENT]: 587*3ac0a46fSAndroid Build Coastguard Worker self._CleanUpOutputFile('before', case_result.case_name) 588*3ac0a46fSAndroid Build Coastguard Worker self._CleanUpOutputFile('after', case_result.case_name) 589*3ac0a46fSAndroid Build Coastguard Worker 590*3ac0a46fSAndroid Build Coastguard Worker def _CleanUpOutputFile(self, run_label, case_name): 591*3ac0a46fSAndroid Build Coastguard Worker """Removes one profile output file. 592*3ac0a46fSAndroid Build Coastguard Worker 593*3ac0a46fSAndroid Build Coastguard Worker If the output file does not exist, fails silently. 594*3ac0a46fSAndroid Build Coastguard Worker 595*3ac0a46fSAndroid Build Coastguard Worker Args: 596*3ac0a46fSAndroid Build Coastguard Worker run_label: String to differentiate a version of the code in output 597*3ac0a46fSAndroid Build Coastguard Worker files from other versions. 598*3ac0a46fSAndroid Build Coastguard Worker case_name: String identifying test case for which to remove the output 599*3ac0a46fSAndroid Build Coastguard Worker file. 600*3ac0a46fSAndroid Build Coastguard Worker """ 601*3ac0a46fSAndroid Build Coastguard Worker try: 602*3ac0a46fSAndroid Build Coastguard Worker os.remove(self._GetProfileFilePath(run_label, case_name)) 603*3ac0a46fSAndroid Build Coastguard Worker except OSError: 604*3ac0a46fSAndroid Build Coastguard Worker pass 605*3ac0a46fSAndroid Build Coastguard Worker 606*3ac0a46fSAndroid Build Coastguard Worker 607*3ac0a46fSAndroid Build Coastguard Workerdef main(): 608*3ac0a46fSAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 609*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 610*3ac0a46fSAndroid Build Coastguard Worker 'input_paths', 611*3ac0a46fSAndroid Build Coastguard Worker nargs='+', 612*3ac0a46fSAndroid Build Coastguard Worker help='pdf files or directories to search for pdf files ' 613*3ac0a46fSAndroid Build Coastguard Worker 'to run as test cases') 614*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 615*3ac0a46fSAndroid Build Coastguard Worker '--branch-before', 616*3ac0a46fSAndroid Build Coastguard Worker help='git branch to use as "before" for comparison. ' 617*3ac0a46fSAndroid Build Coastguard Worker 'Omitting this will use the current branch ' 618*3ac0a46fSAndroid Build Coastguard Worker 'without uncommitted changes as the baseline.') 619*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 620*3ac0a46fSAndroid Build Coastguard Worker '--branch-after', 621*3ac0a46fSAndroid Build Coastguard Worker help='git branch to use as "after" for comparison. ' 622*3ac0a46fSAndroid Build Coastguard Worker 'Omitting this will use the current branch ' 623*3ac0a46fSAndroid Build Coastguard Worker 'with uncommitted changes.') 624*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 625*3ac0a46fSAndroid Build Coastguard Worker '--build-dir', 626*3ac0a46fSAndroid Build Coastguard Worker default=os.path.join('out', 'Release'), 627*3ac0a46fSAndroid Build Coastguard Worker help='relative path from the base source directory ' 628*3ac0a46fSAndroid Build Coastguard Worker 'to the build directory') 629*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 630*3ac0a46fSAndroid Build Coastguard Worker '--build-dir-before', 631*3ac0a46fSAndroid Build Coastguard Worker help='relative path from the base source directory ' 632*3ac0a46fSAndroid Build Coastguard Worker 'to the build directory for the "before" branch, if ' 633*3ac0a46fSAndroid Build Coastguard Worker 'different from the build directory for the ' 634*3ac0a46fSAndroid Build Coastguard Worker '"after" branch') 635*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 636*3ac0a46fSAndroid Build Coastguard Worker '--cache-dir', 637*3ac0a46fSAndroid Build Coastguard Worker default=None, 638*3ac0a46fSAndroid Build Coastguard Worker help='directory with a new or preexisting cache for ' 639*3ac0a46fSAndroid Build Coastguard Worker 'downloads. Default is to not use a cache.') 640*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 641*3ac0a46fSAndroid Build Coastguard Worker '--this-repo', 642*3ac0a46fSAndroid Build Coastguard Worker action='store_true', 643*3ac0a46fSAndroid Build Coastguard Worker help='use the repository where the script is instead of ' 644*3ac0a46fSAndroid Build Coastguard Worker 'checking out a temporary one. This is faster and ' 645*3ac0a46fSAndroid Build Coastguard Worker 'does not require downloads, but although it ' 646*3ac0a46fSAndroid Build Coastguard Worker 'restores the state of the local repo, if the ' 647*3ac0a46fSAndroid Build Coastguard Worker 'script is killed or crashes the changes can remain ' 648*3ac0a46fSAndroid Build Coastguard Worker 'stashed and you may be on another branch.') 649*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 650*3ac0a46fSAndroid Build Coastguard Worker '--profiler', 651*3ac0a46fSAndroid Build Coastguard Worker default='callgrind', 652*3ac0a46fSAndroid Build Coastguard Worker help='which profiler to use. Supports callgrind, ' 653*3ac0a46fSAndroid Build Coastguard Worker 'perfstat, and none. Default is callgrind.') 654*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 655*3ac0a46fSAndroid Build Coastguard Worker '--interesting-section', 656*3ac0a46fSAndroid Build Coastguard Worker action='store_true', 657*3ac0a46fSAndroid Build Coastguard Worker help='whether to measure just the interesting section or ' 658*3ac0a46fSAndroid Build Coastguard Worker 'the whole test harness. Limiting to only the ' 659*3ac0a46fSAndroid Build Coastguard Worker 'interesting section does not work on Release since ' 660*3ac0a46fSAndroid Build Coastguard Worker 'the delimiters are optimized out') 661*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 662*3ac0a46fSAndroid Build Coastguard Worker '--pages', 663*3ac0a46fSAndroid Build Coastguard Worker help='selects some pages to be rendered. Page numbers ' 664*3ac0a46fSAndroid Build Coastguard Worker 'are 0-based. "--pages A" will render only page A. ' 665*3ac0a46fSAndroid Build Coastguard Worker '"--pages A-B" will render pages A to B ' 666*3ac0a46fSAndroid Build Coastguard Worker '(inclusive).') 667*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 668*3ac0a46fSAndroid Build Coastguard Worker '--num-workers', 669*3ac0a46fSAndroid Build Coastguard Worker default=multiprocessing.cpu_count(), 670*3ac0a46fSAndroid Build Coastguard Worker type=int, 671*3ac0a46fSAndroid Build Coastguard Worker help='run NUM_WORKERS jobs in parallel') 672*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 673*3ac0a46fSAndroid Build Coastguard Worker '--output-dir', help='directory to write the profile data output files') 674*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 675*3ac0a46fSAndroid Build Coastguard Worker '--png-dir', 676*3ac0a46fSAndroid Build Coastguard Worker default=None, 677*3ac0a46fSAndroid Build Coastguard Worker help='outputs pngs to the specified directory that can ' 678*3ac0a46fSAndroid Build Coastguard Worker 'be compared with a static html generated. Will ' 679*3ac0a46fSAndroid Build Coastguard Worker 'affect performance measurements.') 680*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 681*3ac0a46fSAndroid Build Coastguard Worker '--png-threshold', 682*3ac0a46fSAndroid Build Coastguard Worker default=0.0, 683*3ac0a46fSAndroid Build Coastguard Worker type=float, 684*3ac0a46fSAndroid Build Coastguard Worker help='Requires --png-dir. Threshold above which a png ' 685*3ac0a46fSAndroid Build Coastguard Worker 'is considered to have changed.') 686*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 687*3ac0a46fSAndroid Build Coastguard Worker '--threshold-significant', 688*3ac0a46fSAndroid Build Coastguard Worker default=0.02, 689*3ac0a46fSAndroid Build Coastguard Worker type=float, 690*3ac0a46fSAndroid Build Coastguard Worker help='variations in performance above this factor are ' 691*3ac0a46fSAndroid Build Coastguard Worker 'considered significant') 692*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 693*3ac0a46fSAndroid Build Coastguard Worker '--machine-readable', 694*3ac0a46fSAndroid Build Coastguard Worker action='store_true', 695*3ac0a46fSAndroid Build Coastguard Worker help='whether to get output for machines. If enabled the ' 696*3ac0a46fSAndroid Build Coastguard Worker 'output will be a json with the format specified in ' 697*3ac0a46fSAndroid Build Coastguard Worker 'ComparisonConclusions.GetOutputDict(). Default is ' 698*3ac0a46fSAndroid Build Coastguard Worker 'human-readable.') 699*3ac0a46fSAndroid Build Coastguard Worker parser.add_argument( 700*3ac0a46fSAndroid Build Coastguard Worker '--case-order', 701*3ac0a46fSAndroid Build Coastguard Worker default=None, 702*3ac0a46fSAndroid Build Coastguard Worker help='what key to use when sorting test cases in the ' 703*3ac0a46fSAndroid Build Coastguard Worker 'output. Accepted values are "after", "before", ' 704*3ac0a46fSAndroid Build Coastguard Worker '"ratio" and "rating". Default is sorting by test ' 705*3ac0a46fSAndroid Build Coastguard Worker 'case path.') 706*3ac0a46fSAndroid Build Coastguard Worker 707*3ac0a46fSAndroid Build Coastguard Worker args = parser.parse_args() 708*3ac0a46fSAndroid Build Coastguard Worker 709*3ac0a46fSAndroid Build Coastguard Worker # Always start at the pdfium src dir, which is assumed to be two level above 710*3ac0a46fSAndroid Build Coastguard Worker # this script. 711*3ac0a46fSAndroid Build Coastguard Worker pdfium_src_dir = os.path.join( 712*3ac0a46fSAndroid Build Coastguard Worker os.path.dirname(__file__), os.path.pardir, os.path.pardir) 713*3ac0a46fSAndroid Build Coastguard Worker os.chdir(pdfium_src_dir) 714*3ac0a46fSAndroid Build Coastguard Worker 715*3ac0a46fSAndroid Build Coastguard Worker git = GitHelper() 716*3ac0a46fSAndroid Build Coastguard Worker 717*3ac0a46fSAndroid Build Coastguard Worker if args.branch_after and not args.branch_before: 718*3ac0a46fSAndroid Build Coastguard Worker PrintErr('--branch-after requires --branch-before to be specified.') 719*3ac0a46fSAndroid Build Coastguard Worker return 1 720*3ac0a46fSAndroid Build Coastguard Worker 721*3ac0a46fSAndroid Build Coastguard Worker if args.branch_after and not git.BranchExists(args.branch_after): 722*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Branch "%s" does not exist' % args.branch_after) 723*3ac0a46fSAndroid Build Coastguard Worker return 1 724*3ac0a46fSAndroid Build Coastguard Worker 725*3ac0a46fSAndroid Build Coastguard Worker if args.branch_before and not git.BranchExists(args.branch_before): 726*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Branch "%s" does not exist' % args.branch_before) 727*3ac0a46fSAndroid Build Coastguard Worker return 1 728*3ac0a46fSAndroid Build Coastguard Worker 729*3ac0a46fSAndroid Build Coastguard Worker if args.output_dir: 730*3ac0a46fSAndroid Build Coastguard Worker args.output_dir = os.path.expanduser(args.output_dir) 731*3ac0a46fSAndroid Build Coastguard Worker if not os.path.isdir(args.output_dir): 732*3ac0a46fSAndroid Build Coastguard Worker PrintErr('"%s" is not a directory' % args.output_dir) 733*3ac0a46fSAndroid Build Coastguard Worker return 1 734*3ac0a46fSAndroid Build Coastguard Worker 735*3ac0a46fSAndroid Build Coastguard Worker if args.png_dir: 736*3ac0a46fSAndroid Build Coastguard Worker args.png_dir = os.path.expanduser(args.png_dir) 737*3ac0a46fSAndroid Build Coastguard Worker if not os.path.isdir(args.png_dir): 738*3ac0a46fSAndroid Build Coastguard Worker PrintErr('"%s" is not a directory' % args.png_dir) 739*3ac0a46fSAndroid Build Coastguard Worker return 1 740*3ac0a46fSAndroid Build Coastguard Worker 741*3ac0a46fSAndroid Build Coastguard Worker if args.threshold_significant <= 0.0: 742*3ac0a46fSAndroid Build Coastguard Worker PrintErr('--threshold-significant should receive a positive float') 743*3ac0a46fSAndroid Build Coastguard Worker return 1 744*3ac0a46fSAndroid Build Coastguard Worker 745*3ac0a46fSAndroid Build Coastguard Worker if args.png_threshold: 746*3ac0a46fSAndroid Build Coastguard Worker if not args.png_dir: 747*3ac0a46fSAndroid Build Coastguard Worker PrintErr('--png-threshold requires --png-dir to be specified.') 748*3ac0a46fSAndroid Build Coastguard Worker return 1 749*3ac0a46fSAndroid Build Coastguard Worker 750*3ac0a46fSAndroid Build Coastguard Worker if args.png_threshold <= 0.0: 751*3ac0a46fSAndroid Build Coastguard Worker PrintErr('--png-threshold should receive a positive float') 752*3ac0a46fSAndroid Build Coastguard Worker return 1 753*3ac0a46fSAndroid Build Coastguard Worker 754*3ac0a46fSAndroid Build Coastguard Worker if args.pages: 755*3ac0a46fSAndroid Build Coastguard Worker if not re.match(r'^\d+(-\d+)?$', args.pages): 756*3ac0a46fSAndroid Build Coastguard Worker PrintErr('Supported formats for --pages are "--pages 7" and ' 757*3ac0a46fSAndroid Build Coastguard Worker '"--pages 3-6"') 758*3ac0a46fSAndroid Build Coastguard Worker return 1 759*3ac0a46fSAndroid Build Coastguard Worker 760*3ac0a46fSAndroid Build Coastguard Worker run = CompareRun(args) 761*3ac0a46fSAndroid Build Coastguard Worker return run.Run() 762*3ac0a46fSAndroid Build Coastguard Worker 763*3ac0a46fSAndroid Build Coastguard Worker 764*3ac0a46fSAndroid Build Coastguard Workerif __name__ == '__main__': 765*3ac0a46fSAndroid Build Coastguard Worker sys.exit(main()) 766