CCTLib
Calling-context and data-object attribution library for Intel Pin
splay_deserialize_test.cpp
Go to the documentation of this file.
1 // GoogleTest unit test proving the CCT-deserialize splay-tree insertion bug.
2 //
3 // Migrated from tests/splay_deserialize_test.cpp. The insert() helper runs
4 // the deserialize logic in two modes: baseline (buggy -- missing the
5 // `*rootp = newNode;` line) and fixed. The test asserts:
6 // FIXED: every inserted key is reachable from root; every splay lookup
7 // for that key succeeds.
8 // BUGGY: at least one inserted key is unreachable / lookup-fails on some
9 // scenario. If BUGGY ever passes cleanly the fix would be unnecessary,
10 // and this test forces us to notice.
11 
12 #include <cstdint>
13 #include <set>
14 #include <vector>
15 
16 #include <gtest/gtest.h>
17 
18 #include "splay-macros.h"
19 
20 namespace {
21 
22 struct TraceSplay {
23  uintptr_t key;
24  int value;
27 };
28 
29 TraceSplay* splay(TraceSplay* root, uintptr_t key) {
30  REGULAR_SPLAY_TREE(TraceSplay, root, key, key, left, right);
31  return root;
32 }
33 
34 // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) -- when apply_fix is
35 // false the buggy path intentionally leaks newNode.
36 void insert(TraceSplay** rootp, uintptr_t key, int value, bool apply_fix) {
37  auto* newNode = new TraceSplay{key, value, nullptr, nullptr};
38  if (*rootp == nullptr) {
39  *rootp = newNode;
40  return;
41  }
42  TraceSplay* found = splay(*rootp, key);
43  if (apply_fix) {
44  *rootp = newNode;
45  }
46  if (key < found->key) {
47  newNode->left = found->left;
48  newNode->right = found;
49  found->left = nullptr;
50  } else {
51  newNode->left = found;
52  newNode->right = found->right;
53  found->right = nullptr;
54  }
55 }
56 // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
57 
58 void collect_keys(TraceSplay* root, std::set<uintptr_t>& out) {
59  if (!root) return;
60  if (!out.insert(root->key).second) return;
61  collect_keys(root->left, out);
62  collect_keys(root->right, out);
63 }
64 
65 bool splay_finds(TraceSplay** rootp, uintptr_t key) {
66  if (!*rootp) return false;
67  *rootp = splay(*rootp, key);
68  return (*rootp)->key == key;
69 }
70 
71 struct Diagnosis {
72  size_t missing_reachable = 0;
73  size_t missing_splay = 0;
74 };
75 
76 Diagnosis run(bool apply_fix, const std::vector<uintptr_t>& seq) {
77  TraceSplay* root = nullptr;
78  for (size_t i = 0; i < seq.size(); ++i) {
79  insert(&root, seq[i], (int)i, apply_fix);
80  }
81  std::set<uintptr_t> reachable;
82  collect_keys(root, reachable);
83  Diagnosis d;
84  for (uintptr_t k : seq) {
85  if (!reachable.count(k)) ++d.missing_reachable;
86  }
87  for (uintptr_t k : seq) {
88  if (!splay_finds(&root, k)) ++d.missing_splay;
89  }
90  return d;
91 }
92 
93 class SplayDeserialize : public ::testing::TestWithParam<std::vector<uintptr_t>> {};
94 
95 TEST_P(SplayDeserialize, FixedIsWellFormed) {
96  Diagnosis d = run(/*apply_fix=*/true, GetParam());
97  EXPECT_EQ(0u, d.missing_reachable) << "keys missing from tree";
98  EXPECT_EQ(0u, d.missing_splay) << "splay lookups failed";
99 }
100 
101 TEST_P(SplayDeserialize, BuggyLosesKeys) {
102  Diagnosis d = run(/*apply_fix=*/false, GetParam());
103  EXPECT_GT(d.missing_reachable + d.missing_splay, 0u)
104  << "buggy variant unexpectedly did not lose any keys -- fix may be "
105  "unnecessary or the test scenario is too weak";
106 }
107 
109  Scenarios, SplayDeserialize,
110  ::testing::Values(
111  std::vector<uintptr_t>{100, 200, 300, 400, 500, 600, 700, 800}, // ascending
112  std::vector<uintptr_t>{800, 700, 600, 500, 400, 300, 200, 100}, // descending
113  std::vector<uintptr_t>{500, 100, 900, 300, 700, 200, 800, 400, 600} // mixed
114  ));
115 
116 } // namespace
INSTANTIATE_TEST_SUITE_P(Scenarios, SplayDeserialize, ::testing::Values(std::vector< uintptr_t >{100, 200, 300, 400, 500, 600, 700, 800}, std::vector< uintptr_t >{800, 700, 600, 500, 400, 300, 200, 100}, std::vector< uintptr_t >{500, 100, 900, 300, 700, 200, 800, 400, 600}))
Diagnosis run(bool apply_fix, const std::vector< uintptr_t > &seq)
uint8_t value[MAX_WRITE_OP_LENGTH]
#define REGULAR_SPLAY_TREE(type, root, key, value, left, right)
Definition: splay-macros.h:124
static void insert(TraceSplay **rootp, uintptr_t key, int value, bool apply_fix)
static bool splay_finds(TraceSplay **rootp, uintptr_t key)
static void collect_keys(TraceSplay *root, std::set< uintptr_t > &out)
static TraceSplay * splay(TraceSplay *root, uintptr_t key)