35 #include <gtest/gtest.h>
39 std::string
env(
const char* name) {
const char* v = getenv(name);
return v ? v :
""; }
40 std::string
cctlib_root() { std::string r =
env(
"CCTLIB_ROOT");
return r.empty() ?
"../.." : r; }
43 int run_pin(
const std::string& tool,
const std::vector<std::string>& args) {
44 std::string pin =
pin_root() +
"/pin";
45 std::vector<char*> argv;
46 argv.push_back(
const_cast<char*
>(pin.c_str()));
47 argv.push_back(
const_cast<char*
>(
"-t"));
48 argv.push_back(
const_cast<char*
>(tool.c_str()));
49 argv.push_back(
const_cast<char*
>(
"--"));
50 for (
auto&
a : args) argv.push_back(
const_cast<char*
>(
a.c_str()));
51 argv.push_back(
nullptr);
54 if (pid < 0)
return -1;
56 if (!
env(
"CCTLIB_TEST_VERBOSE").size()) {
57 int devnull = open(
"/dev/null", O_WRONLY);
58 if (devnull >= 0) { dup2(devnull, 1); dup2(devnull, 2); close(devnull); }
60 execv(pin.c_str(), argv.data());
64 if (waitpid(pid, &
status, 0) < 0)
return -1;
69 std::string
find_newest(
const std::string& dir,
const std::string& prefix) {
70 std::string cmd =
"ls -t " + dir +
"/" + prefix +
"* 2>/dev/null | head -1";
71 FILE* p = popen(cmd.c_str(),
"r");
73 char buf[4096]; std::string out;
74 if (fgets(
buf,
sizeof(
buf), p)) out =
buf;
76 if (!out.empty() && out.back() ==
'\n') out.pop_back();
80 std::ifstream in(path); std::stringstream ss; ss << in.rdbuf();
return ss.str();
82 void cleanup(
const std::string& dir,
const std::string& prefix) {
83 std::string cmd =
"rm -f " + dir +
"/" + prefix +
"*"; (void)system(cmd.c_str());
89 std::regex re(R
"(GrandTotalDead = (\d+))");
91 if (!std::regex_search(content, m, re))
return -1;
92 return std::stol(m[1]);
96 std::regex re(R
"(GrandTotalWrites = (\d+))");
98 if (!std::regex_search(content, m, re))
return -1;
99 return std::stol(m[1]);
108 ASSERT_FALSE(
pin_root().empty()) <<
"PIN_ROOT required";
109 tool_ = root_ +
"/clients/obj-intel64/deadspy_client.so";
110 ASSERT_EQ(0, access(tool_.c_str(), F_OK)) << tool_ <<
" missing";
111 ASSERT_EQ(0, chdir(root_.c_str())) <<
"chdir(" << root_ <<
") failed";
117 cleanup(root_,
"deadspy.out.");
118 int rc =
run_pin(tool_, {victim});
119 EXPECT_EQ(0, rc) <<
"deadspy on " << victim <<
" returned " << rc;
120 std::string out =
find_newest(root_,
"deadspy.out.");
121 EXPECT_FALSE(out.empty()) <<
"no deadspy.out.* file produced";
133 long dead = run_and_parse_dead(
"/bin/ls", &writes);
134 ASSERT_GE(dead, 0) <<
"deadspy report missing GrandTotalDead";
135 EXPECT_GT(writes, 0);
143 long dead = run_and_parse_dead(root_ +
"/apps/obj-intel64/deadWrites.exe", &writes);
145 EXPECT_GT(dead, 0) <<
"deadWrites.exe should produce some dead writes";
157 long tp = run_and_parse_dead(root_ +
"/tests/gtest/obj/apps/deadspy_tp_simple");
158 long tn = run_and_parse_dead(root_ +
"/tests/gtest/obj/apps/deadspy_tn_simple");
164 EXPECT_GT(tp - tn, 40000)
165 <<
"TP=" << tp <<
" TN=" << tn
166 <<
" -- expected TP-TN > 40000 dead-write bytes from the workload";
173 long tp = run_and_parse_dead(root_ +
"/tests/gtest/obj/apps/deadspy_tp_sizes");
174 long tn = run_and_parse_dead(root_ +
"/tests/gtest/obj/apps/deadspy_tn_simple");
177 EXPECT_GT(tp - tn, 30000)
178 <<
"TP-multi-size=" << tp <<
" TN=" << tn;
187 cleanup(root_,
"deadspy.out.");
188 int rc =
run_pin(tool_, {root_ +
"/tests/gtest/obj/apps/deadspy_tp_simple"});
190 std::string out =
find_newest(root_,
"deadspy.out.");
191 ASSERT_FALSE(out.empty());
195 std::string cmd =
"grep -c 'store8:.*deadspy_tp_simple.c' " + out;
196 FILE* p = popen(cmd.c_str(),
"r");
197 ASSERT_NE(p,
nullptr);
198 char buf[64]; std::string s;
201 long hits = s.empty() ? 0 : std::stol(s);
202 EXPECT_GT(hits, 0) <<
"deadspy report has no context mentioning store8 "
203 "in deadspy_tp_simple.c";
223 public ::testing::WithParamInterface<IsaVictim> {};
226 long baseline = run_and_parse_dead(root_ +
"/tests/gtest/obj/apps/deadspy_tp_minimal");
227 long isa = run_and_parse_dead(root_ +
"/tests/gtest/obj/apps/isa/" + GetParam().name);
228 ASSERT_GE(baseline, 0);
230 EXPECT_GT(isa - baseline, GetParam().min_extra_dead_bytes)
231 <<
"victim=" << GetParam().name
232 <<
" isa-count=" << isa
233 <<
" baseline=" << baseline
234 <<
" threshold=" << GetParam().min_extra_dead_bytes;
243 IsaVictim{
"deadspy_avx32_tp", 200000},
247 IsaVictim{
"deadspy_partial_qword_then_byte_tp", 15000},
249 IsaVictim{
"deadspy_partial_qword_then_byte_high_tp", 15000},
251 IsaVictim{
"deadspy_repstos_tp", 200000},
259 IsaVictim{
"deadspy_addressing_tp", 100000},
263 IsaVictim{
"deadspy_cross_page_qword_tp", 40000},
267 IsaVictim{
"deadspy_pushpop_dead_tp", 40000},
269 IsaVictim{
"deadspy_movnti_tp", 40000},
274 IsaVictim{
"deadspy_prefetch_tp", 200000}),
275 [](
const testing::TestParamInfo<IsaVictim>& info) {
276 return info.param.name;
291 long baseline = run_and_parse_dead(root_ +
"/tests/gtest/obj/apps/deadspy_tp_minimal");
292 long isa = run_and_parse_dead(root_ +
"/tests/gtest/obj/apps/isa/deadspy_cmpxchg_tn");
293 ASSERT_GE(baseline, 0);
298 EXPECT_LT(isa - baseline, 5000)
299 <<
"cmpxchg pair falsely reported as dead. isa=" << isa
300 <<
" baseline=" << baseline;
313 long baseline = run_and_parse_dead(root_ +
"/tests/gtest/obj/apps/deadspy_tp_minimal");
314 long isa = run_and_parse_dead(root_ +
"/tests/gtest/obj/apps/isa/deadspy_xchg_tn");
315 ASSERT_GE(baseline, 0);
317 EXPECT_LT(isa - baseline, 5000)
318 <<
"xchg pair falsely reported as dead. isa=" << isa
319 <<
" baseline=" << baseline;
long run_and_parse_dead(const std::string &victim, long *total_writes=nullptr)
static uint64_t buf[ITERS]
std::string cctlib_root()
int run_pin(const std::string &tool, const std::vector< std::string > &args)
TEST_P(DeadspyIsa, ExceedsBaseline)
TEST_F(DeadspyIsa, XchgIsNotFalselyDead)
long parse_grand_total_dead(const std::string &content)
long parse_grand_total_writes(const std::string &content)
std::string read_file(const std::string &path)
std::string find_newest(const std::string &dir, const std::string &prefix)
INSTANTIATE_TEST_SUITE_P(IsaBreadth, DeadspyIsa, ::testing::Values(IsaVictim{"deadspy_sse16_tp", 100000}, IsaVictim{"deadspy_avx32_tp", 200000}, IsaVictim{"deadspy_partial_qword_then_byte_tp", 15000}, IsaVictim{"deadspy_partial_qword_then_byte_high_tp", 15000}, IsaVictim{"deadspy_repstos_tp", 200000}, IsaVictim{"deadspy_addressing_tp", 100000}, IsaVictim{"deadspy_cross_page_qword_tp", 40000}, IsaVictim{"deadspy_pushpop_dead_tp", 40000}, IsaVictim{"deadspy_movnti_tp", 40000}, IsaVictim{"deadspy_prefetch_tp", 200000}), [](const testing::TestParamInfo< IsaVictim > &info) { return info.param.name;})
void cleanup(const std::string &dir, const std::string &prefix)
std::string env(const char *name)
long min_extra_dead_bytes