This source file includes following definitions.
- Evict
- start_time_
- Start
- FDWrite
- DumpProcSelfMaps
- Stop
- Reset
- GetCurrentState
- FlushTable
- Add
- FlushEvicted
#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"
const int ProfileData::kMaxStackDepth;
const int ProfileData::kAssociativity;
const int ProfileData::kBuckets;
const int ProfileData::kBufferLength;
ProfileData::Options::Options()
: frequency_(1) {
}
void ProfileData::Evict(const Entry& entry) {
const int d = entry.depth;
const int nslots = d + 2;
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;
}
int fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0666);
if (fd < 0) {
return false;
}
start_time_ = time(NULL);
fname_ = strdup(fname);
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);
evict_[num_evicted_++] = 0;
evict_[num_evicted_++] = 3;
evict_[num_evicted_++] = 0;
CHECK_NE(0, options.frequency());
int period = 1000000 / options.frequency();
evict_[num_evicted_++] = period;
evict_[num_evicted_++] = 0;
out_ = fd;
return true;
}
ProfileData::~ProfileData() {
Stop();
}
#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);
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;
}
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) {
FlushEvicted();
}
evict_[num_evicted_++] = 0;
evict_[num_evicted_++] = 1;
evict_[num_evicted_++] = 0;
FlushEvicted();
DumpProcSelfMaps(out_);
Reset();
fprintf(stderr, "PROFILE: interrupts/evictions/bytes = %d/%d/%" PRIuS "\n",
count_, evictions_, total_bytes_);
}
void ProfileData::Reset() {
if (!enabled()) {
return;
}
close(out_);
delete[] hash_;
hash_ = 0;
delete[] evict_;
evict_ = 0;
num_evicted_ = 0;
free(fname_);
fname_ = 0;
start_time_ = 0;
out_ = -1;
}
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';
}
}
void ProfileData::FlushTable() {
if (!enabled()) {
return;
}
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;
}
}
}
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");
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_++;
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) {
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);
}
e->depth = depth;
e->count = 1;
for (int i = 0; i < depth; i++) {
e->stack[i] = reinterpret_cast<Slot>(stack[i]);
}
}
}
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;
}