mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-23 10:08:00 +02:00
610 lines
20 KiB
C++
610 lines
20 KiB
C++
// Copyright (c) 2007, Google Inc.
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
// in the documentation and/or other materials provided with the
|
|
// distribution.
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
// contributors may be used to endorse or promote products derived from
|
|
// this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// ---
|
|
// Author: Chris Demetriou
|
|
//
|
|
// This file contains the unit tests for the ProfileData class.
|
|
|
|
#if defined HAVE_STDINT_H
|
|
#include <stdint.h> // to get uintptr_t
|
|
#elif defined HAVE_INTTYPES_H
|
|
#include <inttypes.h> // another place uintptr_t might be defined
|
|
#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;
|
|
|
|
// Some helpful macros for the test class
|
|
#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_;
|
|
};
|
|
|
|
// Re-runs fn until it doesn't cause EINTR.
|
|
#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
|
|
|
|
// Read up to "count" bytes from file descriptor "fd" into the buffer
|
|
// starting at "buf" while handling short reads and EINTR. On
|
|
// success, return the number of bytes read. Otherwise, return -1.
|
|
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) { // There was an error other than EINTR.
|
|
return -1;
|
|
}
|
|
if (len == 0) { // Reached EOF.
|
|
break;
|
|
}
|
|
num_bytes += len;
|
|
}
|
|
CHECK(num_bytes <= count);
|
|
return num_bytes;
|
|
}
|
|
|
|
// Thin wrapper around a file descriptor so that the file descriptor
|
|
// gets closed for sure.
|
|
struct FileDescriptor {
|
|
const int fd_;
|
|
explicit FileDescriptor(int fd) : fd_(fd) {}
|
|
~FileDescriptor() {
|
|
if (fd_ >= 0) {
|
|
NO_INTR(close(fd_));
|
|
}
|
|
}
|
|
int get() { return fd_; }
|
|
};
|
|
|
|
// must be the same as with ProfileData::Slot.
|
|
typedef uintptr_t ProfileDataSlot;
|
|
|
|
// Quick and dirty function to make a number into a void* for use in a
|
|
// sample.
|
|
inline void* V(intptr_t x) { return reinterpret_cast<void*>(x); }
|
|
|
|
// String returned by ProfileDataChecker helper functions to indicate success.
|
|
const char kNoError[] = "";
|
|
|
|
class ProfileDataChecker {
|
|
public:
|
|
ProfileDataChecker() {
|
|
const char* tmpdir = getenv("TMPDIR");
|
|
if (tmpdir == NULL)
|
|
tmpdir = "/tmp";
|
|
mkdir(tmpdir, 0755); // if necessary
|
|
filename_ = string(tmpdir) + "/profiledata_unittest.tmp";
|
|
}
|
|
|
|
string filename() const { return filename_; }
|
|
|
|
// Checks the first 'num_slots' profile data slots in the file
|
|
// against the data pointed to by 'slots'. Returns kNoError if the
|
|
// data matched, otherwise returns an indication of the cause of the
|
|
// mismatch.
|
|
string Check(const ProfileDataSlot* slots, int num_slots) {
|
|
return CheckWithSkips(slots, num_slots, NULL, 0);
|
|
}
|
|
|
|
// Checks the first 'num_slots' profile data slots in the file
|
|
// against the data pointed to by 'slots', skipping over entries
|
|
// described by 'skips' and 'num_skips'.
|
|
//
|
|
// 'skips' must be a sorted list of (0-based) slot numbers to be
|
|
// skipped, of length 'num_skips'. Note that 'num_slots' includes
|
|
// any skipped slots, i.e., the first 'num_slots' profile data slots
|
|
// will be considered, but some may be skipped.
|
|
//
|
|
// Returns kNoError if the data matched, otherwise returns an
|
|
// indication of the cause of the mismatch.
|
|
string CheckWithSkips(const ProfileDataSlot* slots, int num_slots,
|
|
const int* skips, int num_skips);
|
|
|
|
// Validate that a profile is correctly formed. The profile is
|
|
// assumed to have been created by the same kind of binary (e.g.,
|
|
// same slot size, same endian, etc.) as is validating the profile.
|
|
//
|
|
// Returns kNoError if the profile appears valid, otherwise returns
|
|
// an indication of the problem with the profile.
|
|
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";
|
|
|
|
// Must have enough data for the header and the trailer.
|
|
if (filesize < (5 + 3) * sizeof(ProfileDataSlot))
|
|
return "not enough data in profile for header + trailer";
|
|
|
|
// Check the header
|
|
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";
|
|
// Period (slot 3) can have any value.
|
|
if (reinterpret_cast<ProfileDataSlot*>(filedata.get())[4] != 0)
|
|
return "error in header: non-zero padding value";
|
|
ssize_t cur_offset = 5 * sizeof(ProfileDataSlot);
|
|
|
|
// While there are samples, skip them. Each sample consists of
|
|
// at least three slots.
|
|
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;
|
|
}
|
|
|
|
// There must be at least one line in the (text) list of mapped objects,
|
|
// and it must be terminated by a newline. Note, the use of newline
|
|
// here and below Might not be reasonable on non-UNIX systems.
|
|
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;
|
|
|
|
// Find the end of the line, and replace it with a NUL for easier
|
|
// scanning.
|
|
char* line_end = strchr(line_start, '\n');
|
|
*line_end = '\0';
|
|
|
|
// Advance past any leading space. It's allowed in some lines,
|
|
// but not in others.
|
|
bool has_leading_space = false;
|
|
char* line_cur = line_start;
|
|
while (*line_cur == ' ') {
|
|
has_leading_space = true;
|
|
line_cur++;
|
|
}
|
|
|
|
bool found_match = false;
|
|
|
|
// Check for build lines.
|
|
if (!found_match) {
|
|
found_match = (strncmp(line_cur, "build=", 6) == 0);
|
|
// Anything may follow "build=", and leading space is allowed.
|
|
}
|
|
|
|
// A line from ProcMapsIterator::FormatLine, of the form:
|
|
//
|
|
// 40000000-40015000 r-xp 00000000 03:01 12845071 /lib/ld-2.3.2.so
|
|
//
|
|
// Leading space is not allowed. The filename may be omitted or
|
|
// may consist of multiple words, so we scan only up to the
|
|
// space before the filename.
|
|
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);
|
|
}
|
|
|
|
// A line from DumpAddressMap, of the form:
|
|
//
|
|
// 40000000-40015000: /lib/ld-2.3.2.so
|
|
//
|
|
// Leading space is allowed. The filename may be omitted or may
|
|
// consist of multiple words, so we scan only up to the space
|
|
// before the filename.
|
|
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:
|
|
// The tests to run
|
|
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;
|
|
}
|
|
};
|
|
|
|
// Check that various operations are safe when stopped.
|
|
TEST_F(ProfileDataTest, OpsWhenStopped) {
|
|
ExpectStopped();
|
|
EXPECT_FALSE(collector_.enabled());
|
|
|
|
// Verify that state is disabled, all-empty/all-0
|
|
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);
|
|
|
|
// Safe to call stop again.
|
|
collector_.Stop();
|
|
|
|
// Safe to call FlushTable.
|
|
collector_.FlushTable();
|
|
|
|
// Safe to call Add.
|
|
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);
|
|
}
|
|
|
|
// Start and Stop, collecting no samples. Verify output contents.
|
|
TEST_F(ProfileDataTest, StartStopEmpty) {
|
|
const int frequency = 1;
|
|
ProfileDataSlot slots[] = {
|
|
0, 3, 0, 1000000 / frequency, 0, // binary header
|
|
0, 1, 0 // binary trailer
|
|
};
|
|
|
|
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)));
|
|
}
|
|
|
|
// Start and Stop with no options, collecting no samples. Verify
|
|
// output contents.
|
|
TEST_F(ProfileDataTest, StartStopNoOptionsEmpty) {
|
|
// We're not requesting a specific period, implementation can do
|
|
// whatever it likes.
|
|
ProfileDataSlot slots[] = {
|
|
0, 3, 0, 0 /* skipped */, 0, // binary header
|
|
0, 1, 0 // binary trailer
|
|
};
|
|
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)));
|
|
}
|
|
|
|
// Start after already started. Should return false and not impact
|
|
// collected data or state.
|
|
TEST_F(ProfileDataTest, StartWhenStarted) {
|
|
const int frequency = 1;
|
|
ProfileDataSlot slots[] = {
|
|
0, 3, 0, 1000000 / frequency, 0, // binary header
|
|
0, 1, 0 // binary trailer
|
|
};
|
|
|
|
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)));
|
|
}
|
|
|
|
// Like StartStopEmpty, but uses a different file name and frequency.
|
|
TEST_F(ProfileDataTest, StartStopEmpty2) {
|
|
const int frequency = 2;
|
|
ProfileDataSlot slots[] = {
|
|
0, 3, 0, 1000000 / frequency, 0, // binary header
|
|
0, 1, 0 // binary trailer
|
|
};
|
|
|
|
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, // binary header
|
|
1, 5, 100, 101, 102, 103, 104, // our sample
|
|
0, 1, 0 // binary trailer
|
|
};
|
|
|
|
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, // binary header
|
|
2, 5, 100, 201, 302, 403, 504, // our two samples
|
|
0, 1, 0 // binary trailer
|
|
};
|
|
|
|
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, // binary header
|
|
1, 5, 100, 201, 302, 403, 504, // first sample (flushed)
|
|
1, 5, 100, 201, 302, 403, 504, // second identical sample
|
|
0, 1, 0 // binary trailer
|
|
};
|
|
|
|
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)));
|
|
}
|
|
|
|
// Start then reset, verify that the result is *not* a valid profile.
|
|
// Then start again and make sure the result is OK.
|
|
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();
|
|
// We expect the resulting file to be empty. This is a minimal test
|
|
// of ValidateProfile.
|
|
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; // Different frequency than used above.
|
|
ProfileDataSlot slots[] = {
|
|
0, 3, 0, 1000000 / frequency, 0, // binary header
|
|
0, 1, 0 // binary trailer
|
|
};
|
|
|
|
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)));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char** argv) {
|
|
int rc = ProfileDataTest::RUN_ALL_TESTS();
|
|
printf("%s\n", rc == 0 ? "PASS" : "FAIL");
|
|
return rc;
|
|
}
|