This source file includes following definitions.
- get
- ReadPersistent
- V
- filename
- Check
- CheckWithSkips
- ValidateProfile
- ExpectStopped
- ExpectRunningSamples
- ExpectSameState
- RUN_ALL_TESTS
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- main
#if defined HAVE_STDINT_H
#include <stdint.h>
#elif defined HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <string>
#include "profiledata.h"
#include "base/commandlineflags.h"
#include "base/logging.h"
using std::string;
#define TEST_F(cls, fn) void cls :: fn()
namespace {
template<typename T> class scoped_array {
public:
scoped_array(T* data) : data_(data) { }
~scoped_array() { delete[] data_; }
T* get() { return data_; }
T& operator[](int i) { return data_[i]; }
private:
T* const data_;
};
#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
static ssize_t ReadPersistent(const int fd, void *buf, const size_t count) {
CHECK_GE(fd, 0);
char *buf0 = reinterpret_cast<char *>(buf);
ssize_t num_bytes = 0;
while (num_bytes < count) {
ssize_t len;
NO_INTR(len = read(fd, buf0 + num_bytes, count - num_bytes));
if (len < 0) {
return -1;
}
if (len == 0) {
break;
}
num_bytes += len;
}
CHECK(num_bytes <= count);
return num_bytes;
}
struct FileDescriptor {
const int fd_;
explicit FileDescriptor(int fd) : fd_(fd) {}
~FileDescriptor() {
if (fd_ >= 0) {
NO_INTR(close(fd_));
}
}
int get() { return fd_; }
};
typedef uintptr_t ProfileDataSlot;
inline void* V(intptr_t x) { return reinterpret_cast<void*>(x); }
const char kNoError[] = "";
class ProfileDataChecker {
public:
ProfileDataChecker() {
const char* tmpdir = getenv("TMPDIR");
if (tmpdir == NULL)
tmpdir = "/tmp";
mkdir(tmpdir, 0755);
filename_ = string(tmpdir) + "/profiledata_unittest.tmp";
}
string filename() const { return filename_; }
string Check(const ProfileDataSlot* slots, int num_slots) {
return CheckWithSkips(slots, num_slots, NULL, 0);
}
string CheckWithSkips(const ProfileDataSlot* slots, int num_slots,
const int* skips, int num_skips);
string ValidateProfile();
private:
string filename_;
};
string ProfileDataChecker::CheckWithSkips(const ProfileDataSlot* slots,
int num_slots, const int* skips,
int num_skips) {
FileDescriptor fd(open(filename_.c_str(), O_RDONLY));
if (fd.get() < 0)
return "file open error";
scoped_array<ProfileDataSlot> filedata(new ProfileDataSlot[num_slots]);
size_t expected_bytes = num_slots * sizeof filedata[0];
ssize_t bytes_read = ReadPersistent(fd.get(), filedata.get(), expected_bytes);
if (expected_bytes != bytes_read)
return "file too small";
for (int i = 0; i < num_slots; i++) {
if (num_skips > 0 && *skips == i) {
num_skips--;
skips++;
continue;
}
if (slots[i] != filedata[i])
return "data mismatch";
}
return kNoError;
}
string ProfileDataChecker::ValidateProfile() {
FileDescriptor fd(open(filename_.c_str(), O_RDONLY));
if (fd.get() < 0)
return "file open error";
struct stat statbuf;
if (fstat(fd.get(), &statbuf) != 0)
return "fstat error";
if (statbuf.st_size != static_cast<ssize_t>(statbuf.st_size))
return "file impossibly large";
ssize_t filesize = statbuf.st_size;
scoped_array<char> filedata(new char[filesize]);
if (ReadPersistent(fd.get(), filedata.get(), filesize) != filesize)
return "read of whole file failed";
if (filesize < (5 + 3) * sizeof(ProfileDataSlot))
return "not enough data in profile for header + trailer";
if (reinterpret_cast<ProfileDataSlot*>(filedata.get())[0] != 0)
return "error in header: non-zero count";
if (reinterpret_cast<ProfileDataSlot*>(filedata.get())[1] != 3)
return "error in header: num_slots != 3";
if (reinterpret_cast<ProfileDataSlot*>(filedata.get())[2] != 0)
return "error in header: non-zero format version";
if (reinterpret_cast<ProfileDataSlot*>(filedata.get())[4] != 0)
return "error in header: non-zero padding value";
ssize_t cur_offset = 5 * sizeof(ProfileDataSlot);
bool seen_trailer = false;
while (!seen_trailer) {
if (cur_offset > filesize - 3 * sizeof(ProfileDataSlot))
return "truncated sample header";
ProfileDataSlot* sample =
reinterpret_cast<ProfileDataSlot*>(filedata.get() + cur_offset);
ProfileDataSlot slots_this_sample = 2 + sample[1];
ssize_t size_this_sample = slots_this_sample * sizeof(ProfileDataSlot);
if (cur_offset > filesize - size_this_sample)
return "truncated sample";
if (sample[0] == 0 && sample[1] == 1 && sample[2] == 0) {
seen_trailer = true;
} else {
if (sample[0] < 1)
return "error in sample: sample count < 1";
if (sample[1] < 1)
return "error in sample: num_pcs < 1";
for (int i = 2; i < slots_this_sample; i++) {
if (sample[i] == 0)
return "error in sample: NULL PC";
}
}
cur_offset += size_this_sample;
}
if (cur_offset >= filesize)
return "no list of mapped objects";
if (filedata[filesize - 1] != '\n')
return "profile did not end with a complete line";
while (cur_offset < filesize) {
char* line_start = filedata.get() + cur_offset;
char* line_end = strchr(line_start, '\n');
*line_end = '\0';
bool has_leading_space = false;
char* line_cur = line_start;
while (*line_cur == ' ') {
has_leading_space = true;
line_cur++;
}
bool found_match = false;
if (!found_match) {
found_match = (strncmp(line_cur, "build=", 6) == 0);
}
if (!found_match) {
int chars_scanned = -1;
sscanf(line_cur, "%*x-%*x %*c%*c%*c%*c %*x %*x:%*x %*d %n",
&chars_scanned);
found_match = (chars_scanned > 0 && !has_leading_space);
}
if (!found_match) {
int chars_scanned = -1;
sscanf(line_cur, "%*x-%*x: %n", &chars_scanned);
found_match = (chars_scanned > 0);
}
if (!found_match)
return "unrecognized line in text section";
cur_offset += (line_end - line_start) + 1;
}
return kNoError;
}
class ProfileDataTest {
protected:
void ExpectStopped() {
EXPECT_FALSE(collector_.enabled());
}
void ExpectRunningSamples(int samples) {
ProfileData::State state;
collector_.GetCurrentState(&state);
EXPECT_TRUE(state.enabled);
EXPECT_EQ(samples, state.samples_gathered);
}
void ExpectSameState(const ProfileData::State& before,
const ProfileData::State& after) {
EXPECT_EQ(before.enabled, after.enabled);
EXPECT_EQ(before.samples_gathered, after.samples_gathered);
EXPECT_EQ(before.start_time, after.start_time);
EXPECT_STREQ(before.profile_name, after.profile_name);
}
ProfileData collector_;
ProfileDataChecker checker_;
private:
void OpsWhenStopped();
void StartStopEmpty();
void StartStopNoOptionsEmpty();
void StartWhenStarted();
void StartStopEmpty2();
void CollectOne();
void CollectTwoMatching();
void CollectTwoFlush();
void StartResetRestart();
public:
#define RUN(test) do { \
printf("Running %s\n", #test); \
ProfileDataTest pdt; \
pdt.test(); \
} while (0)
static int RUN_ALL_TESTS() {
RUN(OpsWhenStopped);
RUN(StartStopEmpty);
RUN(StartWhenStarted);
RUN(StartStopEmpty2);
RUN(CollectOne);
RUN(CollectTwoMatching);
RUN(CollectTwoFlush);
RUN(StartResetRestart);
return 0;
}
};
TEST_F(ProfileDataTest, OpsWhenStopped) {
ExpectStopped();
EXPECT_FALSE(collector_.enabled());
ProfileData::State state_before;
collector_.GetCurrentState(&state_before);
EXPECT_FALSE(state_before.enabled);
EXPECT_EQ(0, state_before.samples_gathered);
EXPECT_EQ(0, state_before.start_time);
EXPECT_STREQ("", state_before.profile_name);
collector_.Stop();
collector_.FlushTable();
const void *trace[] = { V(100), V(101), V(102), V(103), V(104) };
collector_.Add(arraysize(trace), trace);
ProfileData::State state_after;
collector_.GetCurrentState(&state_after);
ExpectSameState(state_before, state_after);
}
TEST_F(ProfileDataTest, StartStopEmpty) {
const int frequency = 1;
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0,
0, 1, 0
};
ExpectStopped();
ProfileData::Options options;
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
TEST_F(ProfileDataTest, StartStopNoOptionsEmpty) {
ProfileDataSlot slots[] = {
0, 3, 0, 0 , 0,
0, 1, 0
};
int slots_to_skip[] = { 3 };
ExpectStopped();
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(),
ProfileData::Options()));
ExpectRunningSamples(0);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.CheckWithSkips(slots, arraysize(slots),
slots_to_skip,
arraysize(slots_to_skip)));
}
TEST_F(ProfileDataTest, StartWhenStarted) {
const int frequency = 1;
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0,
0, 1, 0
};
ProfileData::Options options;
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ProfileData::State state_before;
collector_.GetCurrentState(&state_before);
options.set_frequency(frequency * 2);
CHECK(!collector_.Start("foobar", options));
ProfileData::State state_after;
collector_.GetCurrentState(&state_after);
ExpectSameState(state_before, state_after);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
TEST_F(ProfileDataTest, StartStopEmpty2) {
const int frequency = 2;
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0,
0, 1, 0
};
ExpectStopped();
ProfileData::Options options;
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
TEST_F(ProfileDataTest, CollectOne) {
const int frequency = 2;
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0,
1, 5, 100, 101, 102, 103, 104,
0, 1, 0
};
ExpectStopped();
ProfileData::Options options;
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
const void *trace[] = { V(100), V(101), V(102), V(103), V(104) };
collector_.Add(arraysize(trace), trace);
ExpectRunningSamples(1);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
TEST_F(ProfileDataTest, CollectTwoMatching) {
const int frequency = 2;
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0,
2, 5, 100, 201, 302, 403, 504,
0, 1, 0
};
ExpectStopped();
ProfileData::Options options;
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
for (int i = 0; i < 2; ++i) {
const void *trace[] = { V(100), V(201), V(302), V(403), V(504) };
collector_.Add(arraysize(trace), trace);
ExpectRunningSamples(i + 1);
}
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
TEST_F(ProfileDataTest, CollectTwoFlush) {
const int frequency = 2;
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0,
1, 5, 100, 201, 302, 403, 504,
1, 5, 100, 201, 302, 403, 504,
0, 1, 0
};
ExpectStopped();
ProfileData::Options options;
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
const void *trace[] = { V(100), V(201), V(302), V(403), V(504) };
collector_.Add(arraysize(trace), trace);
ExpectRunningSamples(1);
collector_.FlushTable();
collector_.Add(arraysize(trace), trace);
ExpectRunningSamples(2);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
TEST_F(ProfileDataTest, StartResetRestart) {
ExpectStopped();
ProfileData::Options options;
options.set_frequency(1);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
collector_.Reset();
ExpectStopped();
EXPECT_NE(kNoError, checker_.ValidateProfile());
struct stat statbuf;
EXPECT_EQ(0, stat(checker_.filename().c_str(), &statbuf));
EXPECT_EQ(0, statbuf.st_size);
const int frequency = 2;
ProfileDataSlot slots[] = {
0, 3, 0, 1000000 / frequency, 0,
0, 1, 0
};
options.set_frequency(frequency);
EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
ExpectRunningSamples(0);
collector_.Stop();
ExpectStopped();
EXPECT_EQ(kNoError, checker_.ValidateProfile());
EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
}
}
int main(int argc, char** argv) {
int rc = ProfileDataTest::RUN_ALL_TESTS();
printf("%s\n", rc == 0 ? "PASS" : "FAIL");
return rc;
}