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/Android.bp b/trusty/coverage/Android.bp
index a4adf81..3d2caa6 100644
--- a/trusty/coverage/Android.bp
+++ b/trusty/coverage/Android.bp
@@ -42,4 +42,5 @@
         "libbase",
         "liblog",
     ],
+    require_root: true,
 }
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
diff --git a/trusty/coverage/coverage_test.cpp b/trusty/coverage/coverage_test.cpp
index d8df7a4..c1efca6 100644
--- a/trusty/coverage/coverage_test.cpp
+++ b/trusty/coverage/coverage_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 #include <trusty/coverage/coverage.h>
 #include <trusty/tipc.h>
@@ -27,6 +28,7 @@
 
 #define TIPC_DEV "/dev/trusty-ipc-dev0"
 #define TEST_SRV_PORT "com.android.trusty.sancov.test.srv"
+#define TEST_SRV_MODULE "srv.syms.elf"
 
 namespace android {
 namespace trusty {
@@ -54,8 +56,8 @@
 };
 
 TEST_F(CoverageTest, CoverageReset) {
-    record_->Reset();
-    auto counter = record_->CountEdges();
+    record_->ResetFullRecord();
+    auto counter = record_->TotalEdgeCounts();
     ASSERT_EQ(counter, 0);
 }
 
@@ -69,7 +71,7 @@
 
     for (size_t i = 1; i < sizeof(magic) * 8; i++) {
         /* Reset coverage */
-        record_->Reset();
+        record_->ResetCounts();
 
         /* Send message to test server */
         uint32_t msg = magic & ~(mask << i);
@@ -81,10 +83,15 @@
         ASSERT_EQ(rc, sizeof(msg));
 
         /* Count number of non-unique blocks executed */
-        auto counter = record_->CountEdges();
+        auto counter = record_->TotalEdgeCounts();
         /* Each consecutive input should exercise more or same blocks */
         ASSERT_GE(counter, high_watermark);
         high_watermark = counter;
+
+        auto sancov_filename = android::base::StringPrintf(
+                "/data/local/tmp/" TEST_SRV_MODULE ".%d.sancov", getpid());
+        auto res = record_->SaveSancovFile(sancov_filename);
+        ASSERT_TRUE(res.ok());
     }
 
     ASSERT_GT(high_watermark, 0);
diff --git a/trusty/coverage/include/trusty/coverage/coverage.h b/trusty/coverage/include/trusty/coverage/coverage.h
index b61b959..b6d46eb 100644
--- a/trusty/coverage/include/trusty/coverage/coverage.h
+++ b/trusty/coverage/include/trusty/coverage/coverage.h
@@ -35,13 +35,26 @@
     CoverageRecord(std::string tipc_dev, struct uuid* uuid);
     ~CoverageRecord();
     Result<void> Open();
-    void Reset();
+    void ResetFullRecord();
+    void ResetCounts();
+    void ResetPCs();
     void GetRawData(volatile void** begin, volatile void** end);
-    uint64_t CountEdges();
+    void GetRawCounts(volatile uint8_t** begin, volatile uint8_t** end);
+    void GetRawPCs(volatile uintptr_t** begin, volatile uintptr_t** end);
+    uint64_t TotalEdgeCounts();
+
+    /**
+     * Save the current set of observed PCs to the given filename.
+     * The resulting .sancov file can be parsed via the LLVM sancov tool to see
+     * coverage statistics and visualize coverage.
+     */
+    Result<void> SaveSancovFile(const std::string& filename);
 
   private:
     Result<void> Rpc(coverage_client_req* req, int req_fd, coverage_client_resp* resp);
 
+    Result<std::pair<size_t, size_t>> GetRegionBounds(uint32_t region_type);
+
     std::string tipc_dev_;
     unique_fd coverage_srv_fd_;
     struct uuid uuid_;
diff --git a/trusty/coverage/include/trusty/coverage/record.h b/trusty/coverage/include/trusty/coverage/record.h
new file mode 100644
index 0000000..bfe06f3
--- /dev/null
+++ b/trusty/coverage/include/trusty/coverage/record.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file needs to be kept in-sync with its counterpart on Trusty side:
+ * trusty/user/base/lib/coverage/common/include/lib/coverage/common/record.h */
+
+#pragma once
+
+#include <stdint.h>
+
+/**
+ * enum coverage_record_type - Coverage region header type
+ * @COV_START: Magic header start marker
+ * @COV_8BIT_COUNTERS: 8bit counter for each instrumentation point
+ * @COV_INSTR_PCS: Pointer length offset of each instrumentation point from the
+ *                 start of the binary
+ * @COV_TOTAL_LENGTH: Total length of the entire coverage record, must be the
+ *                    last header item.
+ *
+ * Describes the type of a region of the coverage record. See &struct
+ * coverage_record_header.
+ */
+enum coverage_record_type {
+    COV_START = 0x434f5652,
+    COV_8BIT_COUNTERS = 1,
+    COV_INSTR_PCS = 2,
+    COV_TOTAL_LENGTH = 0,
+};
+
+/**
+ * struct coverage_record_header - Header entry describing a region of the
+ * coverage record.
+ * @type: type of the region, must be one of @enum coverage_record_type
+ * @offset: offset from the beginning of the header to the start of the region
+ *
+ * Coverage records start with a header which is a list of struct
+ * coverage_record_header, beginning with an entry with type COV_START and
+ * terminated with an entry with type COV_TOTAL_LENGTH. Each of these header
+ * entries corresponds to a region of the record, with the offset indicating the
+ * offset of the start of that region from the beginning of the record (i.e. the
+ * beginning of the header). Each record type and offset is 32-bit field with
+ * native endianness. The first header item must be COV_START with a 0 offset.
+ * The COV_START entry should be initialized when the coverage header is
+ * complete and ready for consumption by the client, because coverage record
+ * initialization happens asynchronously. The final header item,
+ * COV_TOTAL_LENGTH, which must always be present, indicates the total length of
+ * the coverage record, including the header.
+ *
+ * Coverage regions should be contiguous, so the end of one region is the start
+ * of the next, and the coverage header must be in the same order as the regions
+ * in the record body. Thus we can compute the length of a region by subtracting
+ * the region's offset from the offset of the next header item.
+ */
+struct coverage_record_header {
+    uint32_t type;
+    uint32_t offset;
+};
diff --git a/trusty/fuzz/counters.cpp b/trusty/fuzz/counters.cpp
index 3fc9f48..8c79475 100644
--- a/trusty/fuzz/counters.cpp
+++ b/trusty/fuzz/counters.cpp
@@ -42,9 +42,9 @@
     assert(fuzzer::ExtraCountersBegin());
     assert(fuzzer::ExtraCountersEnd());
 
-    uint8_t* begin = NULL;
-    uint8_t* end = NULL;
-    record_->GetRawData((volatile void**)&begin, (volatile void**)&end);
+    volatile uint8_t* begin = NULL;
+    volatile uint8_t* end = NULL;
+    record_->GetRawCounts(&begin, &end);
     assert(end - begin <= sizeof(counters));
 }
 
@@ -53,7 +53,7 @@
 }
 
 void ExtraCounters::Reset() {
-    record_->Reset();
+    record_->ResetCounts();
     fuzzer::ClearExtraCounters();
 }
 
@@ -61,7 +61,7 @@
     volatile uint8_t* begin = NULL;
     volatile uint8_t* end = NULL;
 
-    record_->GetRawData((volatile void**)&begin, (volatile void**)&end);
+    record_->GetRawCounts(&begin, &end);
 
     size_t num_counters = end - begin;
     for (size_t i = 0; i < num_counters; i++) {