Home | History | Annotate | Download | only in src
      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: Sanjay Ghemawat
     32 //         Chris Demetriou (refactoring)
     33 //
     34 // Collect profiling data.
     35 
     36 #include <config.h>
     37 #include <assert.h>
     38 #include <stdio.h>
     39 #include <stdlib.h>
     40 #include <errno.h>
     41 #ifdef HAVE_UNISTD_H
     42 #include <unistd.h>
     43 #endif
     44 #include <sys/time.h>
     45 #include <string.h>
     46 #include <fcntl.h>
     47 
     48 #include "profiledata.h"
     49 
     50 #include "base/logging.h"
     51 #include "base/sysinfo.h"
     52 
     53 // All of these are initialized in profiledata.h.
     54 const int ProfileData::kMaxStackDepth;
     55 const int ProfileData::kAssociativity;
     56 const int ProfileData::kBuckets;
     57 const int ProfileData::kBufferLength;
     58 
     59 ProfileData::Options::Options()
     60     : frequency_(1) {
     61 }
     62 
     63 // This function is safe to call from asynchronous signals (but is not
     64 // re-entrant).  However, that's not part of its public interface.
     65 void ProfileData::Evict(const Entry& entry) {
     66   const int d = entry.depth;
     67   const int nslots = d + 2;     // Number of slots needed in eviction buffer
     68   if (num_evicted_ + nslots > kBufferLength) {
     69     FlushEvicted();
     70     assert(num_evicted_ == 0);
     71     assert(nslots <= kBufferLength);
     72   }
     73   evict_[num_evicted_++] = entry.count;
     74   evict_[num_evicted_++] = d;
     75   memcpy(&evict_[num_evicted_], entry.stack, d * sizeof(Slot));
     76   num_evicted_ += d;
     77 }
     78 
     79 ProfileData::ProfileData()
     80     : hash_(0),
     81       evict_(0),
     82       num_evicted_(0),
     83       out_(-1),
     84       count_(0),
     85       evictions_(0),
     86       total_bytes_(0),
     87       fname_(0),
     88       start_time_(0) {
     89 }
     90 
     91 bool ProfileData::Start(const char* fname,
     92                         const ProfileData::Options& options) {
     93   if (enabled()) {
     94     return false;
     95   }
     96 
     97   // Open output file and initialize various data structures
     98   int fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0666);
     99   if (fd < 0) {
    100     // Can't open outfile for write
    101     return false;
    102   }
    103 
    104   start_time_ = time(NULL);
    105   fname_ = strdup(fname);
    106 
    107   // Reset counters
    108   num_evicted_ = 0;
    109   count_       = 0;
    110   evictions_   = 0;
    111   total_bytes_ = 0;
    112 
    113   hash_ = new Bucket[kBuckets];
    114   evict_ = new Slot[kBufferLength];
    115   memset(hash_, 0, sizeof(hash_[0]) * kBuckets);
    116 
    117   // Record special entries
    118   evict_[num_evicted_++] = 0;                     // count for header
    119   evict_[num_evicted_++] = 3;                     // depth for header
    120   evict_[num_evicted_++] = 0;                     // Version number
    121   CHECK_NE(0, options.frequency());
    122   int period = 1000000 / options.frequency();
    123   evict_[num_evicted_++] = period;                // Period (microseconds)
    124   evict_[num_evicted_++] = 0;                     // Padding
    125 
    126   out_ = fd;
    127 
    128   return true;
    129 }
    130 
    131 ProfileData::~ProfileData() {
    132   Stop();
    133 }
    134 
    135 // Dump /proc/maps data to fd.  Copied from heap-profile-table.cc.
    136 #define NO_INTR(fn)  do {} while ((fn) < 0 && errno == EINTR)
    137 
    138 static void FDWrite(int fd, const char* buf, size_t len) {
    139   while (len > 0) {
    140     ssize_t r;
    141     NO_INTR(r = write(fd, buf, len));
    142     RAW_CHECK(r >= 0, "write failed");
    143     buf += r;
    144     len -= r;
    145   }
    146 }
    147 
    148 static void DumpProcSelfMaps(int fd) {
    149   ProcMapsIterator::Buffer iterbuf;
    150   ProcMapsIterator it(0, &iterbuf);   // 0 means "current pid"
    151 
    152   uint64 start, end, offset;
    153   int64 inode;
    154   char *flags, *filename;
    155   ProcMapsIterator::Buffer linebuf;
    156   while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
    157     int written = it.FormatLine(linebuf.buf_, sizeof(linebuf.buf_),
    158                                 start, end, flags, offset, inode, filename,
    159                                 0);
    160     FDWrite(fd, linebuf.buf_, written);
    161   }
    162 }
    163 
    164 void ProfileData::Stop() {
    165   if (!enabled()) {
    166     return;
    167   }
    168 
    169   // Move data from hash table to eviction buffer
    170   for (int b = 0; b < kBuckets; b++) {
    171     Bucket* bucket = &hash_[b];
    172     for (int a = 0; a < kAssociativity; a++) {
    173       if (bucket->entry[a].count > 0) {
    174         Evict(bucket->entry[a]);
    175       }
    176     }
    177   }
    178 
    179   if (num_evicted_ + 3 > kBufferLength) {
    180     // Ensure there is enough room for end of data marker
    181     FlushEvicted();
    182   }
    183 
    184   // Write end of data marker
    185   evict_[num_evicted_++] = 0;         // count
    186   evict_[num_evicted_++] = 1;         // depth
    187   evict_[num_evicted_++] = 0;         // end of data marker
    188   FlushEvicted();
    189 
    190   // Dump "/proc/self/maps" so we get list of mapped shared libraries
    191   DumpProcSelfMaps(out_);
    192 
    193   Reset();
    194   fprintf(stderr, "PROFILE: interrupts/evictions/bytes = %d/%d/%" PRIuS "\n",
    195           count_, evictions_, total_bytes_);
    196 }
    197 
    198 void ProfileData::Reset() {
    199   if (!enabled()) {
    200     return;
    201   }
    202 
    203   // Don't reset count_, evictions_, or total_bytes_ here.  They're used
    204   // by Stop to print information about the profile after reset, and are
    205   // cleared by Start when starting a new profile.
    206   close(out_);
    207   delete[] hash_;
    208   hash_ = 0;
    209   delete[] evict_;
    210   evict_ = 0;
    211   num_evicted_ = 0;
    212   free(fname_);
    213   fname_ = 0;
    214   start_time_ = 0;
    215 
    216   out_ = -1;
    217 }
    218 
    219 // This function is safe to call from asynchronous signals (but is not
    220 // re-entrant).  However, that's not part of its public interface.
    221 void ProfileData::GetCurrentState(State* state) const {
    222   if (enabled()) {
    223     state->enabled = true;
    224     state->start_time = start_time_;
    225     state->samples_gathered = count_;
    226     int buf_size = sizeof(state->profile_name);
    227     strncpy(state->profile_name, fname_, buf_size);
    228     state->profile_name[buf_size-1] = '\0';
    229   } else {
    230     state->enabled = false;
    231     state->start_time = 0;
    232     state->samples_gathered = 0;
    233     state->profile_name[0] = '\0';
    234   }
    235 }
    236 
    237 // This function is safe to call from asynchronous signals (but is not
    238 // re-entrant).  However, that's not part of its public interface.
    239 void ProfileData::FlushTable() {
    240   if (!enabled()) {
    241     return;
    242   }
    243 
    244   // Move data from hash table to eviction buffer
    245   for (int b = 0; b < kBuckets; b++) {
    246     Bucket* bucket = &hash_[b];
    247     for (int a = 0; a < kAssociativity; a++) {
    248       if (bucket->entry[a].count > 0) {
    249         Evict(bucket->entry[a]);
    250         bucket->entry[a].depth = 0;
    251         bucket->entry[a].count = 0;
    252       }
    253     }
    254   }
    255 
    256   // Write out all pending data
    257   FlushEvicted();
    258 }
    259 
    260 void ProfileData::Add(int depth, const void* const* stack) {
    261   if (!enabled()) {
    262     return;
    263   }
    264 
    265   if (depth > kMaxStackDepth) depth = kMaxStackDepth;
    266   RAW_CHECK(depth > 0, "ProfileData::Add depth <= 0");
    267 
    268   // Make hash-value
    269   Slot h = 0;
    270   for (int i = 0; i < depth; i++) {
    271     Slot slot = reinterpret_cast<Slot>(stack[i]);
    272     h = (h << 8) | (h >> (8*(sizeof(h)-1)));
    273     h += (slot * 31) + (slot * 7) + (slot * 3);
    274   }
    275 
    276   count_++;
    277 
    278   // See if table already has an entry for this trace
    279   bool done = false;
    280   Bucket* bucket = &hash_[h % kBuckets];
    281   for (int a = 0; a < kAssociativity; a++) {
    282     Entry* e = &bucket->entry[a];
    283     if (e->depth == depth) {
    284       bool match = true;
    285       for (int i = 0; i < depth; i++) {
    286         if (e->stack[i] != reinterpret_cast<Slot>(stack[i])) {
    287           match = false;
    288           break;
    289         }
    290       }
    291       if (match) {
    292         e->count++;
    293         done = true;
    294         break;
    295       }
    296     }
    297   }
    298 
    299   if (!done) {
    300     // Evict entry with smallest count
    301     Entry* e = &bucket->entry[0];
    302     for (int a = 1; a < kAssociativity; a++) {
    303       if (bucket->entry[a].count < e->count) {
    304         e = &bucket->entry[a];
    305       }
    306     }
    307     if (e->count > 0) {
    308       evictions_++;
    309       Evict(*e);
    310     }
    311 
    312     // Use the newly evicted entry
    313     e->depth = depth;
    314     e->count = 1;
    315     for (int i = 0; i < depth; i++) {
    316       e->stack[i] = reinterpret_cast<Slot>(stack[i]);
    317     }
    318   }
    319 }
    320 
    321 // This function is safe to call from asynchronous signals (but is not
    322 // re-entrant).  However, that's not part of its public interface.
    323 void ProfileData::FlushEvicted() {
    324   if (num_evicted_ > 0) {
    325     const char* buf = reinterpret_cast<char*>(evict_);
    326     size_t bytes = sizeof(evict_[0]) * num_evicted_;
    327     total_bytes_ += bytes;
    328     FDWrite(out_, buf, bytes);
    329   }
    330   num_evicted_ = 0;
    331 }
    332