summaryrefslogtreecommitdiff
path: root/trunk/src/profiledata.cc
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/profiledata.cc')
-rw-r--r--trunk/src/profiledata.cc331
1 files changed, 331 insertions, 0 deletions
diff --git a/trunk/src/profiledata.cc b/trunk/src/profiledata.cc
new file mode 100644
index 0000000..5f2531b
--- /dev/null
+++ b/trunk/src/profiledata.cc
@@ -0,0 +1,331 @@
+// 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;
+}