aboutsummaryrefslogtreecommitdiffhomepage
path: root/infra/bots/recipes/upload_coverage_results.py
blob: fe78c86c820754cf26add32386e763fb9793614e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# 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'
  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/',
      )
  )