Resolve wasm traps to unit source
This commit is contained in:
+136
-3
@@ -69,9 +69,24 @@ struct WasmAbiInfo
|
||||
{
|
||||
u32 version = 0;
|
||||
String toolchain;
|
||||
String module_name;
|
||||
bool found = false;
|
||||
};
|
||||
|
||||
struct WasmSourceMap
|
||||
{
|
||||
struct Row
|
||||
{
|
||||
u64 address = 0;
|
||||
u32 file = 0;
|
||||
u32 line = 0;
|
||||
u32 column = 0;
|
||||
};
|
||||
String module_name;
|
||||
std::map<u32, String> files;
|
||||
std::vector<Row> rows;
|
||||
};
|
||||
|
||||
enum class WasmUnitImportKind
|
||||
{
|
||||
Memory,
|
||||
@@ -725,6 +740,8 @@ static bool wasm_parse_sections(const std::vector<u8>& bytes, WasmDylinkInfo& dy
|
||||
line_start = line_end + 1;
|
||||
}
|
||||
}
|
||||
else if(name == "uce.module")
|
||||
abi.module_name.assign((const char*)bytes.data() + cursor, end - cursor);
|
||||
}
|
||||
pos = end;
|
||||
}
|
||||
@@ -748,6 +765,53 @@ static bool wasm_read_file(const String& path, std::vector<u8>& out)
|
||||
return((bool)in);
|
||||
}
|
||||
|
||||
static bool wasm_source_map_load(const String& path, WasmSourceMap& map)
|
||||
{
|
||||
std::ifstream input(path);
|
||||
if(!input)
|
||||
return(false);
|
||||
String line;
|
||||
if(!std::getline(input, line) || line.rfind("UCE_SOURCE_MAP_V1\t", 0) != 0)
|
||||
return(false);
|
||||
map.module_name = line.substr(18);
|
||||
while(std::getline(input, line))
|
||||
{
|
||||
auto fields = split(line, "\t");
|
||||
if(fields.size() == 3 && fields[0] == "F")
|
||||
map.files[(u32)strtoul(fields[1].c_str(), 0, 10)] = fields[2];
|
||||
else if(fields.size() == 5 && fields[0] == "L")
|
||||
{
|
||||
WasmSourceMap::Row row;
|
||||
row.address = strtoull(fields[1].c_str(), 0, 16);
|
||||
row.file = (u32)strtoul(fields[2].c_str(), 0, 10);
|
||||
row.line = (u32)strtoul(fields[3].c_str(), 0, 10);
|
||||
row.column = (u32)strtoul(fields[4].c_str(), 0, 10);
|
||||
map.rows.push_back(row);
|
||||
}
|
||||
}
|
||||
return(!map.rows.empty());
|
||||
}
|
||||
|
||||
static String wasm_source_map_lookup(const WasmSourceMap& map, u64 address)
|
||||
{
|
||||
const WasmSourceMap::Row* found = 0;
|
||||
for(auto& row : map.rows)
|
||||
{
|
||||
if(row.address > address)
|
||||
break;
|
||||
found = &row;
|
||||
}
|
||||
if(!found || found->line == 0)
|
||||
return("");
|
||||
auto file = map.files.find(found->file);
|
||||
if(file == map.files.end())
|
||||
return("");
|
||||
String result = file->second + ":" + std::to_string(found->line);
|
||||
if(found->column)
|
||||
result += ":" + std::to_string(found->column);
|
||||
return(result);
|
||||
}
|
||||
|
||||
static bool wasm_pread_all(int fd, u64 offset, u8* out, size_t size, u64& bytes_read)
|
||||
{
|
||||
size_t done = 0;
|
||||
@@ -858,7 +922,7 @@ static bool wasm_read_metadata_file(const String& path, std::vector<u8>& metadat
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(name == "dylink.0" || name == "uce.abi")
|
||||
if(name == "dylink.0" || name == "uce.abi" || name == "uce.module")
|
||||
{
|
||||
if(section_size > 1024 * 1024)
|
||||
{
|
||||
@@ -1771,9 +1835,78 @@ private:
|
||||
return(pos == String::npos ? String("") : path.substr(0, pos));
|
||||
}
|
||||
|
||||
static String trap_text(const wasmtime::TrapError& error)
|
||||
String trap_text(const wasmtime::TrapError& error)
|
||||
{
|
||||
return(wasm_trace_collapse(String(error.message())));
|
||||
String result = wasm_trace_collapse(String(error.message()));
|
||||
struct Frame
|
||||
{
|
||||
String module;
|
||||
String function;
|
||||
u64 offset = 0;
|
||||
};
|
||||
std::vector<Frame> frames;
|
||||
auto collect = [&](const wasmtime::Trace& trace) {
|
||||
if(trace.size() == 0)
|
||||
return;
|
||||
for(auto& frame : trace)
|
||||
{
|
||||
Frame item;
|
||||
if(auto name = frame.module_name())
|
||||
item.module = String(*name);
|
||||
if(auto name = frame.func_name())
|
||||
item.function = wasm_trace_demangle(String(*name));
|
||||
item.offset = frame.module_offset();
|
||||
frames.push_back(item);
|
||||
}
|
||||
};
|
||||
if(auto* trap = std::get_if<wasmtime::Trap>(&error.data))
|
||||
{
|
||||
auto trace = trap->trace();
|
||||
collect(trace);
|
||||
}
|
||||
else if(auto* runtime_error = std::get_if<wasmtime::Error>(&error.data))
|
||||
{
|
||||
auto trace = runtime_error->trace();
|
||||
collect(trace);
|
||||
}
|
||||
|
||||
std::map<String, WasmSourceMap> maps;
|
||||
std::set<String> unavailable;
|
||||
std::vector<String> locations;
|
||||
for(size_t index = 0; index < frames.size() && locations.size() < 12; index++)
|
||||
{
|
||||
const WasmUnitModule* unit = 0;
|
||||
for(auto& loaded : units)
|
||||
if(loaded.mod->abi.module_name == frames[index].module)
|
||||
{
|
||||
unit = loaded.mod.get();
|
||||
break;
|
||||
}
|
||||
if(!unit)
|
||||
continue;
|
||||
String map_path = unit->wasm_path + ".source-map";
|
||||
if(unavailable.find(map_path) != unavailable.end())
|
||||
continue;
|
||||
auto loaded_map = maps.find(map_path);
|
||||
if(loaded_map == maps.end())
|
||||
{
|
||||
WasmSourceMap source_map;
|
||||
if(!wasm_source_map_load(map_path, source_map) || source_map.module_name != frames[index].module)
|
||||
{
|
||||
unavailable.insert(map_path);
|
||||
continue;
|
||||
}
|
||||
loaded_map = maps.emplace(map_path, std::move(source_map)).first;
|
||||
}
|
||||
String location = wasm_source_map_lookup(loaded_map->second, frames[index].offset);
|
||||
if(location == "")
|
||||
continue;
|
||||
String label = frames[index].function == "" ? "wasm function" : frames[index].function;
|
||||
locations.push_back("#" + std::to_string(index) + " " + label + " at " + location);
|
||||
}
|
||||
if(!locations.empty())
|
||||
result += "\nsource locations:\n " + join(locations, "\n ");
|
||||
return(result);
|
||||
}
|
||||
|
||||
// ---- guest memory access (pointer re-derived per call: it moves) ------
|
||||
|
||||
Reference in New Issue
Block a user