Skip to content

Commit c6c07ad

Browse files
authored
v8: emit backtrace when trap happens. (#66)
Signed-off-by: mathetake <takeshi@tetrate.io>
1 parent b1067e6 commit c6c07ad

File tree

1 file changed

+90
-5
lines changed

1 file changed

+90
-5
lines changed

src/v8/v8.cc

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
#include "include/proxy-wasm/v8.h"
1717

1818
#include <cassert>
19-
19+
#include <iomanip>
2020
#include <memory>
2121
#include <optional>
22+
#include <sstream>
2223
#include <utility>
2324
#include <vector>
2425

@@ -84,7 +85,9 @@ class V8 : public WasmVm {
8485
#undef _GET_MODULE_FUNCTION
8586

8687
private:
88+
void buildFunctionNameIndex();
8789
wasm::vec<byte_t> getStrippedSource();
90+
std::string getFailMessage(std::string_view function_name, wasm::own<wasm::Trap> trap);
8891

8992
template <typename... Args>
9093
void registerHostFunctionImpl(std::string_view module_name, std::string_view function_name,
@@ -112,6 +115,8 @@ class V8 : public WasmVm {
112115

113116
absl::flat_hash_map<std::string, FuncDataPtr> host_functions_;
114117
absl::flat_hash_map<std::string, wasm::own<wasm::Func>> module_functions_;
118+
119+
absl::flat_hash_map<uint32_t, std::string> function_names_index_;
115120
};
116121

117122
// Helper functions.
@@ -263,9 +268,58 @@ bool V8::load(const std::string &code, bool allow_precompiled) {
263268
assert((shared_module_ != nullptr));
264269
}
265270

271+
buildFunctionNameIndex();
272+
266273
return module_ != nullptr;
267274
}
268275

276+
void V8::buildFunctionNameIndex() {
277+
// build function index -> function name map for backtrace
278+
// https://webassembly.github.io/spec/core/appendix/custom.html#binary-namesubsection
279+
auto name_section = getCustomSection("name");
280+
if (name_section.size()) {
281+
const byte_t *pos = name_section.data();
282+
const byte_t *end = name_section.data() + name_section.size();
283+
while (pos < end) {
284+
if (*pos++ != 1) {
285+
pos += parseVarint(pos, end);
286+
} else {
287+
const auto size = parseVarint(pos, end);
288+
if (size == static_cast<uint32_t>(-1) || pos + size > end) {
289+
function_names_index_ = {};
290+
return;
291+
}
292+
const auto start = pos;
293+
const auto namemap_vector_size = parseVarint(pos, end);
294+
if (namemap_vector_size == static_cast<uint32_t>(-1) || pos + namemap_vector_size > end) {
295+
function_names_index_ = {};
296+
return;
297+
}
298+
for (auto i = 0; i < namemap_vector_size; i++) {
299+
const auto func_index = parseVarint(pos, end);
300+
if (func_index == static_cast<uint32_t>(-1)) {
301+
function_names_index_ = {};
302+
return;
303+
}
304+
305+
const auto func_name_size = parseVarint(pos, end);
306+
if (func_name_size == static_cast<uint32_t>(-1) || pos + func_name_size > end) {
307+
function_names_index_ = {};
308+
return;
309+
}
310+
function_names_index_.insert({func_index, std::string(pos, func_name_size)});
311+
pos += func_name_size;
312+
}
313+
314+
if (start + size != pos) {
315+
function_names_index_ = {};
316+
return;
317+
}
318+
}
319+
}
320+
}
321+
}
322+
269323
std::unique_ptr<WasmVm> V8::clone() {
270324
assert(shared_module_ != nullptr);
271325

@@ -274,6 +328,7 @@ std::unique_ptr<WasmVm> V8::clone() {
274328
clone->store_ = wasm::Store::make(engine());
275329

276330
clone->module_ = wasm::Module::obtain(clone->store_.get(), shared_module_.get());
331+
clone->function_names_index_ = function_names_index_;
277332

278333
return clone;
279334
}
@@ -500,6 +555,7 @@ bool V8::link(std::string_view debug_name) {
500555
} break;
501556
}
502557
}
558+
503559
return !isFailed();
504560
}
505561

@@ -618,8 +674,7 @@ void V8::getModuleFunctionImpl(std::string_view function_name,
618674
SaveRestoreContext saved_context(context);
619675
auto trap = func->call(params, nullptr);
620676
if (trap) {
621-
fail(FailState::RuntimeError, "Function: " + std::string(function_name) + " failed: " +
622-
std::string(trap->message().get(), trap->message().size()));
677+
fail(FailState::RuntimeError, getFailMessage(std::string(function_name), std::move(trap)));
623678
}
624679
};
625680
}
@@ -651,15 +706,45 @@ void V8::getModuleFunctionImpl(std::string_view function_name,
651706
SaveRestoreContext saved_context(context);
652707
auto trap = func->call(params, results);
653708
if (trap) {
654-
fail(FailState::RuntimeError, "Function: " + std::string(function_name) + " failed: " +
655-
std::string(trap->message().get(), trap->message().size()));
709+
fail(FailState::RuntimeError, getFailMessage(std::string(function_name), std::move(trap)));
656710
return R{};
657711
}
658712
R rvalue = results[0].get<typename ConvertWordTypeToUint32<R>::type>();
659713
return rvalue;
660714
};
661715
}
662716

717+
std::string V8::getFailMessage(std::string_view function_name, wasm::own<wasm::Trap> trap) {
718+
auto message = "Function: " + std::string(function_name) + " failed: ";
719+
message += std::string(trap->message().get(), trap->message().size());
720+
721+
if (function_names_index_.empty()) {
722+
return message;
723+
}
724+
725+
auto trace = trap->trace();
726+
message += "\nProxy-Wasm plugin in-VM backtrace:";
727+
for (size_t i = 0; i < trace.size(); ++i) {
728+
auto frame = trace[i].get();
729+
std::ostringstream oss;
730+
oss << std::setw(3) << std::setfill(' ') << std::to_string(i);
731+
message += "\n" + oss.str() + ": ";
732+
733+
std::stringstream address;
734+
address << std::hex << frame->module_offset();
735+
message += " 0x" + address.str() + " - ";
736+
737+
auto func_index = frame->func_index();
738+
auto it = function_names_index_.find(func_index);
739+
if (it != function_names_index_.end()) {
740+
message += it->second;
741+
} else {
742+
message += "unknown(function index:" + std::to_string(func_index) + ")";
743+
}
744+
}
745+
return message;
746+
}
747+
663748
} // namespace
664749

665750
std::unique_ptr<WasmVm> createV8Vm() { return std::make_unique<V8>(); }

0 commit comments

Comments
 (0)