CCTLib
Calling-context and data-object attribution library for Intel Pin
ins_reuse_integration_test.cpp
Go to the documentation of this file.
1 // GoogleTest-driven integration tests for ins_reuse_client.
2 //
3 // Semantics: for each dynamic instruction execution, the tool measures
4 // the reuse distance -- how many other instructions executed since the
5 // LAST time this same instruction ran. The output is a per-thread
6 // histogram binned in log2 buckets.
7 
8 #include <cerrno>
9 #include <cstdio>
10 #include <cstdlib>
11 #include <cstring>
12 #include <fcntl.h>
13 #include <fstream>
14 #include <regex>
15 #include <sstream>
16 #include <string>
17 #include <sys/wait.h>
18 #include <unistd.h>
19 #include <vector>
20 
21 #include <gtest/gtest.h>
22 
23 namespace {
24 
25 std::string env(const char* n) { const char* v = getenv(n); return v ? v : ""; }
26 std::string cctlib_root() { std::string r = env("CCTLIB_ROOT"); return r.empty() ? "../.." : r; }
27 std::string pin_root() { return env("PIN_ROOT"); }
28 
29 int run_pin(const std::string& tool, const std::vector<std::string>& args) {
30  std::string pin = pin_root() + "/pin";
31  std::vector<char*> argv;
32  argv.push_back(const_cast<char*>(pin.c_str()));
33  argv.push_back(const_cast<char*>("-t"));
34  argv.push_back(const_cast<char*>(tool.c_str()));
35  argv.push_back(const_cast<char*>("--"));
36  for (auto& a : args) argv.push_back(const_cast<char*>(a.c_str()));
37  argv.push_back(nullptr);
38  pid_t pid = fork();
39  if (pid < 0) return -1;
40  if (pid == 0) {
41  if (!env("CCTLIB_TEST_VERBOSE").size()) {
42  int devnull = open("/dev/null", O_WRONLY);
43  if (devnull >= 0) { dup2(devnull, 1); dup2(devnull, 2); close(devnull); }
44  }
45  execv(pin.c_str(), argv.data());
46  _exit(127);
47  }
48  int status = 0;
49  if (waitpid(pid, &status, 0) < 0) return -1;
50  if (WIFEXITED(status)) return WEXITSTATUS(status);
51  return -2;
52 }
53 
54 std::string find_newest(const std::string& dir, const std::string& prefix) {
55  std::string cmd = "ls -t " + dir + "/" + prefix + "* 2>/dev/null | head -1";
56  FILE* p = popen(cmd.c_str(), "r");
57  if (!p) return {};
58  char buf[4096]; std::string out;
59  if (fgets(buf, sizeof(buf), p)) out = buf;
60  pclose(p);
61  if (!out.empty() && out.back() == '\n') out.pop_back();
62  return out;
63 }
64 std::string read_file(const std::string& path) {
65  std::ifstream in(path); std::stringstream ss; ss << in.rdbuf(); return ss.str();
66 }
67 void cleanup(const std::string& dir, const std::string& prefix) {
68  std::string cmd = "rm -f " + dir + "/" + prefix + "*"; (void)system(cmd.c_str());
69 }
70 
71 // Extracts the ins_reuse histogram from the report as a bin->count map.
72 // Ignores per-cacheline sub-histograms, just returns the first "TID 0
73 // instruction-reuse histo" block.
74 std::vector<double> parse_histogram(const std::string& content) {
75  std::vector<double> bins;
76  std::regex header(R"(TID \d+ instruction-reuse histo)");
77  std::smatch m;
78  if (!std::regex_search(content, m, header)) return bins;
79  std::string tail = content.substr(m.position() + m.length());
80  // Match lines like " 3 3.089300e+04 (16.89%)"
81  std::regex row(R"(\s*(\d+)\s+([\d.]+e[+\-]\d+))");
82  auto begin = std::sregex_iterator(tail.begin(), tail.end(), row);
83  auto end = std::sregex_iterator();
84  for (auto it = begin; it != end; ++it) {
85  size_t idx = std::stoul((*it)[1]);
86  double val = std::stod((*it)[2]);
87  if (bins.size() <= idx) bins.resize(idx + 1, 0.0);
88  bins[idx] = val;
89  // Stop after ~32 rows so we don't slurp the next histogram.
90  if (bins.size() >= 32) break;
91  }
92  return bins;
93 }
94 
95 class InsReuseIntegration : public ::testing::Test {
96  protected:
97  std::string root_, tool_;
98  void SetUp() override {
99  root_ = cctlib_root();
100  ASSERT_FALSE(pin_root().empty());
101  tool_ = root_ + "/clients/obj-intel64/ins_reuse_client.so";
102  ASSERT_EQ(0, access(tool_.c_str(), F_OK)) << tool_ << " missing";
103  ASSERT_EQ(0, chdir(root_.c_str())) << "chdir failed";
104  }
105  std::vector<double> run_and_parse(const std::string& victim) {
106  cleanup(root_, "insReuse.out.");
107  int rc = run_pin(tool_, {victim});
108  EXPECT_EQ(0, rc);
109  std::string out = find_newest(root_, "insReuse.out.");
110  EXPECT_FALSE(out.empty());
111  return parse_histogram(read_file(out));
112  }
113 };
114 
115 TEST_F(InsReuseIntegration, RunsCleanlyOnLs) {
116  auto h = run_and_parse("/bin/ls");
117  long total = 0;
118  for (auto v : h) total += (long)v;
119  EXPECT_GT(total, 0);
120 }
121 
122 // The tight 3-instruction inline-asm loop (add / sub / jnz -- 3 real
123 // instructions per iteration) should have its dominant reuse-distance bin
124 // at a small distance. Every one of the 3 loop instructions is re-executed
125 // after distance 3, which falls in bin 2 ( covers [2,4) ) or bin 3
126 // ( covers [4,8) ) depending on how the tool counts.
127 //
128 // Assert that: the sum of low bins (0..4) accounts for the VAST majority
129 // of the histogram mass on this workload -- MUCH more than mid/high bins.
130 TEST_F(InsReuseIntegration, TightLoopFillsLowBins) {
131  auto h = run_and_parse(root_ + "/tests/gtest/obj/apps/ins_reuse_tight_loop");
132  ASSERT_FALSE(h.empty()) << "no histogram parsed";
133  double low = 0.0, mid = 0.0;
134  for (size_t i = 0; i < h.size(); ++i) {
135  if (i <= 4) low += h[i];
136  else if (i <= 12) mid += h[i];
137  }
138  EXPECT_GT(low, mid)
139  << "low-bin mass=" << low << " mid-bin mass=" << mid;
140  // And the total workload should be very large -- ~3 * 1e6 executions.
141  double total = 0.0;
142  for (auto v : h) total += v;
143  EXPECT_GT(total, 1e6);
144 }
145 
146 // ISA breadth: same tight-loop shape but with SIMD (vpaddq %ymm0, %ymm0,
147 // %ymm0) inside. Verifies ins_reuse correctly instruments and counts SIMD
148 // instructions in its reuse histogram (should also fill low bins).
149 TEST_F(InsReuseIntegration, SimdLoopFillsLowBins) {
150  auto h = run_and_parse(root_ + "/tests/gtest/obj/apps/isa/ins_reuse_simd_loop");
151  ASSERT_FALSE(h.empty()) << "no histogram parsed";
152  double low = 0.0, mid = 0.0;
153  for (size_t i = 0; i < h.size(); ++i) {
154  if (i <= 4) low += h[i];
155  else if (i <= 12) mid += h[i];
156  }
157  EXPECT_GT(low, mid)
158  << "low-bin mass=" << low << " mid-bin mass=" << mid;
159  double total = 0.0;
160  for (auto v : h) total += v;
161  EXPECT_GT(total, 1e6);
162 }
163 
164 } // namespace
static uint64_t buf[ITERS]
volatile uint8_t status
Definition: cctlib.cpp:230
std::vector< double > parse_histogram(const std::string &content)
int run_pin(const std::string &tool, const std::vector< std::string > &args)
void cleanup(const std::string &dir, const std::string &prefix)
std::string find_newest(const std::string &dir, const std::string &prefix)
void h()
Definition: test1.c:12
int a[1000]
Definition: testArray.c:5