mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-28 08:16:09 +03:00
332 lines
9.1 KiB
C++
332 lines
9.1 KiB
C++
// 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;
|
|
}
|