| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| #include "sw/device/lib/testing/test_coverage.h" | |
| #include <stdint.h> | |
| #include "sw/device/lib/runtime/log.h" | |
| #include "sw/device/lib/uart.h" | |
| #include "sw/vendor/llvm_clang_rt_profile/compiler-rt/lib/profile/InstrProfiling.h" | |
| /** | |
| * When the linker finds a definition of this symbol, it knows to skip loading | |
| * the object which contains the profiling runtime's static initializer. See | |
| * https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#using-the-profiling-runtime-without-static-initializers | |
| * for more information. | |
| */ | |
| int __llvm_profile_runtime; | |
| /** | |
| * Computes the CRC32 of a buffer as expected by Python's `zlib.crc32()`. The | |
| * implementation below is basically a simplified, i.e. byte-by-byte and without | |
| * a lookup table, version of zlib's crc32. See | |
| * https://github.com/madler/zlib/blob/2fa463bacfff79181df1a5270fb67cc679a53e71/crc32.c, | |
| * lines 111-112 and 276-279. | |
| */ | |
| static uint32_t crc32(uint8_t *buf, size_t len) { | |
| // CRC32 polynomial. | |
| static const uint32_t kPoly = 0xEDB88320; | |
| // Since we use a contiguous buffer, we don't need to call this function | |
| // multiple times. That's why `crc` is not a parameter and is initialized to | |
| // `UINT32_MAX`, i.e. `~0`. | |
| uint32_t crc = UINT32_MAX; | |
| for (size_t i = 0; i < len; ++i) { | |
| crc ^= buf[i]; | |
| for (uint8_t j = 0; j < 8; ++j) { | |
| bool lsb = crc & 1; | |
| crc >>= 1; | |
| if (lsb) { | |
| crc ^= kPoly; | |
| } | |
| } | |
| } | |
| return ~crc; | |
| } | |
| /** | |
| * Sends the given buffer as a hex string over UART. | |
| */ | |
| static void send_buffer(uint8_t *buf, size_t len) { | |
| for (uint32_t i = 0; i < len; ++i) { | |
| base_printf("%2X", buf[i]); | |
| } | |
| } | |
| void test_coverage_send_buffer(void) { | |
| // It looks like we don't have a way to read the profile buffer incrementally. | |
| // Thus, we define the following buffer. | |
| uint8_t buf[8192] = {0}; | |
| // Write the profile buffer to the buffer defined above. | |
| uint32_t buf_size = (uint32_t)__llvm_profile_get_size_for_buffer(); | |
| if (buf_size > sizeof(buf)) { | |
| LOG_ERROR("ERROR: LLVM profile buffer is too large: %u bytes.", buf_size); | |
| } else { | |
| __llvm_profile_write_buffer((char *)buf); | |
| // Send the buffer along with its length and CRC32. | |
| base_printf("\r\nLLVM profile data (length: %u, CRC32: ", buf_size); | |
| uint32_t checksum = crc32(buf, buf_size); | |
| send_buffer((uint8_t *)&checksum, sizeof(checksum)); | |
| base_printf("):\r\n"); | |
| send_buffer(buf, buf_size); | |
| base_printf("\r\n"); | |
| } | |
| // Send `EOT` so that `cat` can exit. Note that this requires enabling | |
| // `icanon` using `stty`. | |
| uart_send_char(4); | |
| } |