trusty: Retrieve coverage PCs from coverage record
Adds the ability to retrieve and save program counter information from
the trusty coverage record data. PC information is saved to a .sancov
file, parseable by the LLVM sancov tool. Sancov can then symbolize and
display this coverage information for consumption by humans.
Adds a sancov dump to the libtrusty_coverage_test for testing.
Bug: 175221942
Test: atest libtrusty_coverage_test
Test: Retrieve sancov file and manually symbolize with sancov
Change-Id: I342ea2ca9abb87986b2904ff69415544ee6070fc
diff --git a/trusty/coverage/coverage.cpp b/trusty/coverage/coverage.cpp
index 1162f42..9413ce3 100644
--- a/trusty/coverage/coverage.cpp
+++ b/trusty/coverage/coverage.cpp
@@ -16,12 +16,15 @@
#define LOG_TAG "coverage"
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <assert.h>
+#include <stdio.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include <trusty/coverage/coverage.h>
+#include <trusty/coverage/record.h>
#include <trusty/coverage/tipc.h>
#include <trusty/tipc.h>
@@ -137,12 +140,59 @@
return {};
}
-void CoverageRecord::Reset() {
- for (size_t i = 0; i < shm_len_; i++) {
+void CoverageRecord::ResetFullRecord() {
+ auto header_region = GetRegionBounds(COV_START);
+ if (!header_region) {
+ // If the header cannot be parsed, we can't reset the proper region yet.
+ return;
+ }
+
+ for (size_t i = header_region->second; i < shm_len_; i++) {
*((volatile uint8_t*)shm_ + i) = 0;
}
}
+void CoverageRecord::ResetCounts() {
+ volatile uint8_t* begin = nullptr;
+ volatile uint8_t* end = nullptr;
+ GetRawCounts(&begin, &end);
+
+ for (volatile uint8_t* x = begin; x < end; x++) {
+ *x = 0;
+ }
+}
+
+void CoverageRecord::ResetPCs() {
+ volatile uintptr_t* begin = nullptr;
+ volatile uintptr_t* end = nullptr;
+ GetRawPCs(&begin, &end);
+
+ for (volatile uintptr_t* x = begin; x < end; x++) {
+ *x = 0;
+ }
+}
+
+Result<std::pair<size_t, size_t>> CoverageRecord::GetRegionBounds(uint32_t region_type) {
+ assert(shm_);
+
+ auto header = (volatile struct coverage_record_header*)shm_;
+
+ if (header->type != COV_START) {
+ return Error() << "Header not yet valid";
+ }
+
+ for (++header; header->type != COV_TOTAL_LENGTH; ++header) {
+ if (header->type == region_type) {
+ // Coverage record must end with a COV_TOTAL_LENGTH header entry, so
+ // it is always safe to read the next entry since we don't iterate
+ // over the COV_TOTAL_LENGTH entry.
+ return {{header->offset, (header + 1)->offset}};
+ }
+ }
+
+ return Error() << "Could not find coverage region type: " << region_type;
+}
+
void CoverageRecord::GetRawData(volatile void** begin, volatile void** end) {
assert(shm_);
@@ -150,7 +200,35 @@
*end = (uint8_t*)(*begin) + record_len_;
}
-uint64_t CoverageRecord::CountEdges() {
+void CoverageRecord::GetRawCounts(volatile uint8_t** begin, volatile uint8_t** end) {
+ auto region = GetRegionBounds(COV_8BIT_COUNTERS);
+ if (!region) {
+ *begin = 0;
+ *end = 0;
+ return;
+ }
+
+ assert(region->second <= record_len_);
+
+ *begin = (volatile uint8_t*)shm_ + region->first;
+ *end = (volatile uint8_t*)shm_ + region->second;
+}
+
+void CoverageRecord::GetRawPCs(volatile uintptr_t** begin, volatile uintptr_t** end) {
+ auto region = GetRegionBounds(COV_INSTR_PCS);
+ if (!region) {
+ *begin = 0;
+ *end = 0;
+ return;
+ }
+
+ assert(region->second <= record_len_);
+
+ *begin = (volatile uintptr_t*)((volatile uint8_t*)shm_ + region->first);
+ *end = (volatile uintptr_t*)((volatile uint8_t*)shm_ + region->second);
+}
+
+uint64_t CoverageRecord::TotalEdgeCounts() {
assert(shm_);
uint64_t counter = 0;
@@ -158,7 +236,7 @@
volatile uint8_t* begin = NULL;
volatile uint8_t* end = NULL;
- GetRawData((volatile void**)&begin, (volatile void**)&end);
+ GetRawCounts(&begin, &end);
for (volatile uint8_t* x = begin; x < end; x++) {
counter += *x;
@@ -167,6 +245,35 @@
return counter;
}
+Result<void> CoverageRecord::SaveSancovFile(const std::string& filename) {
+ android::base::unique_fd output_fd(TEMP_FAILURE_RETRY(creat(filename.c_str(), 00644)));
+ if (!output_fd.ok()) {
+ return ErrnoError() << "Could not open sancov file";
+ }
+
+ uint64_t magic;
+ if (sizeof(uintptr_t) == 8) {
+ magic = 0xC0BFFFFFFFFFFF64;
+ } else if (sizeof(uintptr_t) == 4) {
+ magic = 0xC0BFFFFFFFFFFF32;
+ }
+ WriteFully(output_fd, &magic, sizeof(magic));
+
+ volatile uintptr_t* begin = nullptr;
+ volatile uintptr_t* end = nullptr;
+
+ GetRawPCs(&begin, &end);
+
+ for (volatile uintptr_t* pc_ptr = begin; pc_ptr < end; pc_ptr++) {
+ uintptr_t pc = *pc_ptr;
+ if (pc) {
+ WriteFully(output_fd, &pc, sizeof(pc));
+ }
+ }
+
+ return {};
+}
+
} // namespace coverage
} // namespace trusty
} // namespace android