blob: 96f6f925bdccfac493f31dba2c6c004cedb4ea1e [file] [log] [blame]
// 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);
}