diff options
author | Oxore <oxore@protonmail.com> | 2025-01-03 17:07:00 +0300 |
---|---|---|
committer | Oxore <oxore@protonmail.com> | 2025-01-07 14:39:01 +0300 |
commit | cb96278e25140cfcc1afc22df2102bcf3b6ae38c (patch) | |
tree | 9e93bd8a5fb4d5fbc177924b6b25ca8cd04e7fd7 /src/tracetab_tests.cpp | |
parent | 810dc87cd5173f8cfc81c774fd49cf8f928a9ae8 (diff) |
Impl extended trace table format parser
Diffstat (limited to 'src/tracetab_tests.cpp')
-rw-r--r-- | src/tracetab_tests.cpp | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/src/tracetab_tests.cpp b/src/tracetab_tests.cpp new file mode 100644 index 0000000..e8d0710 --- /dev/null +++ b/src/tracetab_tests.cpp @@ -0,0 +1,317 @@ +/* SPDX-License-Identifier: Unlicense + */ + +#include "tracetab.h" +#include "doctest/doctest.h" +#include "debug.h" + +#include <cstring> + +#define STRING_SLICE(s) StringSlice{ s, (sizeof s) - 1u } + +struct StringSlice { + const char *str; + size_t len; +}; + +TEST_CASE("PC trace") { + const auto data = STRING_SLICE("512\n0x202\n0b1000000100\n01006\n"); + TraceTable tt{}; + const bool ok = ParseTraceData(tt, data.str, data.len, stderr); + REQUIRE(ok); + CHECK_EQ(tt.TypesCount(), 0); + CHECK_EQ(tt.NodesCount(), 4); + CHECK_EQ(tt.Shstr(), nullptr); + CHECK_EQ(tt.Node(0), TraceNode::Pc(512)); + CHECK_EQ(tt.Node(1), TraceNode::Pc(0x202)); + CHECK_EQ(tt.Node(2), TraceNode::Pc(0b1000000100)); + CHECK_EQ(tt.Node(3), TraceNode::Pc(01006)); +} + +TEST_CASE("pc, fn and mixed newlines") { + const auto data = STRING_SLICE( + "0x200 pc __start\r\n\r0x491ac fn 30 printf\n\n\n0x49c78 fn 100 fputs"); + TraceTable tt{}; + const bool ok = ParseTraceData(tt, data.str, data.len, stderr); + REQUIRE(ok); + CHECK_EQ(tt.TypesCount(), 0); + REQUIRE_EQ(tt.NodesCount(), 3); + CHECK_NE(tt.Shstr(), nullptr); + { + const auto n = tt.Node(0); + CHECK_EQ(n.kind, TraceNodeKind::kPc); + CHECK_EQ(n.address, 0x200); + CHECK_EQ(n.size, 0); + CHECK_NE(n.name, 0); + TRACE("tt.Shstr(n.name) = %s\n", tt.Shstr(n.name)); + CHECK_EQ(0, strcmp("__start", tt.Shstr(n.name))); + } + { + const auto n = tt.Node(1); + CHECK_EQ(n.kind, TraceNodeKind::kFunction); + CHECK_EQ(n.address, 0x491ac); + CHECK_EQ(n.size, 30); + CHECK_NE(n.name, 0); + TRACE("tt.Shstr(n.name) = %s\n", tt.Shstr(n.name)); + CHECK_EQ(0, strcmp("printf", tt.Shstr(n.name))); + } + { + const auto n = tt.Node(2); + CHECK_EQ(n.kind, TraceNodeKind::kFunction); + CHECK_EQ(n.address, 0x49c78); + CHECK_EQ(n.size, 100); + CHECK_NE(n.name, 0); + TRACE("tt.Shstr(n.name) = %s\n", tt.Shstr(n.name)); + CHECK_EQ(0, strcmp("fputs", tt.Shstr(n.name))); + } +} + +TEST_CASE("str, strz and blob") { + const auto data = STRING_SLICE( + "0x100 str 0x10 smd_header_sega_genesis\n" + "0xbc1b strz 13 windtrap_wsa\n" + "0x29bb0 blob 256 copy_of_smd_header\n"); + TraceTable tt{}; + const bool ok = ParseTraceData(tt, data.str, data.len, stderr); + REQUIRE(ok); + CHECK_EQ(tt.TypesCount(), 0); + REQUIRE_EQ(tt.NodesCount(), 3); + CHECK_NE(tt.Shstr(), nullptr); + { + const auto n = tt.Node(0); + CHECK_EQ(n.kind, TraceNodeKind::kData); + CHECK_EQ(n.address, 0x100); + CHECK_NE(n.name, 0); + CHECK_EQ(0, strcmp("smd_header_sega_genesis", tt.Shstr(n.name))); + CHECK_EQ(n.data_type.kind, DataTypeKind::kStr); + CHECK_EQ(n.data_type.count, 0x10); + CHECK_EQ(n.data_type.count * n.data_type.BaseSize(), n.size); + CHECK_EQ(0x10, n.size); + } + { + const auto n = tt.Node(1); + CHECK_EQ(n.kind, TraceNodeKind::kData); + CHECK_EQ(n.address, 0xbc1b); + CHECK_NE(n.name, 0); + CHECK_EQ(0, strcmp("windtrap_wsa", tt.Shstr(n.name))); + CHECK_EQ(n.data_type.kind, DataTypeKind::kStrz); + CHECK_EQ(n.data_type.count, 13); + CHECK_EQ(n.data_type.count * n.data_type.BaseSize(), n.size); + CHECK_EQ(13, n.size); + } + { + const auto n = tt.Node(2); + CHECK_EQ(n.kind, TraceNodeKind::kData); + CHECK_EQ(n.address, 0x29bb0); + CHECK_NE(n.name, 0); + CHECK_EQ(0, strcmp("copy_of_smd_header", tt.Shstr(n.name))); + CHECK_EQ(n.data_type.kind, DataTypeKind::kBlob); + CHECK_EQ(n.data_type.count, 256); + CHECK_EQ(n.data_type.count * n.data_type.BaseSize(), n.size); + CHECK_EQ(256, n.size); + } +} + +TEST_CASE("tables") { + const auto data = STRING_SLICE( + "0x4 [ptr] 63\n" + "0x87f44 [ptr,u32,] 19 soundtest_music_name_ptrs\n" + "0x88124 [ptr,u32] passwords_ptrs\n"); + TraceTable tt{}; + const bool ok = ParseTraceData(tt, data.str, data.len, stderr); + REQUIRE(ok); + REQUIRE_NE(tt.TypesCount(), 0); + REQUIRE_EQ(tt.NodesCount(), 3); + CHECK_NE(tt.Shstr(), nullptr); + { + const auto n = tt.Node(0); + CHECK_EQ(n.kind, TraceNodeKind::kData); + CHECK_EQ(n.address, 0x4); + CHECK_EQ(n.name, 0); + CHECK_EQ(n.data_type.kind, DataTypeKind::kTable); + CHECK_EQ(n.data_type.count, 63); + REQUIRE_EQ(n.data_type.nested_num, 1); + const auto ptr_type = tt.Type(n.data_type.nested_idx); + CHECK_EQ(ptr_type.kind, DataTypeKind::kPtr); + CHECK_EQ(ptr_type.count, 1); + CHECK_EQ(n.data_type.count * (ptr_type.count * ptr_type.BaseSize()), n.size); + CHECK_NE(0, n.size); + } + { + const auto n = tt.Node(1); + CHECK_EQ(n.kind, TraceNodeKind::kData); + CHECK_EQ(n.address, 0x87f44); + CHECK_NE(n.name, 0); + CHECK_EQ(0, strcmp("soundtest_music_name_ptrs", tt.Shstr(n.name))); + CHECK_EQ(n.data_type.kind, DataTypeKind::kTable); + CHECK_EQ(n.data_type.count, 19); + REQUIRE_EQ(n.data_type.nested_num, 2); + const auto ptr_type = tt.Type(n.data_type.nested_idx); + CHECK_EQ(ptr_type.kind, DataTypeKind::kPtr); + CHECK_EQ(ptr_type.count, 1); + const auto u32_type = tt.Type(n.data_type.nested_idx + 1); + CHECK_EQ(u32_type.kind, DataTypeKind::kU32); + CHECK_EQ(u32_type.count, 1); + CHECK_EQ( + n.data_type.count * (ptr_type.count * ptr_type.BaseSize() + + u32_type.count * u32_type.BaseSize()), + n.size); + CHECK_NE(0, n.size); + } + { + const auto n = tt.Node(2); + CHECK_EQ(n.kind, TraceNodeKind::kData); + CHECK_EQ(n.address, 0x88124); + CHECK_NE(n.name, 0); + CHECK_EQ(0, strcmp("passwords_ptrs", tt.Shstr(n.name))); + CHECK_EQ(n.data_type.kind, DataTypeKind::kTable); + CHECK_EQ(n.data_type.count, 0); + REQUIRE_EQ(n.data_type.nested_num, 2); + const auto ptr_type = tt.Type(n.data_type.nested_idx); + CHECK_EQ(ptr_type.kind, DataTypeKind::kPtr); + CHECK_EQ(ptr_type.count, 1); + const auto u32_type = tt.Type(n.data_type.nested_idx + 1); + CHECK_EQ(u32_type.kind, DataTypeKind::kU32); + CHECK_EQ(u32_type.count, 1); + CHECK_EQ( + n.data_type.count * (ptr_type.count * ptr_type.BaseSize() + + u32_type.count * u32_type.BaseSize()), + 0); + CHECK_EQ(0, n.size); + } +} + +TEST_CASE("table types with count specified") { + const auto data = STRING_SLICE( + "0x88 [ptr 4,u16 2, u8 4] 14 something_idk\n"); + TraceTable tt{}; + const bool ok = ParseTraceData(tt, data.str, data.len, stderr); + REQUIRE(ok); + REQUIRE_NE(tt.TypesCount(), 0); + REQUIRE_EQ(tt.NodesCount(), 1); + CHECK_NE(tt.Shstr(), nullptr); + { + const auto n = tt.Node(0); + CHECK_EQ(n.kind, TraceNodeKind::kData); + CHECK_EQ(n.address, 0x88); + CHECK_NE(n.name, 0); + CHECK_EQ(0, strcmp("something_idk", tt.Shstr(n.name))); + CHECK_EQ(n.data_type.kind, DataTypeKind::kTable); + CHECK_EQ(n.data_type.count, 14); + REQUIRE_EQ(n.data_type.nested_num, 3); + const auto ptr_type = tt.Type(n.data_type.nested_idx); + CHECK_EQ(ptr_type.kind, DataTypeKind::kPtr); + CHECK_EQ(ptr_type.count, 4); + const auto u16_type = tt.Type(n.data_type.nested_idx + 1); + CHECK_EQ(u16_type.kind, DataTypeKind::kU16); + CHECK_EQ(u16_type.count, 2); + const auto u8_type = tt.Type(n.data_type.nested_idx + 2); + CHECK_EQ(u8_type.kind, DataTypeKind::kU8); + CHECK_EQ(u8_type.count, 4); + CHECK_EQ( + n.data_type.count * + (ptr_type.count * ptr_type.BaseSize() + + u16_type.count * u16_type.BaseSize() + + u8_type.count * u8_type.BaseSize()), + n.size); + CHECK_NE(0, n.size); + } +} + +TEST_CASE("multiline trace node with comments") { + const auto data = STRING_SLICE( + "0x100 \\\n" + " [\\# side comments are ignored\n" + " ptr \\\n" + " 4\\\n" + " ,\\\n" + " u32\\#another random comment\n" + " ] \\\n" + " 4\\# and another one\n" + " trace_node#proper trailing comment\n"); + TraceTable tt{}; + const bool ok = ParseTraceData(tt, data.str, data.len, stderr); + REQUIRE(ok); + REQUIRE_NE(tt.TypesCount(), 0); + REQUIRE_EQ(tt.NodesCount(), 1); + CHECK_NE(tt.Shstr(), nullptr); + { + const auto n = tt.Node(0); + CHECK_EQ(n.kind, TraceNodeKind::kData); + CHECK_EQ(n.address, 0x100); + CHECK_NE(n.name, 0); + CHECK_EQ(0, strcmp("trace_node", tt.Shstr(n.name))); + CHECK_EQ(n.data_type.kind, DataTypeKind::kTable); + CHECK_EQ(n.data_type.count, 4); + REQUIRE_EQ(n.data_type.nested_num, 2); + const auto ptr_type = tt.Type(n.data_type.nested_idx); + CHECK_EQ(ptr_type.kind, DataTypeKind::kPtr); + CHECK_EQ(ptr_type.count, 4); + const auto u32_type = tt.Type(n.data_type.nested_idx + 1); + CHECK_EQ(u32_type.kind, DataTypeKind::kU32); + CHECK_EQ(u32_type.count, 1); + CHECK_EQ( + n.data_type.count * + (ptr_type.count * ptr_type.BaseSize() + + u32_type.count * u32_type.BaseSize()), + n.size); + CHECK_NE(0, n.size); + } +} + +TEST_CASE("multiline trace node with comments") { + const auto data = STRING_SLICE( + "# Heading comments are appended to the trace node.\r" + "# And they may be multiline!\n" + "# The newlines in the heading comment are preserved\r\n" + "# if there is more than one line.\n" + "0x100 str 8 trace_node #side comments are ignored\n" + "#Just a stray comment without trace node because empty line follows\n" + "\n" + "# Single line heading comment won't preserve trailing newline symbol\n" + "0x200 strz 4 trace_node2 # side comments are ignored\n" + "#Just a comment without trace node"); + TraceTable tt{}; + const bool ok = ParseTraceData(tt, data.str, data.len, stderr); + REQUIRE(ok); + REQUIRE_EQ(tt.TypesCount(), 0); + REQUIRE_EQ(tt.NodesCount(), 2); + CHECK_NE(tt.Shstr(), nullptr); + { + const auto n = tt.Node(0); + CHECK_EQ(n.kind, TraceNodeKind::kData); + CHECK_EQ(n.address, 0x100); + CHECK_NE(n.name, 0); + TRACE("name = %s", tt.Shstr(n.name)); + CHECK_EQ(0, strcmp("trace_node", tt.Shstr(n.name))); + CHECK_NE(n.comment, 0); + TRACE("comment = %s", tt.Shstr(n.comment)); + CHECK_EQ(0, strcmp( + " Heading comments are appended to the trace node.\n" + " And they may be multiline!\n" + " The newlines in the heading comment are preserved\n" + " if there is more than one line.", + tt.Shstr(n.comment))); + CHECK_EQ(n.data_type.kind, DataTypeKind::kStr); + CHECK_EQ(n.data_type.count, 8); + CHECK_EQ(n.data_type.count * n.data_type.BaseSize(), n.size); + CHECK_EQ(8, n.size); + } + { + const auto n = tt.Node(1); + CHECK_EQ(n.kind, TraceNodeKind::kData); + CHECK_EQ(n.address, 0x200); + CHECK_NE(n.name, 0); + TRACE("n.name = %s", tt.Shstr(n.name)); + CHECK_EQ(0, strcmp("trace_node2", tt.Shstr(n.name))); + CHECK_NE(n.comment, 0); + TRACE("comment = %s", tt.Shstr(n.comment)); + CHECK_EQ(0, strcmp( + " Single line heading comment won't preserve trailing newline symbol", + tt.Shstr(n.comment))); + CHECK_EQ(n.data_type.kind, DataTypeKind::kStrz); + CHECK_EQ(n.data_type.count, 4); + CHECK_EQ(n.data_type.count * n.data_type.BaseSize(), n.size); + CHECK_EQ(4, n.size); + } +} |