Squashed 'third_party/gperftools/' content from commit 54505f1

Change-Id: Id02e833828732b0efe7dac722b8485279e67c5fa
git-subtree-dir: third_party/gperftools
git-subtree-split: 54505f1d50c2d1f4676f5e87090b64a117fd980e
diff --git a/src/profiledata.cc b/src/profiledata.cc
new file mode 100644
index 0000000..8b05d3a
--- /dev/null
+++ b/src/profiledata.cc
@@ -0,0 +1,332 @@
+// -*- 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: 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;
+}