CCTLib
Calling-context and data-object attribution library for Intel Pin
footprint_integration_test.cpp
Go to the documentation of this file.
1 // GoogleTest-driven integration tests for footprint_client.
2 //
3 // Semantics: footprint_client measures how many DISTINCT memory addresses
4 // each context touches. A program that spins on a single address should
5 // report a small footprint; one that sweeps over N unique addresses should
6 // report a footprint proportional to N.
7 //
8 // Output format is text; the first "Footprint is <N>," line following the
9 // per-thread header is the top-of-stack context (ROOT), and its value is
10 // the whole-thread total.
11 
12 #include <cerrno>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <cstring>
16 #include <fcntl.h>
17 #include <fstream>
18 #include <regex>
19 #include <sstream>
20 #include <string>
21 #include <sys/wait.h>
22 #include <unistd.h>
23 #include <vector>
24 
25 #include <gtest/gtest.h>
26 
27 namespace {
28 
29 std::string env(const char* n) { const char* v = getenv(n); return v ? v : ""; }
30 std::string cctlib_root() { std::string r = env("CCTLIB_ROOT"); return r.empty() ? "../.." : r; }
31 std::string pin_root() { return env("PIN_ROOT"); }
32 
33 int run_pin(const std::string& tool, const std::vector<std::string>& args) {
34  std::string pin = pin_root() + "/pin";
35  std::vector<char*> argv;
36  argv.push_back(const_cast<char*>(pin.c_str()));
37  argv.push_back(const_cast<char*>("-t"));
38  argv.push_back(const_cast<char*>(tool.c_str()));
39  argv.push_back(const_cast<char*>("--"));
40  for (auto& a : args) argv.push_back(const_cast<char*>(a.c_str()));
41  argv.push_back(nullptr);
42  pid_t pid = fork();
43  if (pid < 0) return -1;
44  if (pid == 0) {
45  if (!env("CCTLIB_TEST_VERBOSE").size()) {
46  int devnull = open("/dev/null", O_WRONLY);
47  if (devnull >= 0) { dup2(devnull, 1); dup2(devnull, 2); close(devnull); }
48  }
49  execv(pin.c_str(), argv.data());
50  _exit(127);
51  }
52  int status = 0;
53  if (waitpid(pid, &status, 0) < 0) return -1;
54  if (WIFEXITED(status)) return WEXITSTATUS(status);
55  return -2;
56 }
57 
58 std::string find_newest(const std::string& dir, const std::string& prefix) {
59  std::string cmd = "ls -t " + dir + "/" + prefix + "* 2>/dev/null | head -1";
60  FILE* p = popen(cmd.c_str(), "r");
61  if (!p) return {};
62  char buf[4096]; std::string out;
63  if (fgets(buf, sizeof(buf), p)) out = buf;
64  pclose(p);
65  if (!out.empty() && out.back() == '\n') out.pop_back();
66  return out;
67 }
68 std::string read_file(const std::string& path) {
69  std::ifstream in(path); std::stringstream ss; ss << in.rdbuf(); return ss.str();
70 }
71 void cleanup(const std::string& dir, const std::string& prefix) {
72  std::string cmd = "rm -f " + dir + "/" + prefix + "*"; (void)system(cmd.c_str());
73 }
74 
75 // Parses the FIRST "Footprint is <N>" line -- that's the whole-thread root
76 // context total.
77 long parse_first_footprint(const std::string& content) {
78  std::regex re(R"(Footprint is (\d+))");
79  std::smatch m;
80  if (!std::regex_search(content, m, re)) return -1;
81  return std::stol(m[1]);
82 }
83 
84 class FootprintIntegration : public ::testing::Test {
85  protected:
86  std::string root_, tool_;
87  void SetUp() override {
88  root_ = cctlib_root();
89  ASSERT_FALSE(pin_root().empty());
90  tool_ = root_ + "/clients/obj-intel64/footprint_client.so";
91  ASSERT_EQ(0, access(tool_.c_str(), F_OK)) << tool_ << " missing";
92  ASSERT_EQ(0, chdir(root_.c_str())) << "chdir failed";
93  }
94  long run_and_parse_footprint(const std::string& victim) {
95  cleanup(root_, "client.out.");
96  int rc = run_pin(tool_, {victim});
97  EXPECT_EQ(0, rc) << "footprint on " << victim << " returned " << rc;
98  std::string out = find_newest(root_, "client.out.");
99  EXPECT_FALSE(out.empty()) << "no client.out.* file";
100  return parse_first_footprint(read_file(out));
101  }
102 };
103 
104 TEST_F(FootprintIntegration, RunsCleanlyOnLs) {
105  long fp = run_and_parse_footprint("/bin/ls");
106  ASSERT_GE(fp, 0);
107  EXPECT_GT(fp, 0);
108 }
109 
110 // The large-footprint victim sweeps over 100000 unique 8-byte addresses;
111 // the small victim touches one 8-byte address 100000 times. The large
112 // victim's whole-thread footprint should be at least 50000 more distinct
113 // addresses than the small victim's.
114 TEST_F(FootprintIntegration, LargeFootprintExceedsSmall) {
115  long large = run_and_parse_footprint(root_ + "/tests/gtest/obj/apps/footprint_large");
116  long small = run_and_parse_footprint(root_ + "/tests/gtest/obj/apps/footprint_small");
117  ASSERT_GE(large, 0);
118  ASSERT_GE(small, 0);
119  // Both share the same libc startup; the WORKLOAD contribution is
120  // 100000 vs 1 unique addresses. Assert large > small + 50000 (leaves
121  // headroom for startup variance).
122  EXPECT_GT(large - small, 50000)
123  << "large=" << large << " small=" << small;
124 }
125 
126 // ISA breadth: AVX 32-byte store stride sweep. The victim writes 32 bytes
127 // per iteration to a fresh 32-byte cacheline; footprint should scale with
128 // 100000 unique cachelines touched * 32B each = 3.2 M distinct bytes.
129 // The footprint metric returned by the tool is byte-granular; assert the
130 // SIMD sweep's footprint materially exceeds the byte-granular small victim.
131 TEST_F(FootprintIntegration, AVX32SweepFootprintExceedsSmall) {
132  long avx = run_and_parse_footprint(root_ + "/tests/gtest/obj/apps/isa/footprint_avx32_sweep");
133  long small = run_and_parse_footprint(root_ + "/tests/gtest/obj/apps/footprint_small");
134  ASSERT_GE(avx, 0);
135  ASSERT_GE(small, 0);
136  // 100000 iters * 32B distinct = 3.2M. Assert avx >= small + 1M
137  // (large margin, tolerates footprint aggregation quirks).
138  EXPECT_GT(avx - small, 1000000)
139  << "avx=" << avx << " small=" << small;
140 }
141 
142 } // namespace
static uint64_t buf[ITERS]
volatile uint8_t status
Definition: cctlib.cpp:230
void cleanup(const std::string &dir, const std::string &prefix)
std::string find_newest(const std::string &dir, const std::string &prefix)
int run_pin(const std::string &tool, const std::vector< std::string > &args)
TEST_F(FootprintIntegration, AVX32SweepFootprintExceedsSmall)
int a[1000]
Definition: testArray.c:5