123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
- // 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;
- }
|