Commit 923375a4 authored by Peter Ralbovsky's avatar Peter Ralbovsky Committed by Commit Bot

Integrate V8 Builtin coverage to Fuzzilli

Goal is to include coverage of builtin functions into coverage
bitmap send to Fuzzilli fuzzer. In order to do this, after each
REPRL loop, coverage data of bitmaps are retrieved from JS heap
and stored into coverage bitmap. Additionally, there is an option,
to print out statistics about how many of edges from builtin
functions were turned on by the program inputted into REPRL loop.

This commit introduces two flags:
--no-fuzzilli-enable-builtins-coverage - when enable-builtins-coverage
 turned of, builtins coverage will not be exported to fuzzilli

--fuzzilli-coverage-statistics - when turned on, d8 prints
 statistics into covlog.txt file after each loop




Change-Id: I8f9cf8dc693b952467b108c6d6bc00134125bc5f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2263154
Commit-Queue: Peter Ralbovsky <ralbovsky@google.com>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarMichael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68733}
parent d696c37d
......@@ -24,10 +24,13 @@ struct shmem_data {
struct shmem_data* shmem;
uint32_t *__edges_start, *__edges_stop;
void __sanitizer_cov_reset_edgeguards() {
uint32_t *edges_start, *edges_stop;
uint32_t builtins_start;
uint32_t builtins_edge_count;
void sanitizer_cov_reset_edgeguards() {
uint32_t N = 0;
for (uint32_t* x = __edges_start; x < __edges_stop && N < MAX_EDGES; x++)
for (uint32_t* x = edges_start; x < edges_stop && N < MAX_EDGES; x++)
*x = ++N;
}
......@@ -53,15 +56,27 @@ extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start,
}
}
__edges_start = start;
__edges_stop = stop;
__sanitizer_cov_reset_edgeguards();
edges_start = start;
edges_stop = stop;
sanitizer_cov_reset_edgeguards();
shmem->num_edges = static_cast<uint32_t>(stop - start);
builtins_start = 1 + shmem->num_edges;
printf("[COV] edge counters initialized. Shared memory: %s with %u edges\n",
shm_key, shmem->num_edges);
}
uint32_t sanitizer_cov_count_discovered_edges() {
uint32_t on_edges_counter = 0;
for (uint32_t i = 1; i < builtins_start; ++i) {
// TODO(ralbovsky): Can be optimized for fewer divisions.
if (shmem->edges[i / 8] & (1 << (i % 8))) {
++on_edges_counter;
}
}
return on_edges_counter;
}
extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
// There's a small race condition here: if this function executes in two
// threads for the same edge at the same time, the first thread might disable
......@@ -72,3 +87,34 @@ extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
shmem->edges[index / 8] |= 1 << (index % 8);
*guard = 0;
}
void cov_init_builtins_edges(uint32_t num_edges) {
if (num_edges + shmem->num_edges > MAX_EDGES) {
printf(
"[COV] Error: Insufficient amount of edges left for builtins "
"coverage.\n");
exit(-1);
}
builtins_edge_count = num_edges;
builtins_start = 1 + shmem->num_edges;
shmem->num_edges += builtins_edge_count;
printf("[COV] Additional %d edges for builtins initialized.\n", num_edges);
}
// This function is ran once per REPRL loop. In case of crash the coverage of
// crash will not be stored in shared memory. Therefore, it would be useful, if
// we could store these coverage information into shared memory in real time.
void cov_update_builtins_basic_block_coverage(
const std::vector<bool>& cov_map) {
if (cov_map.size() != builtins_edge_count) {
printf("[COV] Error: Size of builtins cov map changed.\n");
exit(-1);
}
for (uint32_t i = 0; i < cov_map.size(); ++i) {
if (cov_map[i]) {
// TODO(ralbovsky): Can be optimized for fewer divisions.
shmem->edges[(i + builtins_start) / 8] |=
(1 << ((i + builtins_start) % 8));
}
}
}
......@@ -10,6 +10,11 @@
// memory
// https://clang.llvm.org/docs/SanitizerCoverage.html
void __sanitizer_cov_reset_edgeguards();
#include <vector>
void sanitizer_cov_reset_edgeguards();
uint32_t sanitizer_cov_count_discovered_edges();
void cov_init_builtins_edges(uint32_t num_edges);
void cov_update_builtins_basic_block_coverage(const std::vector<bool>& cov_map);
#endif // V8_D8_COV_H_
......@@ -36,6 +36,7 @@
#include "src/d8/d8.h"
#include "src/debug/debug-interface.h"
#include "src/deoptimizer/deoptimizer.h"
#include "src/diagnostics/basic-block-profiler.h"
#include "src/execution/vm-state-inl.h"
#include "src/handles/maybe-handles.h"
#include "src/init/v8.h"
......@@ -2272,6 +2273,12 @@ void Shell::Initialize(Isolate* isolate, D8Console* console,
#ifdef V8_FUZZILLI
// Let the parent process (Fuzzilli) know we are ready.
if (options.fuzzilli_enable_builtins_coverage) {
cov_init_builtins_edges(static_cast<uint32_t>(
i::BasicBlockProfiler::Get()
->GetCoverageBitmap(reinterpret_cast<i::Isolate*>(isolate))
.size()));
}
char helo[] = "HELO";
if (write(REPRL_CWFD, helo, 4) != 4 || read(REPRL_CRFD, helo, 4) != 4) {
fuzzilli_reprl = false;
......@@ -3362,6 +3369,14 @@ bool Shell::SetOptions(int argc, char* argv[]) {
options.cpu_profiler = true;
options.cpu_profiler_print = true;
argv[i] = nullptr;
#ifdef V8_FUZZILLI
} else if (strcmp(argv[i], "--no-fuzzilli-enable-builtins-coverage") == 0) {
options.fuzzilli_enable_builtins_coverage = false;
argv[i] = nullptr;
} else if (strcmp(argv[i], "--fuzzilli-coverage-statistics") == 0) {
options.fuzzilli_coverage_statistics = true;
argv[i] = nullptr;
#endif
} else if (strcmp(argv[i], "--fuzzy-module-file-extensions") == 0) {
options.fuzzy_module_file_extensions = true;
argv[i] = nullptr;
......@@ -4156,13 +4171,34 @@ int Shell::Main(int argc, char* argv[]) {
evaluation_context_.Reset();
stringify_function_.Reset();
CollectGarbage(isolate);
#ifdef V8_FUZZILLI
// Send result to parent (fuzzilli) and reset edge guards.
if (fuzzilli_reprl) {
int status = result << 8;
std::vector<bool> bitmap;
if (options.fuzzilli_enable_builtins_coverage) {
bitmap = i::BasicBlockProfiler::Get()->GetCoverageBitmap(
reinterpret_cast<i::Isolate*>(isolate));
cov_update_builtins_basic_block_coverage(bitmap);
}
if (options.fuzzilli_coverage_statistics) {
int tot = 0;
for (bool b : bitmap) {
if (b) tot++;
}
static int iteration_counter = 0;
std::ofstream covlog("covlog.txt", std::ios::app);
covlog << iteration_counter << "\t" << tot << "\t"
<< sanitizer_cov_count_discovered_edges() << "\t"
<< bitmap.size() << std::endl;
iteration_counter++;
}
CHECK_EQ(write(REPRL_CWFD, &status, 4), 4);
__sanitizer_cov_reset_edgeguards();
sanitizer_cov_reset_edgeguards();
if (options.fuzzilli_enable_builtins_coverage) {
i::BasicBlockProfiler::Get()->ResetCounts(
reinterpret_cast<i::Isolate*>(isolate));
}
}
#endif // V8_FUZZILLI
} while (fuzzilli_reprl);
......
......@@ -292,6 +292,8 @@ class ShellOptions {
~ShellOptions() { delete[] isolate_sources; }
bool fuzzilli_coverage_statistics = false;
bool fuzzilli_enable_builtins_coverage = true;
bool send_idle_notification = false;
bool invoke_weak_callbacks = false;
bool omit_quit = false;
......
......@@ -78,6 +78,22 @@ BasicBlockProfilerData::BasicBlockProfilerData(
CHECK_EQ(block_rpo_numbers_.size(), counts_.size());
}
BasicBlockProfilerData::BasicBlockProfilerData(
OnHeapBasicBlockProfilerData js_heap_data) {
function_name_ = js_heap_data.name().ToCString().get();
schedule_ = js_heap_data.schedule().ToCString().get();
code_ = js_heap_data.code().ToCString().get();
ByteArray counts(js_heap_data.counts());
for (int i = 0; i < counts.length() / kBasicBlockSlotSize; ++i) {
counts_.push_back(counts.get_uint32(i));
}
ByteArray rpo_numbers(js_heap_data.block_rpo_numbers());
for (int i = 0; i < rpo_numbers.length() / kBasicBlockSlotSize; ++i) {
block_rpo_numbers_.push_back(rpo_numbers.get_int(i));
}
CHECK_EQ(block_rpo_numbers_.size(), counts_.size());
}
Handle<OnHeapBasicBlockProfilerData> BasicBlockProfilerData::CopyToJSHeap(
Isolate* isolate) {
int array_size_in_bytes = static_cast<int>(n_blocks() * kBasicBlockSlotSize);
......@@ -140,6 +156,21 @@ void BasicBlockProfiler::Print(std::ostream& os, Isolate* isolate) {
os << "---- End Profiling Data ----" << std::endl;
}
std::vector<bool> BasicBlockProfiler::GetCoverageBitmap(Isolate* isolate) {
DisallowHeapAllocation no_gc;
ArrayList list(isolate->heap()->basic_block_profiling_data());
std::vector<bool> out;
int list_length = list.Length();
for (int i = 0; i < list_length; ++i) {
BasicBlockProfilerData data(
OnHeapBasicBlockProfilerData::cast(list.Get(i)));
for (size_t i = 0; i < data.n_blocks(); ++i) {
out.push_back(data.counts_[i] > 0);
}
}
return out;
}
std::ostream& operator<<(std::ostream& os, const BasicBlockProfilerData& d) {
int block_count_sum = std::accumulate(d.counts_.begin(), d.counts_.end(), 0);
if (block_count_sum == 0) return os;
......
......@@ -24,6 +24,8 @@ class BasicBlockProfilerData {
explicit BasicBlockProfilerData(size_t n_blocks);
V8_EXPORT_PRIVATE BasicBlockProfilerData(
Handle<OnHeapBasicBlockProfilerData> js_heap_data, Isolate* isolate);
V8_EXPORT_PRIVATE BasicBlockProfilerData(
OnHeapBasicBlockProfilerData js_heap_data);
size_t n_blocks() const {
DCHECK_EQ(block_rpo_numbers_.size(), counts_.size());
......@@ -69,6 +71,11 @@ class BasicBlockProfiler {
V8_EXPORT_PRIVATE bool HasData(Isolate* isolate);
V8_EXPORT_PRIVATE void Print(std::ostream& os, Isolate* isolate);
// Coverage bitmap in this context includes only on heap BasicBlockProfiler
// data It is used to export coverage of builtins function loaded from
// snapshot.
V8_EXPORT_PRIVATE std::vector<bool> GetCoverageBitmap(Isolate* isolate);
const DataList* data_list() { return &data_list_; }
private:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment