mirror of
https://github.com/beefytech/Beef.git
synced 2025-07-04 15:26:00 +02:00
Moving BeefMem
This commit is contained in:
parent
b3487d733b
commit
65bf1915af
337 changed files with 0 additions and 4 deletions
331
BeefTools/BeefMem/gperftools/src/profiledata.cc
Normal file
331
BeefTools/BeefMem/gperftools/src/profiledata.cc
Normal file
|
@ -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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue