Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / third_party / tcmalloc / chromium / src / tests / profiledata_unittest.cc
blobf569f64b6ab287184c6cd553214a97a9b2569df7
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.
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.
30 // ---
31 // Author: Chris Demetriou
33 // This file contains the unit tests for the ProfileData class.
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>
46 #include "profiledata.h"
48 #include "base/commandlineflags.h"
49 #include "base/logging.h"
51 using std::string;
53 // Some helpful macros for the test class
54 #define TEST_F(cls, fn) void cls :: fn()
56 namespace {
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_;
68 // Re-runs fn until it doesn't cause EINTR.
69 #define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
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;
84 if (len == 0) { // Reached EOF.
85 break;
87 num_bytes += len;
89 CHECK(num_bytes <= count);
90 return num_bytes;
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_));
103 int get() { return fd_; }
106 // must be the same as with ProfileData::Slot.
107 typedef uintptr_t ProfileDataSlot;
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); }
113 // String returned by ProfileDataChecker helper functions to indicate success.
114 const char kNoError[] = "";
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";
126 string filename() const { return filename_; }
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);
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'.
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.
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);
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.
154 // Returns kNoError if the profile appears valid, otherwise returns
155 // an indication of the problem with the profile.
156 string ValidateProfile();
158 private:
159 string filename_;
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";
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";
175 for (int i = 0; i < num_slots; i++) {
176 if (num_skips > 0 && *skips == i) {
177 num_skips--;
178 skips++;
179 continue;
181 if (slots[i] != filedata[i])
182 return "data mismatch";
184 return kNoError;
187 string ProfileDataChecker::ValidateProfile() {
188 FileDescriptor fd(open(filename_.c_str(), O_RDONLY));
189 if (fd.get() < 0)
190 return "file open error";
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;
199 scoped_array<char> filedata(new char[filesize]);
200 if (ReadPersistent(fd.get(), filedata.get(), filesize) != filesize)
201 return "read of whole file failed";
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";
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);
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";
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";
244 cur_offset += size_this_sample;
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";
255 while (cur_offset < filesize) {
256 char* line_start = filedata.get() + cur_offset;
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';
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++;
272 bool found_match = false;
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.
280 // A line from ProcMapsIterator::FormatLine, of the form:
282 // 40000000-40015000 r-xp 00000000 03:01 12845071 /lib/ld-2.3.2.so
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);
294 // A line from DumpAddressMap, of the form:
296 // 40000000-40015000: /lib/ld-2.3.2.so
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);
307 if (!found_match)
308 return "unrecognized line in text section";
310 cur_offset += (line_end - line_start) + 1;
313 return kNoError;
316 class ProfileDataTest {
317 protected:
318 void ExpectStopped() {
319 EXPECT_FALSE(collector_.enabled());
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);
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);
337 ProfileData collector_;
338 ProfileDataChecker checker_;
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();
352 public:
353 #define RUN(test) do { \
354 printf("Running %s\n", #test); \
355 ProfileDataTest pdt; \
356 pdt.test(); \
357 } while (0)
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;
372 // Check that various operations are safe when stopped.
373 TEST_F(ProfileDataTest, OpsWhenStopped) {
374 ExpectStopped();
375 EXPECT_FALSE(collector_.enabled());
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);
385 // Safe to call stop again.
386 collector_.Stop();
388 // Safe to call FlushTable.
389 collector_.FlushTable();
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);
395 ProfileData::State state_after;
396 collector_.GetCurrentState(&state_after);
398 ExpectSameState(state_before, state_after);
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
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)));
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
429 int slots_to_skip[] = { 3 };
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)));
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
452 ProfileData::Options options;
453 options.set_frequency(frequency);
454 EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
456 ProfileData::State state_before;
457 collector_.GetCurrentState(&state_before);
459 options.set_frequency(frequency * 2);
460 CHECK(!collector_.Start("foobar", options));
462 ProfileData::State state_after;
463 collector_.GetCurrentState(&state_after);
464 ExpectSameState(state_before, state_after);
466 collector_.Stop();
467 ExpectStopped();
468 EXPECT_EQ(kNoError, checker_.ValidateProfile());
469 EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
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
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)));
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
499 ExpectStopped();
500 ProfileData::Options options;
501 options.set_frequency(frequency);
502 EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
503 ExpectRunningSamples(0);
505 const void *trace[] = { V(100), V(101), V(102), V(103), V(104) };
506 collector_.Add(arraysize(trace), trace);
507 ExpectRunningSamples(1);
509 collector_.Stop();
510 ExpectStopped();
511 EXPECT_EQ(kNoError, checker_.ValidateProfile());
512 EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
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
523 ExpectStopped();
524 ProfileData::Options options;
525 options.set_frequency(frequency);
526 EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
527 ExpectRunningSamples(0);
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);
535 collector_.Stop();
536 ExpectStopped();
537 EXPECT_EQ(kNoError, checker_.ValidateProfile());
538 EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
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
550 ExpectStopped();
551 ProfileData::Options options;
552 options.set_frequency(frequency);
553 EXPECT_TRUE(collector_.Start(checker_.filename().c_str(), options));
554 ExpectRunningSamples(0);
556 const void *trace[] = { V(100), V(201), V(302), V(403), V(504) };
558 collector_.Add(arraysize(trace), trace);
559 ExpectRunningSamples(1);
560 collector_.FlushTable();
562 collector_.Add(arraysize(trace), trace);
563 ExpectRunningSamples(2);
565 collector_.Stop();
566 ExpectStopped();
567 EXPECT_EQ(kNoError, checker_.ValidateProfile());
568 EXPECT_EQ(kNoError, checker_.Check(slots, arraysize(slots)));
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());
585 struct stat statbuf;
586 EXPECT_EQ(0, stat(checker_.filename().c_str(), &statbuf));
587 EXPECT_EQ(0, statbuf.st_size);
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
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)));
604 } // namespace
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;