some fixes & speedups

This commit is contained in:
silverweed 2025-01-24 11:04:28 +01:00
parent 299ee4e695
commit aef07b24f9
7 changed files with 153 additions and 105 deletions

View file

@ -307,7 +307,7 @@ struct Sec_Hover_Fn {
// An unspecified range of bytes
void range(const char *desc, u64 range_len, Display_Range_Fn display_val = hover_display_generic_range)
{
if (cur_field_off + range_len > section.range.end())
if (range_len == 0 || cur_field_off + range_len > section.range.end())
return;
String8_Node *dsc = display_val(arena, info.desc, desc, data + cur_field_off, range_len);
b8 hovered = cur_field_off <= off && off < cur_field_off + range_len;
@ -340,11 +340,9 @@ struct Sec_Hover_Fn {
return was_zipped;
}
// Returns the key len
u16 tkey(const char *title = "TKey")
void tkey(const char *title = "TKey")
{
u16 keylen = 0;
titled_section(title, [this, &keylen] {
titled_section(title, [this] {
u16 version_be;
if (!read(&version_be, cur_field_off + 4))
return;
@ -363,7 +361,7 @@ struct Sec_Hover_Fn {
});
field_be<u32>("Obj Len: %u");
field<u32>("Datetime: ", hover_display_datetime_str);
field_be<u16>("Key Len: %u", &keylen);
field_be<u16>("Key Len: %u");
field_be<u16>("Cycle: %u");
if (is_big) {
field_be<u64>("Seek Key: 0x%" PRIX64);
@ -376,8 +374,6 @@ struct Sec_Hover_Fn {
field_str8<u8>("Obj Name: %s");
field_str8<u8>("Obj Title: %s");
}, HoverSec_HideIfNotHovered);
return keylen;
}
void envelope_preamble()
@ -1072,6 +1068,9 @@ struct Sec_Hover_Fn {
String8 name = class_name->size ? *class_name : str8("(Unknown)");
titled_section(name.c(), [this] {
tkey();
// Not sure what this is, but sometimes we get extra bytes from the end of the key
// to the start of the actual payload.
range("???", section.range.start - cur_field_off);
range("Payload", section.range.len - section.post_size);
});
}

View file

@ -137,10 +137,30 @@ MemoryEditor make_memory_editor(App_State &app, i32 n_cols)
}
internal
void init_viewer(App_State &app, u16 n_cols)
void init_viewer(Arena *arena, App_State &app, u16 n_cols)
{
Viewer &viewer = app.viewer;
viewer.mem_edit = make_memory_editor(app, (i32)n_cols);
// Init title
if (app.tfile_data.sections[Sec_RNTuple_Anchor].head) {
Temp scratch = scratch_begin(&arena, 1);
defer { scratch_end(scratch); };
const ROOT::RNTuple &anchor = *(const ROOT::RNTuple *)app.tfile_data.sections[Sec_RNTuple_Anchor].head->info;
String8 ntpl_desc = rntuple_description(scratch.arena, anchor);
if (rntuple_is_old_version(anchor)) {
viewer.col_title[0] = 1.f;
viewer.title = push_str8f(arena, "\"%s\" (%s) from file \"%s\" ** old version, some data missing! **",
app.ntpl_name.c(), ntpl_desc.c(), app.inspected_file.name.c());
} else {
viewer.col_title[0] = viewer.col_title[1] = viewer.col_title[2] = 1.f;
viewer.title = push_str8f(arena, "\"%s\" (%s) from file \"%s\"", app.ntpl_name.c(), ntpl_desc.c(), app.inspected_file.name.c());
}
} else {
viewer.col_title[0] = viewer.col_title[1] = viewer.col_title[2] = 1.f;
viewer.title = push_str8f(arena, "(no RNTuple) from file \"%s\"", app.inspected_file.name.c());
}
#define COL(c, r, g, b) viewer.c[0] = r/255.0, viewer.c[1] = g/255.0, viewer.c[2] = b/255.0
COL(col_key, 0, 100, 50);
@ -251,7 +271,7 @@ void viewer_jump_to_page_list(App_State &app, u64 page_list_idx)
}
internal
void viewer_jump_to_section(App_State &app, Section_Id id, u64 sec_idx)
void viewer_jump_to_section(App_State &app, Section_Id id, u64 &sec_idx)
{
// special cases
if (id == Sec_Page_List) {
@ -314,17 +334,14 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms)
Temp scratch = scratch_begin(&arena, 1);
defer { scratch_end(scratch); };
printf("-----------------------\n");
const auto main_win_flags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDecoration;
if (ImGui::Begin("main", nullptr, main_win_flags)) {
// FIXME :multiple_anchors:
const ROOT::RNTuple &anchor = *(const ROOT::RNTuple *)app.tfile_data.sections[Sec_RNTuple_Anchor].head->info;
String8 ntpl_desc = rntuple_description(scratch.arena, anchor);
if (rntuple_is_old_version(anchor)) {
ImGui::TextColored(ImColor(1.f, 0.f, 0.f), "\"%s\" (%s) from file \"%s\" ** old version, some data missing! **",
app.ntpl_name.c(), ntpl_desc.c(), app.inspected_file.name.c());
} else {
ImGui::Text("\"%s\" (%s) from file \"%s\"", app.ntpl_name.c(), ntpl_desc.c(), app.inspected_file.name.c());
{
f32 *c = app.viewer.col_title;
ImGui::TextColored(ImColor(c[0], c[1], c[2]), "%s", app.viewer.title.c());
}
// Draw stats
@ -397,19 +414,24 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms)
if (ImGui::Button(sec_name.c()))
viewer_jump_to_section(app, static_cast<Section_Id>(i), app.viewer.latest_section_gone_to[i]);
ImGui::SameLine();
ImGui::Text("%s", to_pretty_size(scratch.arena, app.tfile_data.sections[i].tot_size).c());
ImGui::Text("%s", to_pretty_size(scratch.arena, app.tfile_data.sections[i].tot_size).c());
if (app.tfile_data.sections[i].count > 1) {
// } else {
// ImGui::Text("%s (%u)", to_pretty_size(scratch.arena, app.tfile_data.sections[i].tot_size).c(), app.tfile_data.sections[i].count);
ImGui::SameLine();
i64 sec_to_go_to = app.viewer.latest_section_gone_to[i];
ImGui::PushItemWidth(80.f);
ImGui::PushItemWidth(120.f);
if (ImGui::InputScalar(push_str8f(scratch.arena, "##%s_viewed", sec_name.c()).c(), ImGuiDataType_S64,
&sec_to_go_to, &step_i64, nullptr, "%u")
&& ImGui::IsItemDeactivatedAfterEdit())
{
viewer_jump_to_section(app, static_cast<Section_Id>(i), sec_to_go_to);
app.viewer.latest_section_gone_to[i] = sec_to_go_to;
viewer_jump_to_section(app, static_cast<Section_Id>(i), app.viewer.latest_section_gone_to[i]);
}
ImGui::PopItemWidth();
ImGui::SameLine();
ImGui::Text(" / %u", app.tfile_data.sections[i].count);
}
}
@ -494,7 +516,7 @@ void update_and_render(Arena *arena, App_State &app, f32 delta_time_ms)
ImGui::TextColored(ImColor(0.f, 0.65f, 0.f), "Offset: 0x%" PRIX64, hovered_off - 1);
Section hovered_section = find_section(app, hovered_off - 1);
b8 hover_display_grouped = !(app.user_input.key_state[KEY_ALT] & KEY_STATE_IS_DOWN);
b8 old_version = rntuple_is_old_version(anchor);
b8 old_version = hovered_section.from_old_version_rntuple;
Sec_Hover_Info hover_info = get_section_hover_info(scratch.arena, hovered_section, hovered_off - 1,
app.inspected_file.mem, hover_display_grouped, old_version);
ImGui::TextColored(ImColor(0.5f, 0.5f, 0.5f), "(Hint: hold Alt for single-field hover information)");

View file

@ -10,6 +10,9 @@ struct Viewer {
#ifndef RNT_NO_GFX
MemoryEditor mem_edit;
String8 title;
f32 col_title[3];
b8 highlight_cluster;
u64 highlighted_cluster;

View file

@ -19,12 +19,6 @@ void compute_tot_sections_size(Sections *sections)
}
}
internal
b8 rntuple_is_old_version(const ROOT::RNTuple &ntuple)
{
return ntuple.GetVersionEpoch() == 0 && ntuple.GetVersionMajor() < 3;
}
internal
String8 rntuple_description(Arena *arena, const ROOT::RNTuple &ntuple)
{
@ -491,7 +485,7 @@ Section find_section(App_State &app, u64 off, i64 hilite_cluster = -1)
Section sec {};
for (u32 i = 1; i < Sec_COUNT; ++i) {
for (u32 i = 1; i < Sec_COUNT_Regular; ++i) {
for (Section *sec = app.tfile_data.sections[i].head; sec; sec = sec->next) {
if (sec->range.start - sec->pre_size <= off && off < sec->range.end()) {
return *sec;
@ -499,90 +493,104 @@ Section find_section(App_State &app, u64 off, i64 hilite_cluster = -1)
}
}
u64 rblob_sz = app.tfile_data.sections[Sec_Page].head->pre_size;
// Page / PageList lookup.
// If we don't have a Sec_Page section it means we don't have any RNTuple, so skip it.
if (app.tfile_data.sections[Sec_Page].head) {
u64 rblob_sz = app.tfile_data.sections[Sec_Page].head->pre_size;
/// Page fast lookup (relative to app.last_pinfo)
{
// fast case: `off` is in the same page info as previous `off`.
for (i32 i = 0; i < 2; ++i) {
u64 pre_size = app.last_pinfo->is_first_in_cluster ? rblob_sz : 0;
if (app.last_pinfo->range.start - pre_size < off && off < app.last_pinfo->range.end()) {
/// Page fast lookup (relative to app.last_pinfo)
{
// fast case: `off` is in the same page info as previous `off`.
for (i32 i = 0; i < 2; ++i) {
u64 pre_size = app.last_pinfo->is_first_in_cluster ? rblob_sz : 0;
if (app.last_pinfo->range.start - pre_size <= off && off < app.last_pinfo->range.end()) {
sec.id = Sec_Page;
sec.range = app.last_pinfo->range;
sec.pre_size = pre_size;
sec.post_size = app.last_pinfo->checksum_size();
sec.highlighted = hilite_cluster >= 0 && app.last_pinfo->cluster_id == (u64)hilite_cluster;
sec.info = app.last_pinfo;
return sec;
}
// Try another common and still fast case: `off is in the next page info as the previous.
if (app.last_pinfo->next) // don't check if it's checksum, since it's the first byte of the page
app.last_pinfo = app.last_pinfo->next;
else
break;
}
}
// Check if we're in a page list
// @Speed: linear search through all cluster groups
for (u64 cg_idx = 0; cg_idx < rdata.n_cluster_groups; ++cg_idx) {
Cluster_Group_Info &cg_info = rdata.cluster_groups[cg_idx];
if (cg_info.rng_page_list.start - rblob_sz <= off && off < cg_info.rng_page_list.end()) {
sec.id = Sec_Page_List;
sec.range = cg_info.rng_page_list;
sec.pre_size = rblob_sz;
sec.post_size = 8;
return sec;
}
}
// Slow page group lookup, ideally only done once per render when last_pinfo is invalid.
for (Page_Info_Chunk *chunk = rdata.page_chunks; chunk; chunk = chunk->next) {
// If we're at the start of a chunk, return a fake Sec_Page used to highlight the RBlob header bytes.
if (chunk->range.start - rblob_sz <= off && off < chunk->range.start) {
sec.id = Sec_Page;
sec.range = app.last_pinfo->range;
sec.pre_size = pre_size;
sec.post_size = app.last_pinfo->checksum_size();
sec.highlighted = hilite_cluster >= 0 && app.last_pinfo->cluster_id == (u64)hilite_cluster;
sec.info = app.last_pinfo;
sec.range = { chunk->range.start, 0 };
sec.pre_size = rblob_sz;
return sec;
}
// Try another common and still fast case: `off is in the next page info as the previous.
if (app.last_pinfo->next) // don't check if it's checksum, since it's the first byte of the page
app.last_pinfo = app.last_pinfo->next;
else
break;
}
}
// Check if we're in a page list
// @Speed: linear search through all cluster groups
for (u64 cg_idx = 0; cg_idx < rdata.n_cluster_groups; ++cg_idx) {
Cluster_Group_Info &cg_info = rdata.cluster_groups[cg_idx];
if (cg_info.rng_page_list.start - rblob_sz <= off && off < cg_info.rng_page_list.end()) {
sec.id = Sec_Page_List;
sec.range = cg_info.rng_page_list;
sec.pre_size = rblob_sz;
sec.post_size = 8;
return sec;
}
}
if (chunk->range.start <= off && off < chunk->range.end()) {
for (u64 group_idx = chunk->first_group; group_idx < rdata.n_page_groups; ++group_idx) {
const Page_Info_Group &group = rdata.page_groups[group_idx];
if (off < group.range.start || off >= group.range.end())
continue;
// Slow page group lookup, ideally only done once per render when last_pinfo is invalid.
for (Page_Info_Chunk *chunk = rdata.page_chunks; chunk; chunk = chunk->next) {
// If we're at the start of a chunk, return a fake Sec_Page used to highlight the RBlob header bytes.
if (chunk->range.start - rblob_sz <= off && off < chunk->range.start) {
sec.id = Sec_Page;
sec.range = { chunk->range.start, 0 };
sec.pre_size = rblob_sz;
return sec;
}
if (chunk->range.start <= off && off < chunk->range.end()) {
for (u64 group_idx = chunk->first_group; group_idx < rdata.n_page_groups; ++group_idx) {
const Page_Info_Group &group = rdata.page_groups[group_idx];
if (off < group.range.start || off >= group.range.end())
continue;
for (Page_Info_Node *pinfo = group.first; pinfo; pinfo = pinfo->next) {
u64 pre_size = pinfo->is_first_in_cluster ? rblob_sz : 0;
if (pinfo->range.start - pre_size <= off && off < pinfo->range.end()) {
app.last_pinfo = pinfo;
sec.id = Sec_Page;
sec.range = pinfo->range;
sec.pre_size = pre_size;
sec.post_size = pinfo->checksum_size();
sec.highlighted = hilite_cluster >= 0 && pinfo->cluster_id == (u64)hilite_cluster;
sec.info = pinfo;
return sec;
for (Page_Info_Node *pinfo = group.first; pinfo; pinfo = pinfo->next) {
u64 pre_size = pinfo->is_first_in_cluster ? rblob_sz : 0;
if (pinfo->range.start - pre_size <= off && off < pinfo->range.end()) {
app.last_pinfo = pinfo;
sec.id = Sec_Page;
sec.range = pinfo->range;
sec.pre_size = pre_size;
sec.post_size = pinfo->checksum_size();
sec.highlighted = hilite_cluster >= 0 && pinfo->cluster_id == (u64)hilite_cluster;
sec.info = pinfo;
return sec;
}
}
}
}
fprintf(stderr, "Offset 0x%" PRIX64 " is in chunk 0x%" PRIX64 " - 0x%" PRIX64 ", but found in no page_info range!\n",
off, chunk->range.start, chunk->range.end());
assert(false);
fprintf(stderr, "Offset 0x%" PRIX64 " is in chunk 0x%" PRIX64 " - 0x%" PRIX64 ", but found in no page_info range!\n",
off, chunk->range.start, chunk->range.end());
assert(false);
}
}
}
// Copypasted from the fast page lookup
// Lookup other root objects.
// @Copypasted from the fast page lookup
if (app.tfile_data.sections[Sec_Other].count > 0) {
// fast case: `off` is in the same obj as previous `off`.
for (i32 i = 0; i < 2; ++i) {
const Section &other_sec = *app.last_other_root_obj;
if (other_sec.range.start - other_sec.pre_size < off && off < other_sec.range.end()) {
if (i == 1)
printf("checking if 0x%lX is in the next root obj, i.e. 0x%lX - 0x%lX - %s\n",
off, other_sec.range.start - other_sec.pre_size, other_sec.range.end(),
(other_sec.range.start - other_sec.pre_size <= off && off < other_sec.range.end()) ? "yes" : "no");
// else
// printf("checking if 0x%lX is in the first root obj, i.e. 0x%lX - 0x%lX - %s\n",
// off, other_sec.range.start - other_sec.pre_size, other_sec.range.end(),
// (other_sec.range.start - other_sec.pre_size < off && off < other_sec.range.end()) ? "yes" : "no");
if (other_sec.range.start - other_sec.pre_size <= off && off < other_sec.range.end()) {
return other_sec;
}
// Try another common and still fast case: `off is in the next root object as the previous.
if (app.last_other_root_obj->next)
app.last_other_root_obj = app.last_other_root_obj->next;
else
@ -590,9 +598,12 @@ Section find_section(App_State &app, u64 off, i64 hilite_cluster = -1)
}
// Slow linear lookup, ideally only done once per render when last_other_root_obj is invalid.
printf("page fault\n");
for (Section *other_sec = app.tfile_data.sections[Sec_Other].head; other_sec; other_sec = other_sec->next) {
if (other_sec->range.start - other_sec->pre_size <= off && off < other_sec->range.end()) {
app.last_other_root_obj = other_sec;
printf("off 0x%lX: last root obj set to 0x%lX - 0x%lX\n",
off, other_sec->range.start - other_sec->pre_size, other_sec->range.end());
return *other_sec;
}
}

View file

@ -65,8 +65,11 @@ enum Section_Id {
Sec_RNTuple_Header,
Sec_RNTuple_Footer,
Sec_Page_List,
Sec_Page,
Sec_Free_Slot,
Sec_COUNT_Regular,
// sections past this have a special handling in find_section() (i.e. fast lookup)
Sec_Page = Sec_COUNT_Regular,
// any other object stored in a TKey
Sec_Other,
@ -78,9 +81,10 @@ struct Section {
Section_Id id;
Byte_Range range;
u64 pre_size; // usually the TKey header, excluded from `range`
u64 post_size; // usually the checksum, included in `range`
u32 pre_size; // usually the TKey header, excluded from `range`
u32 post_size; // usually the checksum, included in `range`
b8 highlighted;
b8 from_old_version_rntuple;
// Optional pointer to the specific information for the section.
// Here are the actual types:
@ -103,8 +107,8 @@ internal const String8 section_names[] = {
str8("RNTuple Header"),
str8("RNTuple Footer"),
str8("Page List"),
str8("Page"),
str8("Free Slot"),
str8("Page"),
str8("Other"),
};
static_assert(countof(section_names) == Sec_COUNT);
@ -169,3 +173,10 @@ struct RNTuple_Data {
internal
Section *push_section(Arena *arena, TFile_Data &tdata, Section_Id id);
internal
b8 rntuple_is_old_version(const ROOT::RNTuple &ntuple)
{
return ntuple.GetVersionEpoch() == 0 && ntuple.GetVersionMajor() < 3;
}

View file

@ -191,7 +191,7 @@ int main(int argc, char **argv)
}
#ifndef RNT_NO_GFX
init_viewer(app, args.n_cols);
init_viewer(arena, app, args.n_cols);
// Init imgui and GLFW
GLFWwindow *window = init_glfw(app, 1600, 900);

View file

@ -249,7 +249,7 @@ b8 walk_tkeys(Arena *arena, const u8 *data, u64 data_len, u32 flags, TFile_Data
(u64)(n_bytes - keylen) != sections[Sec_TKey_List].head->range.len)
{
fprintf(stderr, "Warning: inconsistent key list data, the file may have not parsed properly.\n");
fprintf(stderr, " (keylen: %u vs %lu, nbytes: %u vs %lu)\n",
fprintf(stderr, " (keylen: %u vs %u, nbytes: %u vs %lu)\n",
keylen, sections[Sec_TKey_List].head->pre_size,
n_bytes, sections[Sec_TKey_List].head->range.len);
}
@ -304,6 +304,7 @@ b8 walk_tkeys(Arena *arena, const u8 *data, u64 data_len, u32 flags, TFile_Data
sec_anchor->range.len = n_bytes - keylen;
sec_anchor->pre_size = keylen;
sec_anchor->post_size = 8;
sec_anchor->from_old_version_rntuple = rntuple_is_old_version(*rntuple);
} else if (key_has_class_name("RBlob")) {
if (!sections[Sec_Page].head) {
@ -415,11 +416,12 @@ void map_rntuple_rblobs(Arena *arena, TFile_Data &tfile_data)
}
if (best_anchor_idx >= 0) {
found_sec[best_anchor_idx] = true;
Section *sec_header = push_section(arena, tfile_data, sec_id);
sec_header->range.start = tkey->rng.start + tkey->rng.len;
sec_header->range.len = sec_id == Sec_RNTuple_Header ? best_anchor->GetNBytesHeader() : best_anchor->GetNBytesFooter();
sec_header->pre_size = tkey->rng.len;
sec_header->post_size = 8;
Section *section = push_section(arena, tfile_data, sec_id);
section->range.start = tkey->rng.start + tkey->rng.len;
section->range.len = sec_id == Sec_RNTuple_Header ? best_anchor->GetNBytesHeader() : best_anchor->GetNBytesFooter();
section->pre_size = tkey->rng.len;
section->post_size = 8;
section->from_old_version_rntuple = rntuple_is_old_version(*best_anchor);
}
}
}