| // 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: Sanjay Ghemawat | 
 | //         Chris Demetriou (refactoring) | 
 | // | 
 | // Collect profiling data. | 
 |  | 
 | #include <config.h> | 
 | #include <assert.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <errno.h> | 
 | #ifdef HAVE_UNISTD_H | 
 | #include <unistd.h> | 
 | #endif | 
 | #include <sys/time.h> | 
 | #include <string.h> | 
 | #include <fcntl.h> | 
 |  | 
 | #include "profiledata.h" | 
 |  | 
 | #include "base/logging.h" | 
 | #include "base/sysinfo.h" | 
 |  | 
 | // All of these are initialized in profiledata.h. | 
 | const int ProfileData::kMaxStackDepth; | 
 | const int ProfileData::kAssociativity; | 
 | const int ProfileData::kBuckets; | 
 | const int ProfileData::kBufferLength; | 
 |  | 
 | ProfileData::Options::Options() | 
 |     : frequency_(1) { | 
 | } | 
 |  | 
 | // This function is safe to call from asynchronous signals (but is not | 
 | // re-entrant).  However, that's not part of its public interface. | 
 | void ProfileData::Evict(const Entry& entry) { | 
 |   const int d = entry.depth; | 
 |   const int nslots = d + 2;     // Number of slots needed in eviction buffer | 
 |   if (num_evicted_ + nslots > kBufferLength) { | 
 |     FlushEvicted(); | 
 |     assert(num_evicted_ == 0); | 
 |     assert(nslots <= kBufferLength); | 
 |   } | 
 |   evict_[num_evicted_++] = entry.count; | 
 |   evict_[num_evicted_++] = d; | 
 |   memcpy(&evict_[num_evicted_], entry.stack, d * sizeof(Slot)); | 
 |   num_evicted_ += d; | 
 | } | 
 |  | 
 | ProfileData::ProfileData() | 
 |     : hash_(0), | 
 |       evict_(0), | 
 |       num_evicted_(0), | 
 |       out_(-1), | 
 |       count_(0), | 
 |       evictions_(0), | 
 |       total_bytes_(0), | 
 |       fname_(0), | 
 |       start_time_(0) { | 
 | } | 
 |  | 
 | bool ProfileData::Start(const char* fname, | 
 |                         const ProfileData::Options& options) { | 
 |   if (enabled()) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Open output file and initialize various data structures | 
 |   int fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0666); | 
 |   if (fd < 0) { | 
 |     // Can't open outfile for write | 
 |     return false; | 
 |   } | 
 |  | 
 |   start_time_ = time(NULL); | 
 |   fname_ = strdup(fname); | 
 |  | 
 |   // Reset counters | 
 |   num_evicted_ = 0; | 
 |   count_       = 0; | 
 |   evictions_   = 0; | 
 |   total_bytes_ = 0; | 
 |  | 
 |   hash_ = new Bucket[kBuckets]; | 
 |   evict_ = new Slot[kBufferLength]; | 
 |   memset(hash_, 0, sizeof(hash_[0]) * kBuckets); | 
 |  | 
 |   // Record special entries | 
 |   evict_[num_evicted_++] = 0;                     // count for header | 
 |   evict_[num_evicted_++] = 3;                     // depth for header | 
 |   evict_[num_evicted_++] = 0;                     // Version number | 
 |   CHECK_NE(0, options.frequency()); | 
 |   int period = 1000000 / options.frequency(); | 
 |   evict_[num_evicted_++] = period;                // Period (microseconds) | 
 |   evict_[num_evicted_++] = 0;                     // Padding | 
 |  | 
 |   out_ = fd; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | ProfileData::~ProfileData() { | 
 |   Stop(); | 
 | } | 
 |  | 
 | // Dump /proc/maps data to fd.  Copied from heap-profile-table.cc. | 
 | #define NO_INTR(fn)  do {} while ((fn) < 0 && errno == EINTR) | 
 |  | 
 | static void FDWrite(int fd, const char* buf, size_t len) { | 
 |   while (len > 0) { | 
 |     ssize_t r; | 
 |     NO_INTR(r = write(fd, buf, len)); | 
 |     RAW_CHECK(r >= 0, "write failed"); | 
 |     buf += r; | 
 |     len -= r; | 
 |   } | 
 | } | 
 |  | 
 | static void DumpProcSelfMaps(int fd) { | 
 |   ProcMapsIterator::Buffer iterbuf; | 
 |   ProcMapsIterator it(0, &iterbuf);   // 0 means "current pid" | 
 |  | 
 |   uint64 start, end, offset; | 
 |   int64 inode; | 
 |   char *flags, *filename; | 
 |   ProcMapsIterator::Buffer linebuf; | 
 |   while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) { | 
 |     int written = it.FormatLine(linebuf.buf_, sizeof(linebuf.buf_), | 
 |                                 start, end, flags, offset, inode, filename, | 
 |                                 0); | 
 |     FDWrite(fd, linebuf.buf_, written); | 
 |   } | 
 | } | 
 |  | 
 | void ProfileData::Stop() { | 
 |   if (!enabled()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // Move data from hash table to eviction buffer | 
 |   for (int b = 0; b < kBuckets; b++) { | 
 |     Bucket* bucket = &hash_[b]; | 
 |     for (int a = 0; a < kAssociativity; a++) { | 
 |       if (bucket->entry[a].count > 0) { | 
 |         Evict(bucket->entry[a]); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   if (num_evicted_ + 3 > kBufferLength) { | 
 |     // Ensure there is enough room for end of data marker | 
 |     FlushEvicted(); | 
 |   } | 
 |  | 
 |   // Write end of data marker | 
 |   evict_[num_evicted_++] = 0;         // count | 
 |   evict_[num_evicted_++] = 1;         // depth | 
 |   evict_[num_evicted_++] = 0;         // end of data marker | 
 |   FlushEvicted(); | 
 |  | 
 |   // Dump "/proc/self/maps" so we get list of mapped shared libraries | 
 |   DumpProcSelfMaps(out_); | 
 |  | 
 |   Reset(); | 
 |   fprintf(stderr, "PROFILE: interrupts/evictions/bytes = %d/%d/%" PRIuS "\n", | 
 |           count_, evictions_, total_bytes_); | 
 | } | 
 |  | 
 | void ProfileData::Reset() { | 
 |   if (!enabled()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // Don't reset count_, evictions_, or total_bytes_ here.  They're used | 
 |   // by Stop to print information about the profile after reset, and are | 
 |   // cleared by Start when starting a new profile. | 
 |   close(out_); | 
 |   delete[] hash_; | 
 |   hash_ = 0; | 
 |   delete[] evict_; | 
 |   evict_ = 0; | 
 |   num_evicted_ = 0; | 
 |   free(fname_); | 
 |   fname_ = 0; | 
 |   start_time_ = 0; | 
 |  | 
 |   out_ = -1; | 
 | } | 
 |  | 
 | // This function is safe to call from asynchronous signals (but is not | 
 | // re-entrant).  However, that's not part of its public interface. | 
 | void ProfileData::GetCurrentState(State* state) const { | 
 |   if (enabled()) { | 
 |     state->enabled = true; | 
 |     state->start_time = start_time_; | 
 |     state->samples_gathered = count_; | 
 |     int buf_size = sizeof(state->profile_name); | 
 |     strncpy(state->profile_name, fname_, buf_size); | 
 |     state->profile_name[buf_size-1] = '\0'; | 
 |   } else { | 
 |     state->enabled = false; | 
 |     state->start_time = 0; | 
 |     state->samples_gathered = 0; | 
 |     state->profile_name[0] = '\0'; | 
 |   } | 
 | } | 
 |  | 
 | // This function is safe to call from asynchronous signals (but is not | 
 | // re-entrant).  However, that's not part of its public interface. | 
 | void ProfileData::FlushTable() { | 
 |   if (!enabled()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // Move data from hash table to eviction buffer | 
 |   for (int b = 0; b < kBuckets; b++) { | 
 |     Bucket* bucket = &hash_[b]; | 
 |     for (int a = 0; a < kAssociativity; a++) { | 
 |       if (bucket->entry[a].count > 0) { | 
 |         Evict(bucket->entry[a]); | 
 |         bucket->entry[a].depth = 0; | 
 |         bucket->entry[a].count = 0; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Write out all pending data | 
 |   FlushEvicted(); | 
 | } | 
 |  | 
 | void ProfileData::Add(int depth, const void* const* stack) { | 
 |   if (!enabled()) { | 
 |     return; | 
 |   } | 
 |  | 
 |   if (depth > kMaxStackDepth) depth = kMaxStackDepth; | 
 |   RAW_CHECK(depth > 0, "ProfileData::Add depth <= 0"); | 
 |  | 
 |   // Make hash-value | 
 |   Slot h = 0; | 
 |   for (int i = 0; i < depth; i++) { | 
 |     Slot slot = reinterpret_cast<Slot>(stack[i]); | 
 |     h = (h << 8) | (h >> (8*(sizeof(h)-1))); | 
 |     h += (slot * 31) + (slot * 7) + (slot * 3); | 
 |   } | 
 |  | 
 |   count_++; | 
 |  | 
 |   // See if table already has an entry for this trace | 
 |   bool done = false; | 
 |   Bucket* bucket = &hash_[h % kBuckets]; | 
 |   for (int a = 0; a < kAssociativity; a++) { | 
 |     Entry* e = &bucket->entry[a]; | 
 |     if (e->depth == depth) { | 
 |       bool match = true; | 
 |       for (int i = 0; i < depth; i++) { | 
 |         if (e->stack[i] != reinterpret_cast<Slot>(stack[i])) { | 
 |           match = false; | 
 |           break; | 
 |         } | 
 |       } | 
 |       if (match) { | 
 |         e->count++; | 
 |         done = true; | 
 |         break; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   if (!done) { | 
 |     // Evict entry with smallest count | 
 |     Entry* e = &bucket->entry[0]; | 
 |     for (int a = 1; a < kAssociativity; a++) { | 
 |       if (bucket->entry[a].count < e->count) { | 
 |         e = &bucket->entry[a]; | 
 |       } | 
 |     } | 
 |     if (e->count > 0) { | 
 |       evictions_++; | 
 |       Evict(*e); | 
 |     } | 
 |  | 
 |     // Use the newly evicted entry | 
 |     e->depth = depth; | 
 |     e->count = 1; | 
 |     for (int i = 0; i < depth; i++) { | 
 |       e->stack[i] = reinterpret_cast<Slot>(stack[i]); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | // This function is safe to call from asynchronous signals (but is not | 
 | // re-entrant).  However, that's not part of its public interface. | 
 | void ProfileData::FlushEvicted() { | 
 |   if (num_evicted_ > 0) { | 
 |     const char* buf = reinterpret_cast<char*>(evict_); | 
 |     size_t bytes = sizeof(evict_[0]) * num_evicted_; | 
 |     total_bytes_ += bytes; | 
 |     FDWrite(out_, buf, bytes); | 
 |   } | 
 |   num_evicted_ = 0; | 
 | } |