13cab2bb3Spatrick //===-- compression.cpp -----------------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick
93cab2bb3Spatrick #include "gwp_asan/stack_trace_compressor.h"
10*d89ec533Spatrick #include "gwp_asan/tests/harness.h"
113cab2bb3Spatrick
123cab2bb3Spatrick namespace gwp_asan {
133cab2bb3Spatrick namespace compression {
143cab2bb3Spatrick
TEST(GwpAsanCompressionTest,SingleByteVarInt)153cab2bb3Spatrick TEST(GwpAsanCompressionTest, SingleByteVarInt) {
163cab2bb3Spatrick uint8_t Compressed[1];
173cab2bb3Spatrick
183cab2bb3Spatrick uintptr_t Uncompressed = 0x00;
193cab2bb3Spatrick EXPECT_EQ(1u, pack(&Uncompressed, 1u, Compressed, sizeof(Compressed)));
203cab2bb3Spatrick EXPECT_EQ(Compressed[0], 0x00);
213cab2bb3Spatrick
223cab2bb3Spatrick Uncompressed = 0x01;
233cab2bb3Spatrick EXPECT_EQ(1u, pack(&Uncompressed, 1u, Compressed, sizeof(Compressed)));
243cab2bb3Spatrick EXPECT_EQ(Compressed[0], 0x02); // +1 => 2 in zigzag.
253cab2bb3Spatrick
263cab2bb3Spatrick Uncompressed = 0x3f;
273cab2bb3Spatrick EXPECT_EQ(1u, pack(&Uncompressed, 1u, Compressed, sizeof(Compressed)));
283cab2bb3Spatrick EXPECT_EQ(Compressed[0], 0x7e); // +63 => 127 in zigzag.
293cab2bb3Spatrick }
303cab2bb3Spatrick
TEST(GwpAsanCompressionTest,MultiByteVarInt)313cab2bb3Spatrick TEST(GwpAsanCompressionTest, MultiByteVarInt) {
323cab2bb3Spatrick uint8_t Compressed[sizeof(uintptr_t) + 1];
333cab2bb3Spatrick
343cab2bb3Spatrick uintptr_t Uncompressed = 0x40;
353cab2bb3Spatrick EXPECT_EQ(2u, pack(&Uncompressed, 1u, Compressed, sizeof(Compressed)));
363cab2bb3Spatrick EXPECT_EQ(Compressed[0], 0x80); // +64 => 128 in zigzag.
373cab2bb3Spatrick EXPECT_EQ(Compressed[1], 0x01);
383cab2bb3Spatrick
393cab2bb3Spatrick Uncompressed = 0x41;
403cab2bb3Spatrick EXPECT_EQ(2u, pack(&Uncompressed, 1u, Compressed, sizeof(Compressed)));
413cab2bb3Spatrick EXPECT_EQ(Compressed[0], 0x82); // +65 => 130 in zigzag
423cab2bb3Spatrick EXPECT_EQ(Compressed[1], 0x01);
433cab2bb3Spatrick
443cab2bb3Spatrick Uncompressed = 0x1fff;
453cab2bb3Spatrick EXPECT_EQ(2u, pack(&Uncompressed, 1u, Compressed, sizeof(Compressed)));
463cab2bb3Spatrick EXPECT_EQ(Compressed[0], 0xfe); // +8191 => 16382 in zigzag
473cab2bb3Spatrick EXPECT_EQ(Compressed[1], 0x7f);
483cab2bb3Spatrick
493cab2bb3Spatrick Uncompressed = 0x2000;
503cab2bb3Spatrick EXPECT_EQ(3u, pack(&Uncompressed, 1u, Compressed, sizeof(Compressed)));
513cab2bb3Spatrick EXPECT_EQ(Compressed[0], 0x80); // +8192 => 16384 in zigzag
523cab2bb3Spatrick EXPECT_EQ(Compressed[1], 0x80);
533cab2bb3Spatrick EXPECT_EQ(Compressed[2], 0x01);
543cab2bb3Spatrick
553cab2bb3Spatrick Uncompressed = 0x7f010ff0;
563cab2bb3Spatrick EXPECT_EQ(5u, pack(&Uncompressed, 1u, Compressed, sizeof(Compressed)));
573cab2bb3Spatrick EXPECT_EQ(Compressed[0], 0xe0); // +0x7f010ff0 => 0xFE021FE0 in zigzag
583cab2bb3Spatrick EXPECT_EQ(Compressed[1], 0xbf);
593cab2bb3Spatrick EXPECT_EQ(Compressed[2], 0x88);
603cab2bb3Spatrick EXPECT_EQ(Compressed[3], 0xf0);
613cab2bb3Spatrick EXPECT_EQ(Compressed[4], 0x0f);
623cab2bb3Spatrick }
633cab2bb3Spatrick
TEST(GwpAsanCompressionTest,CorrectDifference)643cab2bb3Spatrick TEST(GwpAsanCompressionTest, CorrectDifference) {
653cab2bb3Spatrick uint8_t Compressed[10];
663cab2bb3Spatrick uintptr_t Uncompressed[2] = {0x00, 0x00};
673cab2bb3Spatrick
683cab2bb3Spatrick EXPECT_EQ(2u, pack(Uncompressed, sizeof(Uncompressed) / sizeof(uintptr_t),
693cab2bb3Spatrick Compressed, sizeof(Compressed)));
703cab2bb3Spatrick EXPECT_EQ(Compressed[1], 0x00); // +0 difference => 0 in zigzag.
713cab2bb3Spatrick
723cab2bb3Spatrick Uncompressed[1] = 0x01;
733cab2bb3Spatrick EXPECT_EQ(2u, pack(Uncompressed, sizeof(Uncompressed) / sizeof(uintptr_t),
743cab2bb3Spatrick Compressed, sizeof(Compressed)));
753cab2bb3Spatrick EXPECT_EQ(Compressed[1], 0x02); // +1 difference => 2 in zigzag.
763cab2bb3Spatrick
773cab2bb3Spatrick Uncompressed[1] = 0x02;
783cab2bb3Spatrick EXPECT_EQ(2u, pack(Uncompressed, sizeof(Uncompressed) / sizeof(uintptr_t),
793cab2bb3Spatrick Compressed, sizeof(Compressed)));
803cab2bb3Spatrick EXPECT_EQ(Compressed[1], 0x04); // +2 difference => 4 in zigzag.
813cab2bb3Spatrick
823cab2bb3Spatrick Uncompressed[1] = 0x80;
833cab2bb3Spatrick EXPECT_EQ(3u, pack(Uncompressed, sizeof(Uncompressed) / sizeof(uintptr_t),
843cab2bb3Spatrick Compressed, sizeof(Compressed)));
853cab2bb3Spatrick EXPECT_EQ(Compressed[1], 0x80); // +128 difference => +256 in zigzag (note the
863cab2bb3Spatrick EXPECT_EQ(Compressed[2], 0x02); // varint encoding here).
873cab2bb3Spatrick
883cab2bb3Spatrick Uncompressed[0] = 0x01;
893cab2bb3Spatrick Uncompressed[1] = 0x00;
903cab2bb3Spatrick EXPECT_EQ(2u, pack(Uncompressed, sizeof(Uncompressed) / sizeof(uintptr_t),
913cab2bb3Spatrick Compressed, sizeof(Compressed)));
923cab2bb3Spatrick EXPECT_EQ(Compressed[1], 0x01); // -1 difference => +1 in zigzag.
933cab2bb3Spatrick
943cab2bb3Spatrick Uncompressed[0] = 0x02;
953cab2bb3Spatrick EXPECT_EQ(2u, pack(Uncompressed, sizeof(Uncompressed) / sizeof(uintptr_t),
963cab2bb3Spatrick Compressed, sizeof(Compressed)));
973cab2bb3Spatrick EXPECT_EQ(Compressed[1], 0x03); // -2 difference => +3 in zigzag.
983cab2bb3Spatrick
993cab2bb3Spatrick Uncompressed[0] = 0x80;
1003cab2bb3Spatrick EXPECT_EQ(4u, pack(Uncompressed, sizeof(Uncompressed) / sizeof(uintptr_t),
1013cab2bb3Spatrick Compressed, sizeof(Compressed)));
1023cab2bb3Spatrick EXPECT_EQ(Compressed[2], 0xff); // -128 difference => +255 in zigzag (note the
1033cab2bb3Spatrick EXPECT_EQ(Compressed[3], 0x01); // varint encoding here).
1043cab2bb3Spatrick }
1053cab2bb3Spatrick
1063cab2bb3Spatrick // Space needed to encode the biggest uintptr_t as a varint is ceil((8 / 7) *
1073cab2bb3Spatrick // sizeof(uintptr_t)), as each 7 bits requires 8 bits of space.
1083cab2bb3Spatrick constexpr size_t kBytesForLargestVarInt = (sizeof(uintptr_t) * 8) / 7 + 1;
1093cab2bb3Spatrick
1103cab2bb3Spatrick // Ensures that when the closest diff between two pointers is via. underflow,
1113cab2bb3Spatrick // we take the underflow option.
TEST(GwpAsanCompressionTest,ClosestDiffIsUnderflow)1123cab2bb3Spatrick TEST(GwpAsanCompressionTest, ClosestDiffIsUnderflow) {
1133cab2bb3Spatrick uint8_t Compressed[2];
1143cab2bb3Spatrick uintptr_t Uncompressed[2] = {0x00, UINTPTR_MAX};
1153cab2bb3Spatrick
1163cab2bb3Spatrick EXPECT_EQ(2u, pack(Uncompressed, sizeof(Uncompressed) / sizeof(uintptr_t),
1173cab2bb3Spatrick Compressed, sizeof(Compressed)));
1183cab2bb3Spatrick // -1 difference => +1 in zigzag.
1193cab2bb3Spatrick EXPECT_EQ(Compressed[1], 0x01);
1203cab2bb3Spatrick }
1213cab2bb3Spatrick
1223cab2bb3Spatrick // Ensures that when the closest diff between two pointers is via. overflow,
1233cab2bb3Spatrick // that we take this option.
TEST(GwpAsanCompressionTest,ClosestDiffIsOverflow)1243cab2bb3Spatrick TEST(GwpAsanCompressionTest, ClosestDiffIsOverflow) {
1253cab2bb3Spatrick uint8_t Compressed[2];
1263cab2bb3Spatrick uintptr_t Uncompressed[2] = {UINTPTR_MAX, 0x00};
1273cab2bb3Spatrick
1283cab2bb3Spatrick // Note here that the first element is encoded as the difference from zero.
1293cab2bb3Spatrick EXPECT_EQ(2u, pack(Uncompressed, sizeof(Uncompressed) / sizeof(uintptr_t),
1303cab2bb3Spatrick Compressed, sizeof(Compressed)));
1313cab2bb3Spatrick // -1 difference => +1 in zigzag (the first pointer is encoded as -1).
1323cab2bb3Spatrick EXPECT_EQ(Compressed[0], 0x01);
1333cab2bb3Spatrick // +1 difference => +2 in zigzag.
1343cab2bb3Spatrick EXPECT_EQ(Compressed[1], 0x02);
1353cab2bb3Spatrick }
1363cab2bb3Spatrick
runPackUnpack(uintptr_t * Test,size_t NumEntries)1373cab2bb3Spatrick void runPackUnpack(uintptr_t *Test, size_t NumEntries) {
1383cab2bb3Spatrick // Setup the input/output buffers based on the maximum possible size.
1393cab2bb3Spatrick uintptr_t *Uncompressed =
1403cab2bb3Spatrick static_cast<uintptr_t *>(alloca(NumEntries * sizeof(uintptr_t)));
1413cab2bb3Spatrick size_t CompressedBufferSize = NumEntries * kBytesForLargestVarInt;
1423cab2bb3Spatrick uint8_t *Compressed = static_cast<uint8_t *>(alloca(CompressedBufferSize));
1433cab2bb3Spatrick
1443cab2bb3Spatrick // Pack the provided testcase, recoding the number of bytes it took for
1453cab2bb3Spatrick // storage.
1463cab2bb3Spatrick size_t BytesUsedForPacking =
1473cab2bb3Spatrick pack(Test, NumEntries, Compressed, CompressedBufferSize);
1483cab2bb3Spatrick EXPECT_NE(BytesUsedForPacking, 0u);
1493cab2bb3Spatrick
1503cab2bb3Spatrick // Unpack the testcase and ensure that the correct number of entries was
1513cab2bb3Spatrick // unpacked.
1523cab2bb3Spatrick EXPECT_EQ(NumEntries,
1533cab2bb3Spatrick unpack(Compressed, BytesUsedForPacking, Uncompressed, NumEntries));
1543cab2bb3Spatrick
1553cab2bb3Spatrick // Ensure that the unpacked trace is the same as the original testcase.
1563cab2bb3Spatrick for (size_t i = 0; i < NumEntries; ++i) {
1573cab2bb3Spatrick EXPECT_EQ(Uncompressed[i], Test[i]);
1583cab2bb3Spatrick }
1593cab2bb3Spatrick }
1603cab2bb3Spatrick
TEST(GwpAsanCompressionTest,UncompressVarInt)1613cab2bb3Spatrick TEST(GwpAsanCompressionTest, UncompressVarInt) {
1623cab2bb3Spatrick uint8_t Compressed[] = {0x00, 0xaa, 0xaf, 0xd0, 0xda, 0x04};
1633cab2bb3Spatrick uintptr_t Uncompressed[2];
1643cab2bb3Spatrick
1653cab2bb3Spatrick EXPECT_EQ(2u, unpack(Compressed, sizeof(Compressed), Uncompressed, 2u));
1663cab2bb3Spatrick EXPECT_EQ(Uncompressed[0], 0x00u);
1673cab2bb3Spatrick EXPECT_EQ(Uncompressed[1], 0x25aa0bd5u);
1683cab2bb3Spatrick }
1693cab2bb3Spatrick
TEST(GwpAsanCompressionTest,UncompressVarIntUnderflow)1703cab2bb3Spatrick TEST(GwpAsanCompressionTest, UncompressVarIntUnderflow) {
1713cab2bb3Spatrick uint8_t Compressed[] = {0x00, 0xab, 0xaf, 0xd0, 0xda, 0x04};
1723cab2bb3Spatrick uintptr_t Uncompressed[2];
1733cab2bb3Spatrick
1743cab2bb3Spatrick EXPECT_EQ(2u, unpack(Compressed, sizeof(Compressed), Uncompressed, 2u));
1753cab2bb3Spatrick EXPECT_EQ(Uncompressed[0], 0x00u);
1763cab2bb3Spatrick EXPECT_EQ(Uncompressed[1], UINTPTR_MAX - 0x25aa0bd5u);
1773cab2bb3Spatrick }
1783cab2bb3Spatrick
TEST(GwpAsanCompressionTest,CompressUncompressAscending)1793cab2bb3Spatrick TEST(GwpAsanCompressionTest, CompressUncompressAscending) {
1803cab2bb3Spatrick uintptr_t Test[] = {1, 2, 3};
1813cab2bb3Spatrick runPackUnpack(Test, sizeof(Test) / sizeof(uintptr_t));
1823cab2bb3Spatrick }
1833cab2bb3Spatrick
TEST(GwpAsanCompressionTest,CompressUncompressDescending)1843cab2bb3Spatrick TEST(GwpAsanCompressionTest, CompressUncompressDescending) {
1853cab2bb3Spatrick uintptr_t Test[] = {3, 2, 1};
1863cab2bb3Spatrick runPackUnpack(Test, sizeof(Test) / sizeof(uintptr_t));
1873cab2bb3Spatrick }
1883cab2bb3Spatrick
TEST(GwpAsanCompressionTest,CompressUncompressRepeated)1893cab2bb3Spatrick TEST(GwpAsanCompressionTest, CompressUncompressRepeated) {
1903cab2bb3Spatrick uintptr_t Test[] = {3, 3, 3};
1913cab2bb3Spatrick runPackUnpack(Test, sizeof(Test) / sizeof(uintptr_t));
1923cab2bb3Spatrick }
1933cab2bb3Spatrick
TEST(GwpAsanCompressionTest,CompressUncompressZigZag)1943cab2bb3Spatrick TEST(GwpAsanCompressionTest, CompressUncompressZigZag) {
1953cab2bb3Spatrick uintptr_t Test[] = {1, 3, 2, 4, 1, 2};
1963cab2bb3Spatrick runPackUnpack(Test, sizeof(Test) / sizeof(uintptr_t));
1973cab2bb3Spatrick }
1983cab2bb3Spatrick
TEST(GwpAsanCompressionTest,CompressUncompressVarInt)1993cab2bb3Spatrick TEST(GwpAsanCompressionTest, CompressUncompressVarInt) {
2003cab2bb3Spatrick uintptr_t Test[] = {0x1981561, 0x18560, 0x25ab9135, 0x1232562};
2013cab2bb3Spatrick runPackUnpack(Test, sizeof(Test) / sizeof(uintptr_t));
2023cab2bb3Spatrick }
2033cab2bb3Spatrick
TEST(GwpAsanCompressionTest,CompressUncompressLargestDifference)2043cab2bb3Spatrick TEST(GwpAsanCompressionTest, CompressUncompressLargestDifference) {
2053cab2bb3Spatrick uintptr_t Test[] = {0x00, INTPTR_MAX, UINTPTR_MAX, INTPTR_MAX, 0x00};
2063cab2bb3Spatrick runPackUnpack(Test, sizeof(Test) / sizeof(uintptr_t));
2073cab2bb3Spatrick }
2083cab2bb3Spatrick
TEST(GwpAsanCompressionTest,CompressUncompressBigPointers)2093cab2bb3Spatrick TEST(GwpAsanCompressionTest, CompressUncompressBigPointers) {
2103cab2bb3Spatrick uintptr_t Test[] = {UINTPTR_MAX, UINTPTR_MAX - 10};
2113cab2bb3Spatrick runPackUnpack(Test, sizeof(Test) / sizeof(uintptr_t));
2123cab2bb3Spatrick
2133cab2bb3Spatrick uintptr_t Test2[] = {UINTPTR_MAX - 10, UINTPTR_MAX};
2143cab2bb3Spatrick runPackUnpack(Test2, sizeof(Test2) / sizeof(uintptr_t));
2153cab2bb3Spatrick }
2163cab2bb3Spatrick
TEST(GwpAsanCompressionTest,UncompressFailsWithOutOfBoundsVarInt)2173cab2bb3Spatrick TEST(GwpAsanCompressionTest, UncompressFailsWithOutOfBoundsVarInt) {
2183cab2bb3Spatrick uint8_t Compressed[kBytesForLargestVarInt + 1];
2193cab2bb3Spatrick for (size_t i = 0; i < kBytesForLargestVarInt; ++i) {
2203cab2bb3Spatrick Compressed[i] = 0x80;
2213cab2bb3Spatrick }
2223cab2bb3Spatrick Compressed[kBytesForLargestVarInt] = 0x00;
2233cab2bb3Spatrick
2243cab2bb3Spatrick uintptr_t Uncompressed;
2253cab2bb3Spatrick EXPECT_EQ(unpack(Compressed, kBytesForLargestVarInt + 1, &Uncompressed, 1),
2263cab2bb3Spatrick 0u);
2273cab2bb3Spatrick }
2283cab2bb3Spatrick
TEST(GwpAsanCompressionTest,UncompressFailsWithTooSmallBuffer)2293cab2bb3Spatrick TEST(GwpAsanCompressionTest, UncompressFailsWithTooSmallBuffer) {
2303cab2bb3Spatrick uint8_t Compressed[] = {0x80, 0x00};
2313cab2bb3Spatrick
2323cab2bb3Spatrick uintptr_t Uncompressed;
2333cab2bb3Spatrick EXPECT_EQ(unpack(Compressed, 1u, &Uncompressed, 1), 0u);
2343cab2bb3Spatrick }
2353cab2bb3Spatrick
TEST(GwpAsanCompressionTest,CompressPartiallySucceedsWithTooSmallBuffer)2363cab2bb3Spatrick TEST(GwpAsanCompressionTest, CompressPartiallySucceedsWithTooSmallBuffer) {
2373cab2bb3Spatrick uintptr_t Uncompressed[] = {
2383cab2bb3Spatrick 0x80, // Requires 2 bytes for varint.
2393cab2bb3Spatrick 0x100, // Requires two bytes for varint difference of 0x80.
2403cab2bb3Spatrick 0xff, // Requires single byte for varint difference of -0x01
2413cab2bb3Spatrick };
2423cab2bb3Spatrick uint8_t Compressed[3 * kBytesForLargestVarInt];
2433cab2bb3Spatrick
2443cab2bb3Spatrick // Zero and one byte buffers shouldn't encode anything (see above for size
2453cab2bb3Spatrick // requirements).
2463cab2bb3Spatrick EXPECT_EQ(pack(Uncompressed, 3u, Compressed, 0u), 0u);
2473cab2bb3Spatrick EXPECT_EQ(pack(Uncompressed, 3u, Compressed, 1u), 0u);
2483cab2bb3Spatrick
2493cab2bb3Spatrick // Two byte buffer should hold a single varint-encoded value.
2503cab2bb3Spatrick EXPECT_EQ(pack(Uncompressed, 3u, Compressed, 2u), 2u);
2513cab2bb3Spatrick
2523cab2bb3Spatrick // Three bytes isn't enough to cover the first two pointers, as both take two
2533cab2bb3Spatrick // bytes each to store. Expect a single value to be compressed.
2543cab2bb3Spatrick EXPECT_EQ(pack(Uncompressed, 3u, Compressed, 3u), 2u);
2553cab2bb3Spatrick
2563cab2bb3Spatrick // Four bytes is enough for the first two pointers to be stored.
2573cab2bb3Spatrick EXPECT_EQ(pack(Uncompressed, 3u, Compressed, 4u), 4u);
2583cab2bb3Spatrick
2593cab2bb3Spatrick // And five is enough for all three pointers to be stored.
2603cab2bb3Spatrick EXPECT_EQ(pack(Uncompressed, 3u, Compressed, 5u), 5u);
2613cab2bb3Spatrick // And a buffer that's bigger than five bytes should still only write five
2623cab2bb3Spatrick // bytes.
2633cab2bb3Spatrick EXPECT_EQ(pack(Uncompressed, 3u, Compressed, 6u), 5u);
2643cab2bb3Spatrick EXPECT_EQ(pack(Uncompressed, 3u, Compressed, 3 * kBytesForLargestVarInt), 5u);
2653cab2bb3Spatrick }
2663cab2bb3Spatrick } // namespace compression
2673cab2bb3Spatrick } // namespace gwp_asan
268