| # Copyright 2017 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| |
| # Recipe for uploading Coverage results. |
| |
| |
| import calendar |
| |
| |
| DEPS = [ |
| 'recipe_engine/json', |
| 'recipe_engine/path', |
| 'recipe_engine/properties', |
| 'recipe_engine/step', |
| 'recipe_engine/time', |
| 'gsutil', |
| ] |
| |
| |
| TRY_JOB_FOLDER = 'trybot/%s/%s/' # % (issue_number, patchset_number) |
| COMMIT_FOLDER = 'commit/%s/' # % (git_revision) |
| |
| RAW_FILE = '%s.profraw' |
| PARSED_FILE = '%s.profdata' |
| SUMMARY_FILE = '%s.summary' |
| # Text is an easier format to read with machines (e.g. for Gerrit). |
| COVERAGE_TEXT_FILE = '%s.text.tar' |
| # HTML is a quick and dirty browsable format. (e.g. for coverage.skia.org) |
| COVERAGE_HTML_FILE = '%s.html.tar' |
| |
| def RunSteps(api): |
| # See https://clang.llvm.org/docs/SourceBasedCodeCoverage.html for a |
| # detailed explanation of getting code coverage from LLVM. |
| # Since we have already compiled the binary with the special flags |
| # and run the executable to generate an output.profraw, we |
| # need to merge and index the data, create the coverage output, |
| # and then upload the results to GCS. We also upload the intermediate |
| # results to GCS so we can regenerate reports if needed. |
| builder_name = api.properties['buildername'] |
| bucket = api.properties['gs_bucket'] |
| |
| # The raw data is brought in as an isolated input. |
| raw_data = api.path['start_dir'].join('output.profraw') |
| # The instrumented executable is brought in as an isolated input. |
| executable = api.path['start_dir'].join('out','Debug','dm') |
| # clang_dir is brought in via CIPD. |
| clang_dir = api.path['start_dir'].join('clang_linux', 'bin') |
| |
| revision = api.properties['revision'] |
| path = COMMIT_FOLDER % revision |
| |
| issue = api.properties.get('patch_issue') |
| patchset = api.properties.get('patch_set') |
| if issue and patchset: |
| path = TRY_JOB_FOLDER % (issue, patchset) |
| |
| gcs_file = RAW_FILE % builder_name |
| api.gsutil.cp('raw data', raw_data, |
| 'gs://%s/%s%s' % (bucket, path, gcs_file), ['-Z']) |
| |
| # Merge and Index the data. |
| indexed_data = api.path['start_dir'].join('output.profdata') |
| api.step('merge and index', |
| cmd=[clang_dir.join('llvm-profdata'), |
| 'merge', |
| '-sparse', |
| raw_data, |
| '-o', |
| indexed_data ]) |
| |
| gcs_file = PARSED_FILE % builder_name |
| api.gsutil.cp('parsed data', indexed_data, |
| 'gs://%s/%s%s' % (bucket, path, gcs_file), ['-Z']) |
| |
| # Create text coverage output |
| output_data = api.path['start_dir'].join('coverage_text') |
| api.step('create text summary', |
| cmd=[clang_dir.join('llvm-cov'), |
| 'show', |
| executable, |
| '-instr-profile=' + str(indexed_data), |
| '-use-color=0', |
| '-format=text', |
| '-output-dir=' + str(output_data)]) |
| |
| # Upload the summary by itself so we can get easier access to it (instead of |
| # downloading and untarring all the coverage data. |
| gcs_file = SUMMARY_FILE % builder_name |
| api.gsutil.cp('coverage summary', output_data.join('index.txt'), |
| 'gs://%s/%s%s' % (bucket, path, gcs_file), ['-Z']) |
| |
| tar_file = api.path['start_dir'].join('coverage.text.tar') |
| |
| # Tar and upload the coverage data. We tar it to ease downloading/ingestion, |
| # otherwise, there is a 1:1 mapping of source code files -> coverage files. |
| api.step('create text coverage archive', cmd=['tar', '-cvf', |
| tar_file, output_data]) |
| |
| gcs_file = COVERAGE_TEXT_FILE % builder_name |
| api.gsutil.cp('text coverage data', tar_file, |
| 'gs://%s/%s%s' % (bucket, path, gcs_file), ['-Z']) |
| |
| # Create html coverage output |
| output_data = api.path['start_dir'].join('coverage_html') |
| api.step('create html summary', |
| cmd=[clang_dir.join('llvm-cov'), |
| 'show', |
| executable, |
| '-instr-profile=' + str(indexed_data), |
| '-use-color=1', |
| '-format=html', |
| '-output-dir=' + str(output_data)]) |
| |
| tar_file = api.path['start_dir'].join('coverage.html.tar') |
| |
| # Tar and upload the coverage data. We tar it to ease downloading/ingestion, |
| # otherwise, there is a 1:1 mapping of source code files -> coverage files. |
| api.step('create html coverage archive', |
| cmd=['tar', '-cvf', tar_file, output_data]) |
| |
| gcs_file = COVERAGE_HTML_FILE % builder_name |
| api.gsutil.cp('html coverage data', tar_file, |
| 'gs://%s/%s%s' % (bucket, path, gcs_file), ['-Z']) |
| |
| |
| def GenTests(api): |
| builder = 'Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All' |
| yield ( |
| api.test('normal_bot') + |
| api.properties(buildername=builder, |
| gs_bucket='skia-coverage', |
| revision='abc123', |
| path_config='kitchen') |
| ) |
| |
| yield ( |
| api.test('alternate_bucket') + |
| api.properties(buildername=builder, |
| gs_bucket='skia-coverage-alt', |
| revision='abc123', |
| path_config='kitchen') |
| ) |
| |
| yield ( |
| api.test('failed_once') + |
| api.properties(buildername=builder, |
| gs_bucket='skia-coverage', |
| revision='abc123', |
| path_config='kitchen') + |
| api.step_data('upload raw data', retcode=1) |
| ) |
| |
| yield ( |
| api.test('failed_all') + |
| api.properties(buildername=builder, |
| gs_bucket='skia-coverage', |
| revision='abc123', |
| path_config='kitchen') + |
| api.step_data('upload raw data', retcode=1) + |
| api.step_data('upload raw data (attempt 2)', retcode=1) + |
| api.step_data('upload raw data (attempt 3)', retcode=1) + |
| api.step_data('upload raw data (attempt 4)', retcode=1) + |
| api.step_data('upload raw data (attempt 5)', retcode=1) |
| ) |
| |
| yield ( |
| api.test('trybot') + |
| api.properties( |
| buildername=builder, |
| gs_bucket='skia-coverage', |
| revision='abc123', |
| path_config='kitchen', |
| patch_storage='gerrit') + |
| api.properties.tryserver( |
| buildername=builder, |
| gerrit_project='skia', |
| gerrit_url='https://skia-review.googlesource.com/', |
| ) |
| ) |