Home | History | Annotate | Download | only in tests
      1 // Copyright (c) 2007, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 //
     30 // ---
     31 // Author: Chris Demetriou
     32 //
     33 // This file contains the unit tests for the ProfileData class.
     34 
     35 #if defined HAVE_STDINT_H
     36 #include <stdint.h>             // to get uintptr_t
     37 #elif defined HAVE_INTTYPES_H
     38 #include <inttypes.h>           // another place uintptr_t might be defined
     39 #endif
     40 #include <sys/stat.h>
     41 #include <sys/types.h>
     42 #include <fcntl.h>
     43 #include <string.h>
     44 #include <string>
     45 
     46 #include "profiledata.h"
     47 
     48 #include "base/commandlineflags.h"
     49 #include "base/logging.h"
     50 
     51 using std::string;
     52 
     53 // Some helpful macros for the test class
     54 #define TEST_F(cls, fn)    void cls :: fn()
     55 
     56 namespace {
     57 
     58 template<typename T> class scoped_array {
     59  public:
     60   scoped_array(T* data) : data_(data) { }
     61   ~scoped_array() { delete[] data_; }
     62   T* get() { return data_; }
     63   T& operator[](int i) { return data_[i]; }
     64  private:
     65   T* const data_;
     66 };
     67 
     68 // Re-runs fn until it doesn't cause EINTR.
     69 #define NO_INTR(fn)   do {} while ((fn) < 0 && errno == EINTR)
     70 
     71 // Read up to "count" bytes from file descriptor "fd" into the buffer
     72 // starting at "buf" while handling short reads and EINTR.  On
     73 // success, return the number of bytes read.  Otherwise, return -1.
     74 static ssize_t ReadPersistent(const int fd, void *buf, const size_t count) {
     75   CHECK_GE(fd, 0);
     76   char *buf0 = reinterpret_cast<char *>(buf);
     77   ssize_t num_bytes = 0;
     78   while (num_bytes < count) {
     79     ssize_t len;
     80     NO_INTR(len = read(fd, buf0 + num_bytes, count - num_bytes));
     81     if (len < 0) {  // There was an error other than EINTR.
     82       return -1;
     83     }
     84     if (len == 0) {  // Reached EOF.
     85       break;
     86     }
     87     num_bytes += len;
     88   }
     89   CHECK(num_bytes <= count);
     90   return num_bytes;
     91 }
     92 
     93 // Thin wrapper around a file descriptor so that the file descriptor
     94 // gets closed for sure.
     95 struct FileDescriptor {
     96   const int fd_;
     97   explicit FileDescriptor(int fd) : fd_(fd) {}
     98   ~FileDescriptor() {
     99     if (fd_ >= 0) {
    100       NO_INTR(close(fd_));
    101     }
    102   }
    103   int get() { return fd_; }
    104 };
    105 
    106 // must be the same as with ProfileData::Slot.
    107 typedef uintptr_t ProfileDataSlot;
    108 
    109 // Quick and dirty function to make a number into a void* for use in a
    110 // sample.
    111 inline void* V(intptr_t x) { return reinterpret_cast<void*>(x); }
    112 
    113 // String returned by ProfileDataChecker helper functions to indicate success.
    114 const char kNoError[] = "";
    115 
    116 class ProfileDataChecker {
    117  public:
    118   ProfileDataChecker() {
    119     const char* tmpdir = getenv("TMPDIR");
    120     if (tmpdir == NULL)
    121       tmpdir = "/tmp";
    122     mkdir(tmpdir, 0755);     // if necessary
    123     filename_ = string(tmpdir) + "/profiledata_unittest.tmp";
    124   }
    125 
    126   string filename() const { return filename_; }
    127 
    128   // Checks the first 'num_slots' profile data slots in the file
    129   // against the data pointed to by 'slots'.  Returns kNoError if the
    130   // data matched, otherwise returns an indication of the cause of the
    131   // mismatch.
    132   string Check(const ProfileDataSlot* slots, int num_slots) {
    133     return CheckWithSkips(slots, num_slots, NULL, 0);
    134   }
    135 
    136   // Checks the first 'num_slots' profile data slots in the file
    137   // against the data pointed to by 'slots', skipping over entries
    138   // described by 'skips' and 'num_skips'.
    139   //
    140   // 'skips' must be a sorted list of (0-based) slot numbers to be
    141   // skipped, of length 'num_skips'.  Note that 'num_slots' includes
    142   // any skipped slots, i.e., the first 'num_slots' profile data slots
    143   // will be considered, but some may be skipped.
    144   //
    145   // Returns kNoError if the data matched, otherwise returns an
    146   // indication of the cause of the mismatch.
    147   string CheckWithSkips(const ProfileDataSlot* slots, int num_slots,
    148                         const int* skips, int num_skips);
    149 
    150   // Validate that a profile is correctly formed.  The profile is
    151   // assumed to have been created by the same kind of binary (e.g.,
    152   // same slot size, same endian, etc.) as is validating the profile.
    153   //
    154   // Returns kNoError if the profile appears valid, otherwise returns
    155   // an indication of the problem with the profile.
    156   string ValidateProfile();
    157 
    158  private:
    159   string filename_;
    160 };
    161 
    162 string ProfileDataChecker::CheckWithSkips(const ProfileDataSlot* slots,
    163                                           int num_slots, const int* skips,
    164                                           int num_skips) {
    165   FileDescriptor fd(open(filename_.c_str(), O_RDONLY));
    166   if (fd.get() < 0)
    167     return "file open error";
    168 
    169   scoped_array<ProfileDataSlot> filedata(new ProfileDataSlot[num_slots]);
    170   size_t expected_bytes = num_slots * sizeof filedata[0];
    171   ssize_t bytes_read = ReadPersistent(fd.get(), filedata.get(), expected_bytes);
    172   if (expected_bytes != bytes_read)
    173     return "file too small";
    174 
    175   for (int i = 0; i < num_slots; i++) {
    176     if (num_skips > 0 && *skips == i) {
    177       num_skips--;
    178       skips++;
    179       continue;
    180     }
    181     if (slots[i] != filedata[i])
    182       return "data mismatch";
    183   }
    184   return kNoError;
    185 }
    186 
    187 string ProfileDataChecker::ValidateProfile() {
    188   FileDescriptor fd(open(filename_.c_str(), O_RDONLY));
    189   if (fd.get() < 0)
    190     return "file open error";
    191 
    192   struct stat statbuf;
    193   if (fstat(fd.get(), &statbuf) != 0)
    194     return "fstat error";
    195   if (statbuf.st_size != static_cast<ssize_t>(statbuf.st_size))
    196     return "file impossibly large";
    197   ssize_t filesize = statbuf.st_size;
    198 
    199   scoped_array<char> filedata(new char[filesize]);
    200   if (ReadPersistent(fd.get(), filedata.get(), filesize) != filesize)
    201     return "read of whole file failed";
    202 
    203   // Must have enough data for the header and the trailer.
    204   if (filesize < (5 + 3) * sizeof(ProfileDataSlot))
    205     return "not enough data in profile for header + trailer";
    206 
    207   // Check the header
    208   if (reinterpret_cast<ProfileDataSlot*>(filedata.get())[0] != 0)
    209     return "error in header: non-zero count";
    210   if (reinterpret_cast<ProfileDataSlot*>(filedata.get())[1] != 3)
    211     return "error in header: num_slots != 3";
    212   if (reinterpret_cast<ProfileDataSlot*>(filedata.get())[2] != 0)
    213     return "error in header: non-zero format version";
    214   // Period (slot 3) can have any value.
    215   if (reinterpret_cast<ProfileDataSlot*>(filedata.get())[4] != 0)
    216     return "error in header: non-zero padding value";
    217   ssize_t cur_offset = 5 * sizeof(ProfileDataSlot);
    218 
    219   // While there are samples, skip them.  Each sample consists of
    220   // at least three slots.
    221   bool seen_trailer = false;
    222   while (!seen_trailer) {
    223     if (cur_offset > filesize - 3 * sizeof(ProfileDataSlot))
    224       return "truncated sample header";
    225     ProfileDataSlot* sample =
    226         reinterpret_cast<ProfileDataSlot*>(filedata.get() + cur_offset);
    227     ProfileDataSlot slots_this_sample = 2 + sample[1];
    228     ssize_t size_this_sample = slots_this_sample * sizeof(ProfileDataSlot);
    229     if (cur_offset > filesize - size_this_sample)
    230       return "truncated sample";
    231 
    232     if (sample[0] == 0 && sample[1] == 1 && sample[2] == 0) {
    233       seen_trailer = true;
    234     } else {
    235       if (sample[0] < 1)
    236         return "error in sample: sample count < 1";
    237       if (sample[1] < 1)
    238         return "error in sample: num_pcs < 1";
    239       for (int i = 2; i < slots_this_sample; i++) {
    240         if (sample[i] == 0)
    241           return "error in sample: NULL PC";
    242       }
    243     }
    244     cur_offset += size_this_sample;
    245   }
    246 
    247   // There must be at least one line in the (text) list of mapped objects,
    248   // and it must be terminated by a newline.  Note, the use of newline
    249   // here and below Might not be reasonable on non-UNIX systems.
    250   if (cur_offset >= filesize)
    251     return "no list of mapped objects";
    252   if (filedata[filesize - 1] != '\n')
    253     return "profile did not end with a complete line";
    254 
    255   while (cur_offset < filesize) {
    256     char* line_start = filedata.get() + cur_offset;
    257 
    258     // Find the end of the line, and replace it with a NUL for easier
    259     // scanning.
    260     char* line_end = strchr(line_start, '\n');
    261     *line_end = '\0';
    262 
    263     // Advance past any leading space.  It's allowed in some lines,
    264     // but not in others.
    265     bool has_leading_space = false;
    266     char* line_cur = line_start;
    267     while (*line_cur == ' ') {
    268       has_leading_space = true;
    269       line_cur++;
    270     }
    271 
    272     bool found_match = false;
    273 
    274     // Check for build lines.
    275     if (!found_match) {
    276       found_match = (strncmp(line_cur, "build=", 6) == 0);
    277       // Anything may follow "build=", and leading space is allowed.
    278     }
    279 
    280     // A line from ProcMapsIterator::FormatLine, of the form:
    281     //
    282     // 40000000-40015000 r-xp 00000000 03:01 12845071   /lib/ld-2.3.2.so
    283     //
    284     // Leading space is not allowed.  The filename may be omitted or
    285     // may consist of multiple words, so we scan only up to the
    286     // space before the filename.
    287     if (!found_match) {
    288       int chars_scanned = -1;
    289       sscanf(line_cur, "%*x-%*x %*c%*c%*c%*c %*x %*x:%*x %*d %n",
    290              &chars_scanned);
    291       found_match = (chars_scanned > 0 && !has_leading_space);
    292     }
    293 
    294     // A line from DumpAddressMap, of the form:
    295     //
    296     // 40000000-40015000: /lib/ld-2.3.2.so
    297     //
    298     // Leading space is allowed.  The filename may be omitted or may
    299     // consist of multiple words, so we scan only up to the space
    300     // before the filename.
    301     if (!found_match) {
    302       int chars_scanned = -1;
    303       sscanf(line_cur, "%*x-%*x: %n", &chars_scanned);
    304       found_match = (chars_scanned > 0);
    305     }
    306 
    307     if (!found_match)
    308       return "unrecognized line in text section";
    309 
    310     cur_offset += (line_end - line_start) + 1;
    311   }
    312 
    313   return kNoError;
    314 }
    315 
    316 class ProfileDataTest {
    317  protected:
    318   void ExpectStopped() {
    319     EXPECT_FALSE(collector_.enabled());
    320   }
    321 
    322   void ExpectRunningSamples(int samples) {
    323     ProfileData::State state;
    324     collector_.GetCurrentState(&state);
    325     EXPECT_TRUE(state.enabled);
    326     EXPECT_EQ(samples, state.samples_gathered);
    327   }
    328 
    329   void ExpectSameState(const ProfileData::State& before,
    330                        const ProfileData::State& after) {
    331     EXPECT_EQ(before.enabled, after.enabled);
    332     EXPECT_EQ(before.samples_gathered, after.samples_gathered);
    333     EXPECT_EQ(before.start_time, after.start_time);
    334     EXPECT_STREQ(before.profile_name, after.profile_name);
    335   }
    336 
    337   ProfileData        collector_;
    338   ProfileDataChecker checker_;
    339 
    340  private:
    341   // The tests to run
    342   void OpsWhenStopped();
    343   void StartStopEmpty();
    344   void StartStopNoOptionsEmpty();
    345   void StartWhenStarted();
    346   void StartStopEmpty2();
    347   void CollectOne();
    348   void CollectTwoMatching();
    349   void CollectTwoFlush();
    350   void StartResetRestart();
    351 
    352  public:
    353 #define RUN(test)  do {                         \
    354     printf("Running %s\n", #test);              \
    355     ProfileDataTest pdt;                        \
    356     pdt.test();                                 \
    357 } while (0)
    358 
    359   static int RUN_ALL_TESTS() {
    360     RUN(OpsWhenStopped);
    361     RUN(StartStopEmpty);
    362     RUN(StartWhenStarted);
    363     RUN(StartStopEmpty2);
    364     RUN(CollectOne);
    365     RUN(CollectTwoMatching);
    366     RUN(CollectTwoFlush);
    367     RUN(StartResetRestart);
    368     return 0;
    369   }
    370 };
    371 
    372 // Check that various operations are safe when stopped.
    373 TEST_F(ProfileDataTest, OpsWhenStopped) {
    374   ExpectStopped();
    375   EXPECT_FALSE(collector_.enabled());
    376 
    377   // Verify that state is disabled, all-empty/all-0
    378   ProfileData::State state_before;
    379   collector_.GetCurrentState(&state_before);
    380   EXPECT_FALSE(state_before.enabled);
    381   EXPECT_EQ(0, state_before.samples_gathered);
    382   EXPECT_EQ(0, state_before.start_time);
    383   EXPECT_STREQ("", state_before.profile_name);
    384 
    385   // Safe to call stop again.
    386   collector_.Stop();
    387 
    388   // Safe to call FlushTable.
    389   collector_.FlushTable();
    390 
    391   // Safe to call Add.
    392   const void *trace[] = { V(100), V(101), V(102), V(103), V(104) };
    393   collector_.Add(arraysize(trace), trace);
    394 
    395   ProfileData::State state_after;
    396   collector_.GetCurrentState(&state_after);
    397 
    398   ExpectSameState(state_before, state_after);
    399 }
    400 
    401 // Start and Stop, collecting no samples.  Verify output contents.
    402 TEST_F(ProfileDataTest, StartStopEmpty) {
    403   const int frequency = 1;
    404   ProfileDataSlot slots[] = {
    405     0, 3, 0, 1000000 / frequency, 0,    // binary header
    406     0, 1, 0                             // binary trailer
    407   };
    408 
    409   ExpectStopped();
    410   ProfileData::Options options;
    411   options.set_frequency(frequency);
    412   EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
    413   ExpectRunningSamples(0);
    414   collector_.Stop();
    415   ExpectStopped();
    416   EXPECT_EQ(kNoError, checker_.ValidateProfile());
    417   EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
    418 }
    419 
    420 // Start and Stop with no options, collecting no samples.  Verify
    421 // output contents.
    422 TEST_F(ProfileDataTest, StartStopNoOptionsEmpty) {
    423   // We're not requesting a specific period, implementation can do
    424   // whatever it likes.
    425   ProfileDataSlot slots[] = {
    426     0, 3, 0, 0 /* skipped */, 0,        // binary header
    427     0, 1, 0                             // binary trailer
    428   };
    429   int slots_to_skip[] = { 3 };
    430 
    431   ExpectStopped();
    432   EXPECT_TRUE(collector_.Start(checker_.filename().c_str(),
    433                                ProfileData::Options()));
    434   ExpectRunningSamples(0);
    435   collector_.Stop();
    436   ExpectStopped();
    437   EXPECT_EQ(kNoError, checker_.ValidateProfile());
    438   EXPECT_EQ(kNoError, checker_.CheckWithSkips(slots, arraysize(slots),
    439                                               slots_to_skip,
    440                                               arraysize(slots_to_skip)));
    441 }
    442 
    443 // Start after already started.  Should return false and not impact
    444 // collected data or state.
    445 TEST_F(ProfileDataTest, StartWhenStarted) {
    446   const int frequency = 1;
    447   ProfileDataSlot slots[] = {
    448     0, 3, 0, 1000000 / frequency, 0,    // binary header
    449     0, 1, 0                             // binary trailer
    450   };
    451 
    452   ProfileData::Options options;
    453   options.set_frequency(frequency);
    454   EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
    455 
    456   ProfileData::State state_before;
    457   collector_.GetCurrentState(&state_before);
    458 
    459   options.set_frequency(frequency * 2);
    460   CHECK(!collector_.Start("foobar", options));
    461 
    462   ProfileData::State state_after;
    463   collector_.GetCurrentState(&state_after);
    464   ExpectSameState(state_before, state_after);
    465 
    466   collector_.Stop();
    467   ExpectStopped();
    468   EXPECT_EQ(kNoError, checker_.ValidateProfile());
    469   EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
    470 }
    471 
    472 // Like StartStopEmpty, but uses a different file name and frequency.
    473 TEST_F(ProfileDataTest, StartStopEmpty2) {
    474   const int frequency = 2;
    475   ProfileDataSlot slots[] = {
    476     0, 3, 0, 1000000 / frequency, 0,    // binary header
    477     0, 1, 0                             // binary trailer
    478   };
    479 
    480   ExpectStopped();
    481   ProfileData::Options options;
    482   options.set_frequency(frequency);
    483   EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
    484   ExpectRunningSamples(0);
    485   collector_.Stop();
    486   ExpectStopped();
    487   EXPECT_EQ(kNoError, checker_.ValidateProfile());
    488   EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
    489 }
    490 
    491 TEST_F(ProfileDataTest, CollectOne) {
    492   const int frequency = 2;
    493   ProfileDataSlot slots[] = {
    494     0, 3, 0, 1000000 / frequency, 0,    // binary header
    495     1, 5, 100, 101, 102, 103, 104,      // our sample
    496     0, 1, 0                             // binary trailer
    497   };
    498 
    499   ExpectStopped();
    500   ProfileData::Options options;
    501   options.set_frequency(frequency);
    502   EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
    503   ExpectRunningSamples(0);
    504 
    505   const void *trace[] = { V(100), V(101), V(102), V(103), V(104) };
    506   collector_.Add(arraysize(trace), trace);
    507   ExpectRunningSamples(1);
    508 
    509   collector_.Stop();
    510   ExpectStopped();
    511   EXPECT_EQ(kNoError, checker_.ValidateProfile());
    512   EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
    513 }
    514 
    515 TEST_F(ProfileDataTest, CollectTwoMatching) {
    516   const int frequency = 2;
    517   ProfileDataSlot slots[] = {
    518     0, 3, 0, 1000000 / frequency, 0,    // binary header
    519     2, 5, 100, 201, 302, 403, 504,      // our two samples
    520     0, 1, 0                             // binary trailer
    521   };
    522 
    523   ExpectStopped();
    524   ProfileData::Options options;
    525   options.set_frequency(frequency);
    526   EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
    527   ExpectRunningSamples(0);
    528 
    529   for (int i = 0; i < 2; ++i) {
    530     const void *trace[] = { V(100), V(201), V(302), V(403), V(504) };
    531     collector_.Add(arraysize(trace), trace);
    532     ExpectRunningSamples(i + 1);
    533   }
    534 
    535   collector_.Stop();
    536   ExpectStopped();
    537   EXPECT_EQ(kNoError, checker_.ValidateProfile());
    538   EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
    539 }
    540 
    541 TEST_F(ProfileDataTest, CollectTwoFlush) {
    542   const int frequency = 2;
    543   ProfileDataSlot slots[] = {
    544     0, 3, 0, 1000000 / frequency, 0,    // binary header
    545     1, 5, 100, 201, 302, 403, 504,      // first sample (flushed)
    546     1, 5, 100, 201, 302, 403, 504,      // second identical sample
    547     0, 1, 0                             // binary trailer
    548   };
    549 
    550   ExpectStopped();
    551   ProfileData::Options options;
    552   options.set_frequency(frequency);
    553   EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
    554   ExpectRunningSamples(0);
    555 
    556   const void *trace[] = { V(100), V(201), V(302), V(403), V(504) };
    557 
    558   collector_.Add(arraysize(trace), trace);
    559   ExpectRunningSamples(1);
    560   collector_.FlushTable();
    561 
    562   collector_.Add(arraysize(trace), trace);
    563   ExpectRunningSamples(2);
    564 
    565   collector_.Stop();
    566   ExpectStopped();
    567   EXPECT_EQ(kNoError, checker_.ValidateProfile());
    568   EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
    569 }
    570 
    571 // Start then reset, verify that the result is *not* a valid profile.
    572 // Then start again and make sure the result is OK.
    573 TEST_F(ProfileDataTest, StartResetRestart) {
    574   ExpectStopped();
    575   ProfileData::Options options;
    576   options.set_frequency(1);
    577   EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
    578   ExpectRunningSamples(0);
    579   collector_.Reset();
    580   ExpectStopped();
    581   // We expect the resulting file to be empty.  This is a minimal test
    582   // of ValidateProfile.
    583   EXPECT_NE(kNoError, checker_.ValidateProfile());
    584 
    585   struct stat statbuf;
    586   EXPECT_EQ(0, stat(checker_.filename().c_str(), &statbuf));
    587   EXPECT_EQ(0, statbuf.st_size);
    588 
    589   const int frequency = 2;  // Different frequency than used above.
    590   ProfileDataSlot slots[] = {
    591     0, 3, 0, 1000000 / frequency, 0,    // binary header
    592     0, 1, 0                             // binary trailer
    593   };
    594 
    595   options.set_frequency(frequency);
    596   EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
    597   ExpectRunningSamples(0);
    598   collector_.Stop();
    599   ExpectStopped();
    600   EXPECT_EQ(kNoError, checker_.ValidateProfile());
    601   EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
    602 }
    603 
    604 }  // namespace
    605 
    606 int main(int argc, char** argv) {
    607   int rc = ProfileDataTest::RUN_ALL_TESTS();
    608   printf("%s\n", rc == 0 ? "PASS" : "FAIL");
    609   return rc;
    610 }
    611