Loading trusty/coverage/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -42,4 +42,5 @@ cc_test { "libbase", "liblog", ], require_root: true, } trusty/coverage/coverage.cpp +111 −4 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -137,12 +140,59 @@ Result<void> CoverageRecord::Open() { 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_); Loading @@ -150,7 +200,35 @@ void CoverageRecord::GetRawData(volatile void** begin, volatile void** end) { *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; Loading @@ -158,7 +236,7 @@ uint64_t CoverageRecord::CountEdges() { 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; Loading @@ -167,6 +245,35 @@ uint64_t CoverageRecord::CountEdges() { 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 trusty/coverage/coverage_test.cpp +11 −4 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -27,6 +28,7 @@ using std::unique_ptr; #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 { Loading Loading @@ -54,8 +56,8 @@ class CoverageTest : public ::testing::Test { }; TEST_F(CoverageTest, CoverageReset) { record_->Reset(); auto counter = record_->CountEdges(); record_->ResetFullRecord(); auto counter = record_->TotalEdgeCounts(); ASSERT_EQ(counter, 0); } Loading @@ -69,7 +71,7 @@ TEST_F(CoverageTest, TestServerCoverage) { 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); Loading @@ -81,10 +83,15 @@ TEST_F(CoverageTest, TestServerCoverage) { 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); Loading trusty/coverage/include/trusty/coverage/coverage.h +15 −2 Original line number Diff line number Diff line Loading @@ -35,13 +35,26 @@ class CoverageRecord { 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_; Loading trusty/coverage/include/trusty/coverage/record.h 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * 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; }; Loading
trusty/coverage/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -42,4 +42,5 @@ cc_test { "libbase", "liblog", ], require_root: true, }
trusty/coverage/coverage.cpp +111 −4 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -137,12 +140,59 @@ Result<void> CoverageRecord::Open() { 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_); Loading @@ -150,7 +200,35 @@ void CoverageRecord::GetRawData(volatile void** begin, volatile void** end) { *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; Loading @@ -158,7 +236,7 @@ uint64_t CoverageRecord::CountEdges() { 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; Loading @@ -167,6 +245,35 @@ uint64_t CoverageRecord::CountEdges() { 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
trusty/coverage/coverage_test.cpp +11 −4 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -27,6 +28,7 @@ using std::unique_ptr; #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 { Loading Loading @@ -54,8 +56,8 @@ class CoverageTest : public ::testing::Test { }; TEST_F(CoverageTest, CoverageReset) { record_->Reset(); auto counter = record_->CountEdges(); record_->ResetFullRecord(); auto counter = record_->TotalEdgeCounts(); ASSERT_EQ(counter, 0); } Loading @@ -69,7 +71,7 @@ TEST_F(CoverageTest, TestServerCoverage) { 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); Loading @@ -81,10 +83,15 @@ TEST_F(CoverageTest, TestServerCoverage) { 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); Loading
trusty/coverage/include/trusty/coverage/coverage.h +15 −2 Original line number Diff line number Diff line Loading @@ -35,13 +35,26 @@ class CoverageRecord { 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_; Loading
trusty/coverage/include/trusty/coverage/record.h 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * 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; };