1 # Copyright 2017 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 6 # Recipe for uploading Coverage results. 7 8 9 import calendar 10 11 12 DEPS = [ 13 'gsutil', 14 'recipe_engine/file', 15 'recipe_engine/json', 16 'recipe_engine/path', 17 'recipe_engine/properties', 18 'recipe_engine/python', 19 'recipe_engine/raw_io', 20 'recipe_engine/step', 21 'recipe_engine/time', 22 ] 23 24 25 TRY_JOB_FOLDER = 'trybot/%s/%s/' # % (issue_number, patchset_number) 26 COMMIT_FOLDER = 'commit/%s/' # % (git_revision) 27 28 RAW_FILE = '*.profraw' 29 PARSED_FILE = '%s.profdata' 30 SUMMARY_FILE = '%s.summary' 31 32 COVERAGE_RAW_ARCHIVE = '%s.profraw.tar.gz' 33 # Text is an easier format to read with machines (e.g. for Gerrit). 34 COVERAGE_TEXT_FILE = '%s.text.tar' 35 # HTML is a quick and dirty browsable format. (e.g. for coverage.skia.org) 36 COVERAGE_HTML_FILE = '%s.html.tar' 37 38 def RunSteps(api): 39 # See https://clang.llvm.org/docs/SourceBasedCodeCoverage.html for a 40 # detailed explanation of getting code coverage from LLVM. 41 # Since we have already compiled the binary with the special flags 42 # and run the executable to generate an output.profraw, we 43 # need to merge and index the data, create the coverage output, 44 # and then upload the results to GCS. We also upload the intermediate 45 # results to GCS so we can regenerate reports if needed. 46 builder_name = api.properties['buildername'] 47 bucket = api.properties['gs_bucket'] 48 49 # The raw data files are brought in as isolated inputs. It is possible 50 # for there to be 1 if the coverage task wasn't broken up. 51 raw_inputs = api.file.glob_paths('find raw inputs', api.path['start_dir'], 52 RAW_FILE, 53 test_data=['a.raw', 'b.raw', 'c.raw']) 54 55 56 # The instrumented executable is brought in as an isolated input. 57 executable = api.path['start_dir'].join('out','Debug','dm') 58 # clang_dir is brought in via CIPD. 59 clang_dir = api.path['start_dir'].join('clang_linux', 'bin') 60 61 revision = api.properties['revision'] 62 path = COMMIT_FOLDER % revision 63 64 issue = api.properties.get('patch_issue') 65 patchset = api.properties.get('patch_set') 66 if issue and patchset: 67 path = TRY_JOB_FOLDER % (issue, patchset) 68 69 # Upload the raw files, tarred together to decrease upload time and 70 # improve compression. 71 tar_file = api.path['start_dir'].join('raw_data.profraw.tar.gz') 72 cmd = ['tar', '-zcvf', tar_file] 73 cmd.extend(raw_inputs) 74 api.step('create raw data archive', cmd=cmd) 75 76 gcs_file = COVERAGE_RAW_ARCHIVE % builder_name 77 api.gsutil.cp('raw data archive', tar_file, 78 'gs://%s/%s%s' % (bucket, path, gcs_file)) 79 80 # Merge all the raw data files together, then index the data. 81 # This creates one cohesive 82 indexed_data = api.path['start_dir'].join('output.profdata') 83 cmd = [clang_dir.join('llvm-profdata'), 84 'merge', 85 '-sparse', 86 '-o', 87 indexed_data] 88 cmd.extend(raw_inputs) 89 api.step('merge and index', 90 cmd=cmd) 91 92 gcs_file = PARSED_FILE % builder_name 93 api.gsutil.cp('parsed data', indexed_data, 94 'gs://%s/%s%s' % (bucket, path, gcs_file), extra_args=['-Z']) 95 96 # Create text coverage output 97 output_data = api.path['start_dir'].join('coverage_text') 98 api.step('create text summary', 99 cmd=[clang_dir.join('llvm-cov'), 100 'show', 101 executable, 102 '-instr-profile=' + str(indexed_data), 103 '-use-color=0', 104 '-format=text', 105 '-output-dir=' + str(output_data)]) 106 107 # Upload the summary by itself so we can get easier access to it (instead of 108 # downloading and untarring all the coverage data. 109 gcs_file = SUMMARY_FILE % builder_name 110 api.gsutil.cp('coverage summary', output_data.join('index.txt'), 111 'gs://%s/%s%s' % (bucket, path, gcs_file), extra_args=['-Z']) 112 113 tar_file = api.path['start_dir'].join('coverage.text.tar') 114 115 # Tar and upload the coverage data. We tar it to ease downloading/ingestion, 116 # otherwise, there is a 1:1 mapping of source code files -> coverage files. 117 api.step('create text coverage archive', cmd=['tar', '-cvf', 118 tar_file, output_data]) 119 120 gcs_file = COVERAGE_TEXT_FILE % builder_name 121 api.gsutil.cp('text coverage data', tar_file, 122 'gs://%s/%s%s' % (bucket, path, gcs_file), extra_args=['-Z']) 123 124 # Create html coverage output 125 output_data = api.path['start_dir'].join('coverage_html') 126 api.step('create html summary', 127 cmd=[clang_dir.join('llvm-cov'), 128 'show', 129 executable, 130 '-instr-profile=' + str(indexed_data), 131 '-use-color=1', 132 '-format=html', 133 '-output-dir=' + str(output_data)]) 134 135 tar_file = api.path['start_dir'].join('coverage.html.tar') 136 137 # Tar and upload the coverage data. We tar it to ease downloading/ingestion, 138 # otherwise, there is a 1:1 mapping of source code files -> coverage files. 139 api.step('create html coverage archive', 140 cmd=['tar', '-cvf', tar_file, output_data]) 141 142 gcs_file = COVERAGE_HTML_FILE % builder_name 143 api.gsutil.cp('html coverage data', tar_file, 144 'gs://%s/%s%s' % (bucket, path, gcs_file), extra_args=['-Z']) 145 146 147 def GenTests(api): 148 builder = 'Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All' 149 yield ( 150 api.test('normal_bot') + 151 api.properties(buildername=builder, 152 gs_bucket='skia-coverage', 153 revision='abc123', 154 path_config='kitchen') 155 ) 156 157 yield ( 158 api.test('alternate_bucket') + 159 api.properties(buildername=builder, 160 gs_bucket='skia-coverage-alt', 161 revision='abc123', 162 path_config='kitchen') 163 ) 164 165 yield ( 166 api.test('failed_once') + 167 api.properties(buildername=builder, 168 gs_bucket='skia-coverage', 169 revision='abc123', 170 path_config='kitchen') + 171 api.step_data('upload parsed data', retcode=1) 172 ) 173 174 yield ( 175 api.test('failed_all') + 176 api.properties(buildername=builder, 177 gs_bucket='skia-coverage', 178 revision='abc123', 179 path_config='kitchen') + 180 api.step_data('upload parsed data', retcode=1) + 181 api.step_data('upload parsed data (attempt 2)', retcode=1) + 182 api.step_data('upload parsed data (attempt 3)', retcode=1) + 183 api.step_data('upload parsed data (attempt 4)', retcode=1) + 184 api.step_data('upload parsed data (attempt 5)', retcode=1) 185 ) 186 187 yield ( 188 api.test('trybot') + 189 api.properties( 190 buildername=builder, 191 gs_bucket='skia-coverage', 192 revision='abc123', 193 path_config='kitchen', 194 patch_storage='gerrit') + 195 api.properties.tryserver( 196 buildername=builder, 197 gerrit_project='skia', 198 gerrit_url='https://skia-review.googlesource.com/', 199 ) 200 ) 201