Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 1 | # Regres - SwiftShader automated testing |
| 2 | |
| 3 | ## Introduction |
| 4 | |
| 5 | Regres is a collection of tools to perform [dEQP](https://github.com/KhronosGroup/VK-GL-CTS) |
| 6 | presubmit and continuous integration testing and code coverage evaluation for |
| 7 | SwiftShader. |
| 8 | |
| 9 | Regres provides: |
| 10 | |
| 11 | * [Presubmit testing](#presubmit-testing) - An automatic OpenGL|ES and Vulkan |
| 12 | dEQP test run for each Gerrit patchset put up for review. |
| 13 | * [Continuous integration testing](#daily-run-continuous-integration-testing) - |
| 14 | A OpenGL|ES and Vulkan dEQP test run performed against the `master` branch each night. \ |
| 15 | This nightly run also produces code coverage information which can be viewed at |
| 16 | [swiftshader-regres.github.io/swiftshader-coverage](https://swiftshader-regres.github.io/swiftshader-coverage/). |
| 17 | * [Local dEQP test runner](#local-dEQP-test-runner) Provides a local tool for |
| 18 | efficiently running a number of dEQP tests based wildcard or regex name |
| 19 | matching. |
| 20 | |
Nicolas Capens | 0e62619 | 2020-07-30 21:49:45 -0400 | [diff] [blame] | 21 | The Regres source root directory is at [`<swiftshader>/tests/regres/`](https://cs.opensource.google/swiftshader/SwiftShader/+/master:tests/regres/). |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 22 | |
| 23 | ## Presubmit testing |
| 24 | |
| 25 | Regres monitors changes that have been [put up for review with Gerrit](https://swiftshader-review.googlesource.com/q/status:open). |
| 26 | |
| 27 | Once a new [qualifying](#qualifying) patchset has been found, regres will |
| 28 | checkout, build and test the change against the parent changelist. \ |
| 29 | Any differences in results are reported as a review comment on the change |
| 30 | [[example]](https://swiftshader-review.googlesource.com/c/SwiftShader/+/46369/5#message-4f09ea3e6d01ed94ae26183c8b6c547c90492c12). |
| 31 | |
| 32 | ### Qualifying |
| 33 | |
| 34 | As Regres may be running externally authored code on Google hardware, |
| 35 | Regres will only test a change if it is authored by or reviewed by a Googler. |
| 36 | |
| 37 | Only the most recent patchset of a change will be tested. If a new patchset is |
| 38 | pushed while the previous is currently being tested, then testing will continue |
| 39 | to completion and the previous patchsets will be posted, and the new patchset |
| 40 | will be queued for testing. |
| 41 | |
| 42 | ### Prioritization |
| 43 | |
| 44 | At the time of writing a Regres presubmit run takes a little over 20 minutes to |
| 45 | complete, and there is a single Regres machine servicing all changes. |
| 46 | To keep Regres responsive, changes are prioritized based on their 'readiness to |
| 47 | land', which is determined by the change's `Kokoro-Presubmit`, `Code-Review` and |
| 48 | `Presubmit-Ready` Gerrit labels. |
| 49 | |
| 50 | ### Test Filtering |
| 51 | |
| 52 | By default, Regres will run all the test lists declared in the |
Nicolas Capens | 0e62619 | 2020-07-30 21:49:45 -0400 | [diff] [blame] | 53 | [`<swiftshader>/tests/regres/ci-tests.json`](https://cs.opensource.google/swiftshader/SwiftShader/+/master:tests/regres/ci-tests.json) file.\ |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 54 | As new functionally is being implemented, the test lists in `ci-tests.json` may |
| 55 | reference known-passing test lists updated by the [daily run](#daily-run-continuous-integration-testing), |
| 56 | so that failing tests for incomplete functionality are skipped, but tests that |
| 57 | pass for new functionality *are tested* to ensure they do not regres. |
| 58 | |
| 59 | Additional tests names found in the files referenced by |
Nicolas Capens | 0e62619 | 2020-07-30 21:49:45 -0400 | [diff] [blame] | 60 | [`<swiftshader>/tests/regres/full-tests.json`](https://cs.opensource.google/swiftshader/SwiftShader/+/master:tests/regres/full-tests.json) |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 61 | can be explicitly included in the change's presubmit run |
| 62 | by including a line in the change description with the signature: |
| 63 | |
| 64 | ```text |
| 65 | Test: <dEQP-test-pattern> |
| 66 | ``` |
| 67 | |
| 68 | `<dEQP-test-pattern>` can be a single dEQP test name, or you can use wildcards |
| 69 | [as documented here](https://golang.org/pkg/path/filepath/#Match). |
| 70 | |
| 71 | You can repeat `Test:` as many times as you like. `Tests:` is also acccepted. |
| 72 | |
| 73 | [For example](https://swiftshader-review.googlesource.com/c/SwiftShader/+/26574): |
| 74 | |
| 75 | ```text |
| 76 | Add support for OpLogicalEqual, OpLogicalNotEqual |
| 77 | |
| 78 | Test: dEQP-VK.glsl.operator.bool_compare.* |
| 79 | Test: dEQP-VK.glsl.operator.binary_operator.equal.* |
| 80 | Test: dEQP-VK.glsl.operator.binary_operator.not_equal.* |
| 81 | Bug: b/126870789 |
| 82 | Change-Id: I9d33444d67792274d8027b7d1632235533cfc079 |
| 83 | ``` |
| 84 | |
| 85 | ## Daily-run continuous integration testing |
| 86 | |
Nicolas Capens | 0e62619 | 2020-07-30 21:49:45 -0400 | [diff] [blame] | 87 | Once a day, regres will also test another set of tests from [`<swiftshader>/tests/regres/full-tests.json`](https://cs.opensource.google/swiftshader/SwiftShader/+/master:tests/regres/full-tests.json), |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 88 | and post the test result lists as a Gerrit changelist |
| 89 | [[example]](https://swiftshader-review.googlesource.com/c/SwiftShader/+/46448). |
| 90 | |
| 91 | The daily run also performs code coverage instrumentation per dEQP test, |
| 92 | automatically uploading the results of all the dEQP tests to the viewer at |
| 93 | [swiftshader-regres.github.io/swiftshader-coverage](https://swiftshader-regres.github.io/swiftshader-coverage/). |
| 94 | |
| 95 | ## Local dEQP test runner |
| 96 | |
| 97 | Regres also provides a multi-threaded, [process sandboxed](#process-sandboxing), |
| 98 | local dEQP test runner with a wild-card / regex based test name matcher. |
| 99 | |
| 100 | The local test runner can be run with: |
| 101 | |
Nicolas Capens | 0e62619 | 2020-07-30 21:49:45 -0400 | [diff] [blame] | 102 | [`<swiftshader>/tests/regres/run_testlist.sh`](https://cs.opensource.google/swiftshader/SwiftShader/+/master:tests/regres/run_testlist.sh) `--deqp-vk=<path to deqp-vk> [--filter=<test name filter>]` |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 103 | |
| 104 | `<test name filter>` can be a single dEQP test name, or you can use wildcards |
| 105 | [as documented here](https://golang.org/pkg/path/filepath/#Match). |
| 106 | Alternatively, start with a `/` to use a regex filter. |
| 107 | |
| 108 | Other useful flags: |
| 109 | |
| 110 | ```text |
| 111 | -limit int |
| 112 | only run a maximum of this number of tests |
| 113 | -no-results |
| 114 | disable generation of results.json file |
| 115 | -output string |
| 116 | path to an output JSON results file (default "results.json") |
| 117 | -shuffle |
| 118 | shuffle tests |
| 119 | -test-list string |
| 120 | path to a test list file (default "vk-master-PASS.txt") |
| 121 | ``` |
| 122 | |
Nicolas Capens | 0e62619 | 2020-07-30 21:49:45 -0400 | [diff] [blame] | 123 | Run [`<swiftshader>/tests/regres/run_testlist.sh`](https://cs.opensource.google/swiftshader/SwiftShader/+/master:tests/regres/run_testlist.sh) with `--help` to see all available flags. |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 124 | |
| 125 | ## Process sandboxing |
| 126 | |
| 127 | Regres will run each dEQP test in a separate process to prevent state |
| 128 | leakage between tests. |
| 129 | |
| 130 | Tests are run concurrently, and crashing processes will not take down the test |
| 131 | runner. |
| 132 | |
| 133 | Some dEQP tests are known to perform excessive memory allocations (i.e. keep |
| 134 | allocating until no more can be claimed from the OS). \ |
| 135 | In order to prevent a single test starving other test processes of memory, each |
| 136 | process is restricted to a fraction of the system's memory using [linux resource limits](https://man7.org/linux/man-pages/man2/getrlimit.2.html). |
| 137 | |
| 138 | Tests may also deadlock, so each test process has a time limit before they are |
| 139 | automatically killed. |
| 140 | |
| 141 | ## Implementation details |
| 142 | |
| 143 | ### Presubmit & daily run process |
| 144 | |
| 145 | Regres runs until stopped, and will: |
| 146 | |
| 147 | * Download a known compatible version of Clang to a cache directory. This will |
| 148 | be used for all compilation stages below. |
| 149 | * Periodically poll Gerrit for recently opened changes |
| 150 | * Periodically query Gerrit for details about each tracked change, determining |
| 151 | [whether it should be tested](#qualifying), and determine its current |
| 152 | [priority](#prioritization). |
| 153 | * A qualifying change with the highest priority will be picked, and the |
| 154 | following is performed for the change: |
| 155 | 1. The change is `git fetch`ed into a temporary directory. |
| 156 | 2. If not already cached, the dEQP version described in the |
Nicolas Capens | 0e62619 | 2020-07-30 21:49:45 -0400 | [diff] [blame] | 157 | change's [`<swiftshader>/tests/regres/deqp.json`](https://cs.opensource.google/swiftshader/SwiftShader/+/master:tests/regres/deqp.json) file is downloaded and built the into a cached directory. |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 158 | 3. The source for the change is built into a temporary build directory. |
| 159 | 4. The built dEQP binaries are used to test the change. The full test results |
| 160 | are stored in a cached directory. |
| 161 | 5. If the parent change's test results aren't already cached, then steps 3 and |
| 162 | 4 are repeated for the parent change. |
| 163 | 6. The results of the two changes are diffed, and the results of the diff are |
| 164 | posted to the change as a Gerrit review comment. |
| 165 | * The above is repeated until it is time to perform a daily run, upon which: |
| 166 | 1. The `HEAD` change of `master` is fetched into a temporary directory. |
| 167 | 2. If not already cached, the dEQP version described in the |
Nicolas Capens | 0e62619 | 2020-07-30 21:49:45 -0400 | [diff] [blame] | 168 | change's [`<swiftshader>/tests/regres/deqp.json`](https://cs.opensource.google/swiftshader/SwiftShader/+/master:tests/regres/deqp.json) file is downloaded and built the into a cached directory. |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 169 | 3. The `HEAD` change is built into a temporary directory, optionally with code |
| 170 | coverage instrumenting. |
| 171 | 4. The build dEQP binaries are used to test the change. The full test results |
| 172 | are stored in a cached directory, and the each test is binned by status and |
Nicolas Capens | 0e62619 | 2020-07-30 21:49:45 -0400 | [diff] [blame] | 173 | written to the [`<swiftshader>/tests/regres/testlists`](https://cs.opensource.google/swiftshader/SwiftShader/+/master:tests/regres/testlists) directory. |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 174 | 5. A new Gerrit change is created containing the updated test lists and put up |
| 175 | for review, along with a summary of test result changes [[example]](https://swiftshader-review.googlesource.com/c/SwiftShader/+/46448). |
| 176 | If there's an existing daily test change up for review then this is reused |
| 177 | instead of creating another. |
| 178 | 6. If the build included code coverage instrumentation, then the coverage |
| 179 | results are collated from all test runs, processed and compressed, and |
| 180 | uploaded to [github.com/swiftshader-regres/swiftshader-coverage](https://github.com/swiftshader-regres/swiftshader-coverage) |
| 181 | which is immediately reflected at [swiftshader-regres.github.io/swiftshader-coverage](https://swiftshader-regres.github.io/swiftshader-coverage). |
| 182 | This process is [described in more detail below](#code-coverage). |
| 183 | 7. Stages 3 - 5 are repeated for both the LLVM and Subzero backends. |
| 184 | |
| 185 | ### Caching |
| 186 | |
| 187 | The cache directory is heavily used to avoid duplicated work. For example, it |
| 188 | is common for patchsets to be repeatedly pushed with the same parent change, so |
| 189 | the test results of the parent can be calculated once and stored. A tested |
| 190 | patchset that is merged into master would also be cached when used as a parent |
| 191 | of another change. |
| 192 | |
| 193 | The cache needs to consider more than just the change identifier as the |
| 194 | cache-key for storing and retrieving data. Both the test lists and version of |
| 195 | dEQP used are dictated by the change being tested, and so both used as part of |
| 196 | the cache key. |
| 197 | |
Nicolas Capens | d625b64 | 2021-09-23 09:24:17 -0400 | [diff] [blame] | 198 | ### Vulkan Loader usage |
| 199 | |
| 200 | Applications make use of the Vulkan API by loading the [Vulkan Loader](https://github.com/KhronosGroup/Vulkan-Loader) |
| 201 | library (`libvulkan.so.1` on Linux), which enumerates available Vulkan |
| 202 | implementations (typically GPUs and their drivers) before an actual 'instance' |
| 203 | is created to communicate with a specific Installable Client Driver (ICD). |
| 204 | |
| 205 | However, SwiftShader can build into libvulkan.so.1 itself, which implements the |
| 206 | same API entry functions as the Vulkan Loader. Regres by default will make dEQP |
| 207 | load this SwiftShader library instead of the system's Vulkan Loader. It ensures |
| 208 | test results are independent of the system's Vulkan setup. |
| 209 | |
| 210 | To override this, one can set LD_LIBRARY_PATH to point to the location of a |
| 211 | Loader's libvulkan.so.1. |
| 212 | |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 213 | ### Code coverage |
| 214 | |
| 215 | The [daily run](#daily-run-continuous-integration-testing) produces code |
| 216 | coverage information that can be examined for each individual dEQP test at |
| 217 | [swiftshader-regres.github.io/swiftshader-coverage](https://swiftshader-regres.github.io/swiftshader-coverage/). |
| 218 | |
| 219 | The process for generating this information is complex, and is described in |
| 220 | detail below: |
| 221 | |
| 222 | #### Per-test generation |
| 223 | |
| 224 | Code coverage instrumentation is generated with |
| 225 | [clang's `--coverage`](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html) |
| 226 | functionality. This compiler option is enabled by using SwiftShader's |
| 227 | `SWIFTSHADER_EMIT_COVERAGE` CMake flag. |
| 228 | |
| 229 | Each dEQP test process is run with a unique `LLVM_PROFILE_FILE` environment |
| 230 | variable value which dictates where the process writes its raw coverage profile |
| 231 | file. Each process gets a different path so that we can emit coverage from |
| 232 | multiple, concurrent dEQP test processes. |
| 233 | |
| 234 | #### Parsing |
| 235 | |
| 236 | [Clang provides two tools](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#creating-coverage-reports) for processing coverage data: |
| 237 | |
| 238 | * `llvm-profdata` indexes the raw `.profraw` coverage profile file and emits a |
| 239 | `.profdata` file. |
| 240 | * `llvm-cov` further processes the `.profdata` file into something human |
| 241 | readable or machine parsable. |
| 242 | |
| 243 | `llvm-cov` provides many options, including emitting an pretty HTML file, but is |
| 244 | remarkably slow at producing easily machine-parsable data. Fortunately the core |
| 245 | of `llvm-cov` is [a few hundreds of lines of code](https://github.com/llvm/llvm-project/tree/master/llvm/tools/llvm-cov), as it relies on LLVM libraries to do the heavy lifting. Regres |
Nicolas Capens | 0e62619 | 2020-07-30 21:49:45 -0400 | [diff] [blame] | 246 | replaces `llvm-cov` with ["`turbo-cov`"](https://cs.opensource.google/swiftshader/SwiftShader/+/master:tests/regres/cov/turbo-cov/) which efficiently converts a `.profdata` into a simple binary stream which can |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 247 | be consumed by Regres. |
| 248 | |
| 249 | #### Processing |
| 250 | |
| 251 | At the time of writing there are over 560,000 individual dEQP tests, and around |
Nicolas Capens | 0e62619 | 2020-07-30 21:49:45 -0400 | [diff] [blame] | 252 | 176,000 lines of C++ code in [`<swiftshader>/src`](https://cs.opensource.google/swiftshader/SwiftShader/+/master:src/). |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 253 | If you used 1 bit for each source line, per-line source coverage for all dEQP |
| 254 | tests would require over 11GiB of storage. That's just for one snapshot. |
| 255 | |
| 256 | The processing and compression schemes described below reduces this down to |
| 257 | around 10 MiB (~1100x reduction in size), and supports sub-line coverage scopes. |
| 258 | |
| 259 | ##### Spans |
| 260 | |
| 261 | Code coverage information is described in spans. |
| 262 | |
| 263 | A span is a described as an interval of source locations, where a location is a |
| 264 | line-column pair: |
| 265 | |
| 266 | ```go |
| 267 | type Location struct { |
| 268 | Line, Column int |
| 269 | } |
| 270 | |
| 271 | type Span struct { |
| 272 | Start, End Location |
| 273 | } |
| 274 | ``` |
| 275 | |
| 276 | ##### Test tree construction |
| 277 | |
| 278 | Each dEQP test is uniquely identified by a fully qualified name. |
| 279 | Each test belongs to a group, and that group may be nested within any number of |
| 280 | parent groups. The groups are described in the test name, using dots (`.`) to |
| 281 | delimit the groups and leaf test name. |
| 282 | |
| 283 | For example, the fully qualified test name: |
| 284 | |
| 285 | `dEQP-VK.fragment_shader_interlock.basic.discard.ssbo.sample_unordered.4xaa.sample_shading.16x16` |
| 286 | |
| 287 | Can be broken down into the following groups and test name: |
| 288 | |
| 289 | ```text |
| 290 | dEQP-VK <-- root group name |
| 291 | ╰ fragment_shader_interlock |
| 292 | ╰ basic.discard |
| 293 | ╰ ssbo |
| 294 | ╰ sample_unordered |
| 295 | ╰ 4xaa |
| 296 | ╰ sample_shading |
| 297 | ╰ 16x16 <-- leaf test name |
| 298 | ``` |
| 299 | |
| 300 | Breaking down fully qualified test names into groups provide a natural way to |
| 301 | structure coverage data, as tests of the same group are likely to have similar |
| 302 | coverage spans. |
| 303 | |
| 304 | So, for each source file in the codebase, we create a tree with test groups as |
| 305 | non-leaf nodes, and tests as leaf nodes. |
| 306 | |
| 307 | For example, given the following test list: |
| 308 | |
| 309 | ```text |
| 310 | a.b.d.h |
| 311 | a.b.d.i.n |
| 312 | a.b.d.i.o |
| 313 | a.b.e.j |
| 314 | a.b.e.k.p |
| 315 | a.b.e.k.q |
| 316 | a.c.f |
| 317 | a.c.g.l.r |
| 318 | a.c.g.m |
| 319 | ``` |
| 320 | |
| 321 | We would construct the following tree: |
| 322 | |
| 323 | ```text |
| 324 | a |
| 325 | ╭──────┴──────╮ |
| 326 | b c |
| 327 | ╭───┴───╮ ╭───┴───╮ |
| 328 | d e f g |
| 329 | ╭─┴─╮ ╭─┴─╮ ╭─┴─╮ |
| 330 | h i j k l m |
| 331 | ╭┴╮ ╭┴╮ │ |
| 332 | n o p q r |
| 333 | |
| 334 | ``` |
| 335 | |
| 336 | Each leaf node in this tree (`h`, `n`, `o`, `j`, `p`, `q`, `f`, `r`, `m`) |
| 337 | represent a test, and non-leaf nodes (`a`, `b`, `c`, `d`, `e`, `g`, `i`, `k`, |
| 338 | `l`) are a groups. |
| 339 | |
| 340 | To begin, we create a test tree structure, and associate the full list of test |
| 341 | coverage spans with every leaf node (test) in this tree. |
| 342 | |
| 343 | This data structure hasn't given us any compression benefits yet, but we can |
| 344 | now do a few tricks to dramatically reduce number of spans needed to describe |
| 345 | the graph: |
| 346 | |
| 347 | ##### Optimization 1: Common span promotion |
| 348 | |
| 349 | The first compression scheme is to promote common spans up the tree when they |
| 350 | are common for all children. This will reduce the number of spans needed to be |
| 351 | encoded in the final file. |
| 352 | |
| 353 | For example, if the test group `a` has 4 children that all share the same span |
| 354 | `X`: |
| 355 | |
| 356 | ```text |
| 357 | a |
| 358 | ╭───┬─┴─┬───╮ |
| 359 | b c d e |
| 360 | [X,Y] [X] [X] [X,Z] |
| 361 | ``` |
| 362 | |
| 363 | Then span `X` can be promoted up to `a`: |
| 364 | |
| 365 | ```text |
| 366 | [X] |
| 367 | a |
| 368 | ╭───┬─┴─┬───╮ |
| 369 | b c d e |
| 370 | [Y] [] [] [Z] |
| 371 | ``` |
| 372 | |
| 373 | ##### Optimization 2: Span XOR promotion |
| 374 | |
| 375 | This idea can be extended further, by not requiring all the children to share |
| 376 | the same span before promotion. If **most** child nodes share the same span, we |
| 377 | can still promote the span, but this time we **remove** the span from the |
| 378 | children **if they had it**, and **add** the span to children **if they didn't |
| 379 | have it**. |
| 380 | |
| 381 | For example, if the test group `a` has 4 children with 3 that share the span |
| 382 | `X`: |
| 383 | |
| 384 | ```text |
| 385 | a |
| 386 | ╭───┬─┴─┬───╮ |
| 387 | b c d e |
| 388 | [X,Y] [X] [] [X,Z] |
| 389 | ``` |
| 390 | |
| 391 | Then span `X` can be promoted up to `a` by flipping the presence of `X` on the |
| 392 | child nodes: |
| 393 | |
| 394 | ```text |
| 395 | [X] |
| 396 | a |
| 397 | ╭───┬─┴─┬───╮ |
| 398 | b c d e |
| 399 | [Y] [] [X] [Z] |
| 400 | ``` |
| 401 | |
| 402 | This process repeats up the tree. |
| 403 | |
| 404 | With this optimization applied, we now need to traverse the tree from root to |
| 405 | leaf in order to know whether a given span is in use for the leaf node (test): |
| 406 | |
| 407 | * If the span is encountered an **odd** number of times during traversal, then |
| 408 | the span is **covered**. |
| 409 | * If the span is encountered an **even** number of times during traversal, then |
| 410 | the span is **not covered**. |
| 411 | |
Nicolas Capens | 0e62619 | 2020-07-30 21:49:45 -0400 | [diff] [blame] | 412 | See [`tests/regres/cov/coverage_test.go`](https://cs.opensource.google/swiftshader/SwiftShader/+/master:tests/regres/cov/coverage_test.go) for more examples of this optimization. |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 413 | |
| 414 | ##### Optimization 3: Common span grouping |
| 415 | |
| 416 | With real world data, we encounter groups of spans that are commonly found |
| 417 | together. To further reduce coverage data, the whole graph is scanned for common |
| 418 | span patterns, and are indexed by each tree node. |
| 419 | The XOR'ing of spans as described above is performed as if the spans were not |
| 420 | grouped. |
| 421 | |
| 422 | ##### Optimization 4: Lookup tables |
| 423 | |
| 424 | All spans, span-groups and strings are stored in de-duplicated tables, and are |
| 425 | indexed wherever possible. |
| 426 | |
Nicolas Capens | 0e62619 | 2020-07-30 21:49:45 -0400 | [diff] [blame] | 427 | The final serialization is performed by [`tests/regres/cov/serialization.go`](https://cs.opensource.google/swiftshader/SwiftShader/+/master:tests/regres/cov/serialization.go). |
Ben Clayton | 0a8f44c | 2020-07-15 17:06:13 +0100 | [diff] [blame] | 428 | |
| 429 | ##### Optimization 5: zlib compression |
| 430 | |
| 431 | The coverage data is encoded into JSON for parsing by the web page. |
| 432 | |
| 433 | Before writing the JSON file, the text data is zlib compressed. |
| 434 | |
| 435 | #### Presentation |
| 436 | |
| 437 | The zlib-compressed JSON coverage data is decompressed using |
| 438 | [`pako`](https://github.com/nodeca/pako), and consumed by some |
| 439 | [vanilla JavaScript](https://github.com/swiftshader-regres/swiftshader-coverage/blob/gh-pages/index.html). |
| 440 | |
| 441 | [`codemirror`](https://codemirror.net/) is used to perform coverage span and C++ |
| 442 | syntax highlighting |