Resolve wasm traps to unit source

This commit is contained in:
udo
2026-07-18 10:58:08 +00:00
parent eda124b15f
commit 495a8ae5f0
13 changed files with 338 additions and 17 deletions
+136 -3
View File
@@ -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) ------